initial
This commit is contained in:
commit
5e1adfab60
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.direnv
|
||||
*.bak
|
||||
result
|
||||
tags
|
||||
starpub
|
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Aaron Bieber <aaron@bolddaemon.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
32
data/db.go
Normal file
32
data/db.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.23.0
|
||||
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...interface{}) pgx.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
47
data/models.go
Normal file
47
data/models.go
Normal file
@ -0,0 +1,47 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.23.0
|
||||
|
||||
package data
|
||||
|
||||
import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type Category struct {
|
||||
ID int32 `json:"id"`
|
||||
UserID int32 `json:"user_id"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
Name string `json:"name"`
|
||||
Descr string `json:"descr"`
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
ID int32 `json:"id"`
|
||||
UserID int32 `json:"user_id"`
|
||||
Pubic pgtype.Bool `json:"pubic"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
Title string `json:"title"`
|
||||
Descr string `json:"descr"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type EntryCategory struct {
|
||||
UserID int32 `json:"user_id"`
|
||||
EntryID int32 `json:"entry_id"`
|
||||
CategoryID int32 `json:"category_id"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int32 `json:"id"`
|
||||
Uid pgtype.UUID `json:"uid"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
RealName string `json:"real_name"`
|
||||
Username string `json:"username"`
|
||||
Hash string `json:"hash"`
|
||||
Email string `json:"email"`
|
||||
Pubkey []byte `json:"pubkey"`
|
||||
}
|
308
data/queries.sql.go
Normal file
308
data/queries.sql.go
Normal file
@ -0,0 +1,308 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.23.0
|
||||
// source: queries.sql
|
||||
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createEntry = `-- name: CreateEntry :one
|
||||
INSERT INTO entries (
|
||||
user_id, title, descr
|
||||
) VALUES (
|
||||
$1, $2, $3
|
||||
)
|
||||
RETURNING id, created_at, to_tsvector(descr)
|
||||
`
|
||||
|
||||
type CreateEntryParams struct {
|
||||
UserID int32 `json:"user_id"`
|
||||
Title string `json:"title"`
|
||||
Descr string `json:"descr"`
|
||||
}
|
||||
|
||||
type CreateEntryRow struct {
|
||||
ID int32 `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
ToTsvector interface{} `json:"to_tsvector"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateEntry(ctx context.Context, arg CreateEntryParams) (CreateEntryRow, error) {
|
||||
row := q.db.QueryRow(ctx, createEntry, arg.UserID, arg.Title, arg.Descr)
|
||||
var i CreateEntryRow
|
||||
err := row.Scan(&i.ID, &i.CreatedAt, &i.ToTsvector)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createUser = `-- name: CreateUser :one
|
||||
INSERT INTO users (
|
||||
real_name, username, pubkey
|
||||
) VALUES (
|
||||
$1, $2, $3
|
||||
)
|
||||
RETURNING id, username, pubkey
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
RealName string `json:"real_name"`
|
||||
Username string `json:"username"`
|
||||
Pubkey []byte `json:"pubkey"`
|
||||
}
|
||||
|
||||
type CreateUserRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Pubkey []byte `json:"pubkey"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) {
|
||||
row := q.db.QueryRow(ctx, createUser, arg.RealName, arg.Username, arg.Pubkey)
|
||||
var i CreateUserRow
|
||||
err := row.Scan(&i.ID, &i.Username, &i.Pubkey)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getEntryCategories = `-- name: GetEntryCategories :many
|
||||
SELECT
|
||||
id, user_id, created_at, updated_at, name, descr
|
||||
FROM
|
||||
categories
|
||||
WHERE
|
||||
id IN (
|
||||
SELECT
|
||||
category_id
|
||||
FROM
|
||||
entry_categories a
|
||||
WHERE
|
||||
a.entry_id = $1
|
||||
AND a.user_id = $2
|
||||
)
|
||||
`
|
||||
|
||||
type GetEntryCategoriesParams struct {
|
||||
EntryID int32 `json:"entry_id"`
|
||||
UserID int32 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetEntryCategories(ctx context.Context, arg GetEntryCategoriesParams) ([]Category, error) {
|
||||
rows, err := q.db.Query(ctx, getEntryCategories, arg.EntryID, arg.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []Category{}
|
||||
for rows.Next() {
|
||||
var i Category
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Name,
|
||||
&i.Descr,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getUser = `-- name: GetUser :one
|
||||
SELECT
|
||||
id, uid, created_at, updated_at, real_name, username, hash, email, pubkey
|
||||
FROM
|
||||
users
|
||||
WHERE
|
||||
id = $1
|
||||
LIMIT
|
||||
1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, id int32) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUser, id)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Uid,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.RealName,
|
||||
&i.Username,
|
||||
&i.Hash,
|
||||
&i.Email,
|
||||
&i.Pubkey,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByName = `-- name: GetUserByName :one
|
||||
SELECT
|
||||
id, uid, created_at, updated_at, real_name, username, hash, email, pubkey
|
||||
FROM
|
||||
users
|
||||
WHERE
|
||||
username = $1
|
||||
LIMIT
|
||||
1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByName(ctx context.Context, username string) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByName, username)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Uid,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.RealName,
|
||||
&i.Username,
|
||||
&i.Hash,
|
||||
&i.Email,
|
||||
&i.Pubkey,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserCategories = `-- name: GetUserCategories :many
|
||||
SELECT
|
||||
id, user_id, created_at, updated_at, name, descr
|
||||
FROM
|
||||
categories
|
||||
WHERE
|
||||
user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserCategories(ctx context.Context, userID int32) ([]Category, error) {
|
||||
rows, err := q.db.Query(ctx, getUserCategories, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []Category{}
|
||||
for rows.Next() {
|
||||
var i Category
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Name,
|
||||
&i.Descr,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getUserEntries = `-- name: GetUserEntries :many
|
||||
SELECT
|
||||
id, user_id, pubic, created_at, updated_at, title, descr, signature
|
||||
FROM
|
||||
entries
|
||||
WHERE
|
||||
user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserEntries(ctx context.Context, userID int32) ([]Entry, error) {
|
||||
rows, err := q.db.Query(ctx, getUserEntries, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []Entry{}
|
||||
for rows.Next() {
|
||||
var i Entry
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Pubic,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Title,
|
||||
&i.Descr,
|
||||
&i.Signature,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const similarEntries = `-- name: SimilarEntries :many
|
||||
SELECT
|
||||
id,
|
||||
similarity(descr, $2) AS similarity,
|
||||
ts_headline(
|
||||
'english',
|
||||
descr,
|
||||
q,
|
||||
'StartSel = <b>, StopSel = </b>'
|
||||
) :: text AS headline,
|
||||
title
|
||||
FROM
|
||||
entries,
|
||||
to_tsquery($2) q
|
||||
WHERE
|
||||
user_id = $1
|
||||
AND similarity(descr, $2) > 0.0
|
||||
AND similarity(descr, $2) < 1.0
|
||||
ORDER BY
|
||||
similarity DESC
|
||||
LIMIT
|
||||
10
|
||||
`
|
||||
|
||||
type SimilarEntriesParams struct {
|
||||
UserID int32 `json:"user_id"`
|
||||
Similarity string `json:"similarity"`
|
||||
}
|
||||
|
||||
type SimilarEntriesRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Similarity float32 `json:"similarity"`
|
||||
Headline string `json:"headline"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
func (q *Queries) SimilarEntries(ctx context.Context, arg SimilarEntriesParams) ([]SimilarEntriesRow, error) {
|
||||
rows, err := q.db.Query(ctx, similarEntries, arg.UserID, arg.Similarity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []SimilarEntriesRow{}
|
||||
for rows.Next() {
|
||||
var i SimilarEntriesRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Similarity,
|
||||
&i.Headline,
|
||||
&i.Title,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
27
extension/background.js
Normal file
27
extension/background.js
Normal file
@ -0,0 +1,27 @@
|
||||
import * as Starpub from "./starpub.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
let starKey = new Starpub();
|
||||
|
||||
browser.browserAction.onClicked.addListener(function () {
|
||||
let maybeKey = starKey.getKey();
|
||||
maybeKey.then((_) => {
|
||||
let tab = browser.tabs.query({ active: true, currentWindow: true });
|
||||
tab.then(function (a) {
|
||||
const t = a[0];
|
||||
const st = starKey.sign({
|
||||
url: t.url,
|
||||
title: t.title,
|
||||
});
|
||||
st.then((p) => {
|
||||
const vfy = Coze.Verify(p, starKey.pubKey);
|
||||
vfy.then((v) => {
|
||||
console.log("signature verified: ", v);
|
||||
console.log(t.url, t.title);
|
||||
console.log(starKey.pubKey);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
2
extension/coze.min.js
vendored
Normal file
2
extension/coze.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
extension/icons/star-48.png
Normal file
BIN
extension/icons/star-48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
30
extension/manifest.json
Normal file
30
extension/manifest.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "StarPub",
|
||||
"version": "1.0",
|
||||
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "starpub@starpuber"
|
||||
}
|
||||
},
|
||||
|
||||
"permissions": ["tabs", "storage"],
|
||||
|
||||
"description": "Star anything",
|
||||
|
||||
"icons": {
|
||||
"48": "icons/star-48.png"
|
||||
},
|
||||
|
||||
"browser_action": {
|
||||
"default_icon": "icons/star-48.png",
|
||||
"default_title": "Star it!",
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
|
||||
"background": {
|
||||
"scripts": ["coze.min.js", "background.js"],
|
||||
"type": "module"
|
||||
}
|
||||
}
|
20
extension/popup.html
Normal file
20
extension/popup.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<input id="title" type="text"/>
|
||||
<br />
|
||||
<textarea id="description"></textarea>
|
||||
<button>Add Star</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module" src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
28
extension/popup.js
Normal file
28
extension/popup.js
Normal file
@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
import * as Starpub from "./starpub.min.js";
|
||||
|
||||
let starKey = new Starpub();
|
||||
browser.browserAction.onClicked.addListener(function () {
|
||||
let maybeKey = starKey.getKey();
|
||||
maybeKey.then((_) => {
|
||||
let tab = browser.tabs.query({ active: true, currentWindow: true });
|
||||
tab.then(function (a) {
|
||||
const t = a[0];
|
||||
const st = starKey.sign({
|
||||
url: t.url,
|
||||
title: t.title,
|
||||
});
|
||||
st.then((p) => {
|
||||
const vfy = Coze.Verify(p, starKey.pubKey);
|
||||
vfy.then((v) => {
|
||||
let titleInp = document.getElementById("title");
|
||||
let descInp = document.getElementById("description");
|
||||
|
||||
console.log("signature verified: ", v, t);
|
||||
titleInp.value = t.title;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
20
extension/settings.html
Normal file
20
extension/settings.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<div>
|
||||
<input id="title" type="text"/>
|
||||
<br />
|
||||
<textarea id="description"></textarea>
|
||||
<button>Add Star</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module" src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
46
extension/starpub.js
Normal file
46
extension/starpub.js
Normal file
@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
|
||||
import * as Coze from "./coze.min.js";
|
||||
|
||||
export { Starpub };
|
||||
|
||||
class Starpub {
|
||||
async getKey() {
|
||||
let key = await browser.storage.local.get("starKey");
|
||||
if (Object.keys(key).length < 1) {
|
||||
this.key = await Coze.NewKey("ES512");
|
||||
this.key.kid = "My StarPub Coze Key";
|
||||
await browser.storage.local.set({ starKey: this.key });
|
||||
} else {
|
||||
this.key = key.starKey;
|
||||
}
|
||||
this.thumb = await Coze.Thumbprint(this.key);
|
||||
const { d, ...pkRest } = this.key;
|
||||
this.pubKey = pkRest;
|
||||
}
|
||||
|
||||
async sign(starObj) {
|
||||
return await Coze.SignCozeRaw(
|
||||
{
|
||||
pay: {
|
||||
url: starObj.url,
|
||||
title: starObj.title,
|
||||
typ: "starpub/msg/create",
|
||||
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
alg: this.key.alg,
|
||||
tmb: this.key.tmb,
|
||||
},
|
||||
},
|
||||
this.key,
|
||||
);
|
||||
}
|
||||
|
||||
async verify() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async publish() {}
|
||||
}
|
||||
|
||||
|
BIN
extension/starpub.png
Normal file
BIN
extension/starpub.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
26
flake.lock
Normal file
26
flake.lock
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1711668574,
|
||||
"narHash": "sha256-u1dfs0ASQIEr1icTVrsKwg2xToIpn7ZXxW3RHfHxshg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "219951b495fc2eac67b1456824cc1ec1fd2ee659",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-23.11",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
57
flake.nix
Normal file
57
flake.nix
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
description = "starpub: star anything";
|
||||
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-23.11";
|
||||
|
||||
outputs =
|
||||
{ self
|
||||
, nixpkgs
|
||||
,
|
||||
}:
|
||||
let
|
||||
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||
in
|
||||
{
|
||||
overlay = _: prev: { inherit (self.packages.${prev.system}) starpub; };
|
||||
|
||||
packages = forAllSystems (system:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system};
|
||||
in
|
||||
{
|
||||
starpub = pkgs.buildGoModule {
|
||||
pname = "starpub";
|
||||
version = "v0.0.0";
|
||||
src = ./.;
|
||||
|
||||
vendorHash = pkgs.lib.fakeSha256;
|
||||
};
|
||||
});
|
||||
|
||||
defaultPackage = forAllSystems (system: self.packages.${system}.starpub);
|
||||
devShells = forAllSystems (system:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system};
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
shellHook = ''
|
||||
PS1='\u@\h:\@; '
|
||||
echo "Go `${pkgs.go}/bin/go version`"
|
||||
'';
|
||||
nativeBuildInputs = with pkgs; [
|
||||
git
|
||||
go
|
||||
gopls
|
||||
go-tools
|
||||
sqlc
|
||||
|
||||
(pkgs.callPackage (import ./ogen.nix) { })
|
||||
(postgresql.withPackages (p: [ p.postgis ]))
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
4
generate.go
Normal file
4
generate.go
Normal file
@ -0,0 +1,4 @@
|
||||
package main
|
||||
|
||||
//go:generate ogen --target petstore --clean openapi.yaml
|
||||
//go:generate sqlc generate
|
37
go.mod
Normal file
37
go.mod
Normal file
@ -0,0 +1,37 @@
|
||||
module suah.dev/starpub
|
||||
|
||||
go 1.22.1
|
||||
|
||||
require (
|
||||
github.com/go-faster/errors v0.7.1
|
||||
github.com/go-faster/jx v1.1.0
|
||||
github.com/jackc/pgx/v5 v5.5.5
|
||||
github.com/ogen-go/ogen v1.0.0
|
||||
go.opentelemetry.io/otel v1.25.0
|
||||
go.opentelemetry.io/otel/metric v1.25.0
|
||||
go.opentelemetry.io/otel/trace v1.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-faster/yaml v0.4.6 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
88
go.sum
Normal file
88
go.sum
Normal file
@ -0,0 +1,88 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
|
||||
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
|
||||
github.com/go-faster/jx v1.1.0 h1:ZsW3wD+snOdmTDy9eIVgQdjUpXRRV4rqW8NS3t+20bg=
|
||||
github.com/go-faster/jx v1.1.0/go.mod h1:vKDNikrKoyUmpzaJ0OkIkRQClNHFX/nF3dnTJZb3skg=
|
||||
github.com/go-faster/yaml v0.4.6 h1:lOK/EhI04gCpPgPhgt0bChS6bvw7G3WwI8xxVe0sw9I=
|
||||
github.com/go-faster/yaml v0.4.6/go.mod h1:390dRIvV4zbnO7qC9FGo6YYutc+wyyUSHBgbXL52eXk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ogen-go/ogen v1.0.0 h1:n1hkgOnLtA1Xn369KAzJhqzphQzNo/wAI82NIaFQNXA=
|
||||
github.com/ogen-go/ogen v1.0.0/go.mod h1:NFn616zR+/DPsq8rPoezaHlhKcNQzlYfo5gUieW8utI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
|
||||
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
|
||||
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
|
||||
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
|
||||
go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
|
||||
go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
57
main.go
Normal file
57
main.go
Normal file
@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"suah.dev/starpub/data"
|
||||
)
|
||||
|
||||
var ()
|
||||
|
||||
func logger(f http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%s\n", r.URL.Path)
|
||||
f(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func makeHandler(q *data.Queries, ctx context.Context) func(w http.ResponseWriter, r *http.Request) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
pd, err := pgx.Connect(
|
||||
context.Background(),
|
||||
"host=localhost dbname=postgres sslmode=disable password=''",
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
base := data.New(pd)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/user/auth", logger(makeHandler(base, ctx)))
|
||||
|
||||
mux.HandleFunc("POST /stars", logger(nil))
|
||||
mux.HandleFunc("DELETE /stars", logger(nil))
|
||||
|
||||
mux.HandleFunc("GET /{user}", logger(nil))
|
||||
mux.HandleFunc("GET /{user}.rss", logger(nil))
|
||||
|
||||
s := http.Server{
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
lis, err := net.Listen("tcp", ":8080")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
s.Serve(lis)
|
||||
}
|
26
ogen.nix
Normal file
26
ogen.nix
Normal file
@ -0,0 +1,26 @@
|
||||
{ lib
|
||||
, buildGoModule
|
||||
, fetchFromGitHub
|
||||
, ...
|
||||
}:
|
||||
with lib;
|
||||
buildGoModule rec {
|
||||
pname = "ogen";
|
||||
version = "1.0.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "ogen-go";
|
||||
repo = pname;
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-khUY8PQ12p0slleidysZiGWyZNX3XCxwD55lfRDCwew=";
|
||||
};
|
||||
|
||||
vendorHash = "sha256-kHfp77jKPBt+EKAc52nXdqR4TJ3OVj7mqo4vfv2XelQ=";
|
||||
|
||||
meta = {
|
||||
description = "OpenAPI v3 code generator for go";
|
||||
homepage = "https://github.com/ogen-go/ogen";
|
||||
license = licenses.asl20;
|
||||
maintainers = with maintainers; [ qbit ];
|
||||
};
|
||||
}
|
33
openapi.yaml
Normal file
33
openapi.yaml
Normal file
@ -0,0 +1,33 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Starpub API
|
||||
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
|
||||
version: 1.0.0
|
||||
|
||||
servers:
|
||||
- url: http://api.example.com/v1
|
||||
description: Optional server description, e.g. Main (production) server
|
||||
- url: http://staging-api.example.com
|
||||
description: Optional server description, e.g. Internal staging server for testing
|
||||
|
||||
paths:
|
||||
'/{user}':
|
||||
get:
|
||||
summary: Returns a list of users.
|
||||
description: Optional extended description in CommonMark or HTML.
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
required: true
|
||||
description: username
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200": # status code
|
||||
description: A JSON array of user names
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
90
queries.sql
Normal file
90
queries.sql
Normal file
@ -0,0 +1,90 @@
|
||||
-- name: GetUserCategories :many
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
categories
|
||||
WHERE
|
||||
user_id = $1;
|
||||
|
||||
-- name: GetUserEntries :many
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
entries
|
||||
WHERE
|
||||
user_id = $1;
|
||||
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (
|
||||
real_name, username, pubkey
|
||||
) VALUES (
|
||||
$1, $2, $3
|
||||
)
|
||||
RETURNING id, username, pubkey;
|
||||
|
||||
-- name: SimilarEntries :many
|
||||
SELECT
|
||||
id,
|
||||
similarity(descr, $2) AS similarity,
|
||||
ts_headline(
|
||||
'english',
|
||||
descr,
|
||||
q,
|
||||
'StartSel = <b>, StopSel = </b>'
|
||||
) :: text AS headline,
|
||||
title
|
||||
FROM
|
||||
entries,
|
||||
to_tsquery($2) q
|
||||
WHERE
|
||||
user_id = $1
|
||||
AND similarity(descr, $2) > 0.0
|
||||
AND similarity(descr, $2) < 1.0
|
||||
ORDER BY
|
||||
similarity DESC
|
||||
LIMIT
|
||||
10;
|
||||
|
||||
-- name: GetEntryCategories :many
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
categories
|
||||
WHERE
|
||||
id IN (
|
||||
SELECT
|
||||
category_id
|
||||
FROM
|
||||
entry_categories a
|
||||
WHERE
|
||||
a.entry_id = $1
|
||||
AND a.user_id = $2
|
||||
);
|
||||
|
||||
-- name: GetUser :one
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
users
|
||||
WHERE
|
||||
id = $1
|
||||
LIMIT
|
||||
1;
|
||||
|
||||
-- name: GetUserByName :one
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
users
|
||||
WHERE
|
||||
username = $1
|
||||
LIMIT
|
||||
1;
|
||||
|
||||
-- name: CreateEntry :one
|
||||
INSERT INTO entries (
|
||||
user_id, title, descr
|
||||
) VALUES (
|
||||
$1, $2, $3
|
||||
)
|
||||
RETURNING id, created_at, to_tsvector(descr);
|
48
schema.sql
Normal file
48
schema.sql
Normal file
@ -0,0 +1,48 @@
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
DROP Table if exists entry_categories;
|
||||
drop table if exists categories;
|
||||
drop table if exists entries;
|
||||
drop table if exists users;
|
||||
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
uid UUID NOT NULL DEFAULT gen_random_uuid() UNIQUE,
|
||||
created_at timestamp NOT NULL DEFAULT NOW(),
|
||||
updated_at timestamp,
|
||||
real_name text NOT NULL,
|
||||
username text NOT NULL UNIQUE,
|
||||
hash text NOT NULL,
|
||||
email text NOT NULL,
|
||||
pubkey jsonb NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE categories (
|
||||
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
created_at timestamp NOT NULL DEFAULT NOW(),
|
||||
updated_at timestamp,
|
||||
name text NOT NULL,
|
||||
descr text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE entries (
|
||||
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
pubic bool DEFAULT TRUE,
|
||||
created_at timestamp NOT NULL DEFAULT NOW(),
|
||||
updated_at timestamp,
|
||||
title text NOT NULL DEFAULT '',
|
||||
descr text NOT NULL DEFAULT '',
|
||||
signature text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE entry_categories (
|
||||
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
entry_id INTEGER NOT NULL REFERENCES entries (id) ON DELETE CASCADE,
|
||||
category_id INTEGER NOT NULL REFERENCES categories (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX entry_trgm_idx ON entries USING gist (title, descr gist_trgm_ops);
|
Loading…
Reference in New Issue
Block a user