From 5e1adfab6063ecfc00aca9073a95ca09c8fe27b0 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Fri, 19 Apr 2024 07:04:46 -0600 Subject: [PATCH] initial --- .envrc | 1 + .gitignore | 5 + LICENSE | 15 ++ data/db.go | 32 ++++ data/models.go | 47 ++++++ data/queries.sql.go | 308 ++++++++++++++++++++++++++++++++++++ extension/background.js | 27 ++++ extension/coze.min.js | 2 + extension/icons/star-48.png | Bin 0 -> 21803 bytes extension/manifest.json | 30 ++++ extension/popup.html | 20 +++ extension/popup.js | 28 ++++ extension/settings.html | 20 +++ extension/starpub.js | 46 ++++++ extension/starpub.png | Bin 0 -> 9776 bytes flake.lock | 26 +++ flake.nix | 57 +++++++ generate.go | 4 + go.mod | 37 +++++ go.sum | 88 +++++++++++ main.go | 57 +++++++ ogen.nix | 26 +++ openapi.yaml | 33 ++++ queries.sql | 90 +++++++++++ schema.sql | 48 ++++++ sqlc.yaml | 12 ++ 26 files changed, 1059 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 data/db.go create mode 100644 data/models.go create mode 100644 data/queries.sql.go create mode 100644 extension/background.js create mode 100644 extension/coze.min.js create mode 100644 extension/icons/star-48.png create mode 100644 extension/manifest.json create mode 100644 extension/popup.html create mode 100644 extension/popup.js create mode 100644 extension/settings.html create mode 100644 extension/starpub.js create mode 100644 extension/starpub.png create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 generate.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 ogen.nix create mode 100644 openapi.yaml create mode 100644 queries.sql create mode 100644 schema.sql create mode 100644 sqlc.yaml diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d187fc0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.direnv +*.bak +result +tags +starpub diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5718c90 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Aaron Bieber + * + * 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. + */ diff --git a/data/db.go b/data/db.go new file mode 100644 index 0000000..7a811cf --- /dev/null +++ b/data/db.go @@ -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, + } +} diff --git a/data/models.go b/data/models.go new file mode 100644 index 0000000..0140080 --- /dev/null +++ b/data/models.go @@ -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"` +} diff --git a/data/queries.sql.go b/data/queries.sql.go new file mode 100644 index 0000000..5a90a1a --- /dev/null +++ b/data/queries.sql.go @@ -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 = , StopSel = ' + ) :: 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 +} diff --git a/extension/background.js b/extension/background.js new file mode 100644 index 0000000..0b791c7 --- /dev/null +++ b/extension/background.js @@ -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); + }); + }); + }); + }); + }); +}); diff --git a/extension/coze.min.js b/extension/coze.min.js new file mode 100644 index 0000000..ea21c61 --- /dev/null +++ b/extension/coze.min.js @@ -0,0 +1,2 @@ +var t={UnknownAlg:"UnknownAlg",ES224:"ES224",ES256:"ES256",ES384:"ES384",ES512:"ES512",Ed25519:"Ed25519",Ed25519ph:"Ed25519ph",Ed448:"Ed448",SHA224:"SHA-224",SHA256:"SHA-256",SHA384:"SHA-384",SHA512:"SHA-512",SHA3224:"SHA3-224",SHA3256:"SHA3-256",SHA3384:"SHA3-384",SHA3512:"SHA3-512",SHAKE128:"SHAKE128",SHAKE256:"SHAKE256"},d={EC:"EC",SHA:"SHA",RSA:"RSA"},o={ECDSA:"ECDSA",EdDSA:"EdDSA",SHA2:"SHA2",SHA3:"SHA3"},u={P224:"P-224",P256:"P-256",P384:"P-384",P521:"P-521",Curve25519:"Curve25519",Curve448:"Curve448"},H={Sig:"sig",Enc:"enc",Hsh:"hsh"};function P(r){let e={};e.Name=r,e.Genus=y(r),e.Family=X(r),e.Use=J(r),e.Hash=l(r),e.HashSize=Z(r),e.HashSizeB64=Math.ceil(4*e.HashSize/3);try{e.XSize=T(r),e.XSizeB64=Math.ceil(4*e.XSize/3),e.DSize=_(r),e.DSizeB64=Math.ceil(4*e.DSize/3),e.Curve=B(r),e.SigSize=E(r),e.SigSizeB64=Math.ceil(4*e.SigSize/3)}catch{}return e}function y(r){switch(r){case t.ES224:case t.ES256:case t.ES384:case t.ES512:return o.ECDSA;case t.Ed25519:case t.Ed25519ph:case t.Ed448:return o.EdDSA;case t.SHA224:case t.SHA256:case t.SHA384:case t.SHA512:return o.SHA2;case t.SHA3224:case t.SHA3256:case t.SHA3384:case t.SHA3512:case t.SHAKE128:case t.SHAKE256:return o.SHA3;default:throw new Error("alg.Genus: unsupported algorithm: "+r)}}function X(r){switch(r){case t.ES224:case t.ES256:case t.ES384:case t.ES512:case t.Ed25519:case t.Ed25519ph:case t.Ed448:return d.EC;case t.SHA224:case t.SHA256:case t.SHA384:case t.SHA512:case t.SHA3224:case t.SHA3256:case t.SHA3384:case t.SHA3512:case t.SHAKE128:case t.SHAKE256:return d.SHA;default:throw new Error("alg.Family: unsupported algorithm: "+r)}}function l(r){switch(r){case t.ES224:case t.SHA224:return t.SHA224;case t.SHA256:case t.ES256:return t.SHA256;case t.SHA384:case t.ES384:return t.SHA384;case t.SHA512:case t.ES512:case t.Ed25519:case t.Ed25519ph:return t.SHA512;case t.SHAKE128:return t.SHAKE128;case t.SHAKE256:case t.Ed448:return t.SHAKE256;case t.SHA3224:return t.SHA3224;case t.SHA3256:return t.SHA3256;case t.SHA3384:return t.SHA3384;case t.SHA3512:return t.SHA3512;default:throw new Error("alg.HashAlg: unsupported algorithm: "+r)}}function Z(r){switch(l(r)){case t.SHA224:case t.SHA3224:return 28;case t.SHA256:case t.SHA3256:case t.SHAKE128:return 32;case t.SHA384:case t.SHA3384:return 48;case t.SHA512:case t.SHA3512:case t.SHAKE256:return 64;default:throw new Error("alg.HashSize: unsupported algorithm: "+r)}}function E(r){switch(r){case t.ES224:return 56;case t.ES256:case t.Ed25519:case t.Ed25519ph:return 64;case t.ES384:return 96;case t.Ed448:return 114;case t.ES512:return 132;default:throw new Error("alg.SigSize: unsupported algorithm: "+r)}}function T(r){switch(r){case t.Ed25519:case t.Ed25519ph:return 32;case t.ES224:return 56;case t.Ed448:return 57;case t.ES256:return 64;case t.ES384:return 96;case t.ES512:return 132;default:throw new Error("alg.XSize: unsupported algorithm: "+r)}}function _(r){switch(r){case t.ES224:return 28;case t.ES256:case t.Ed25519:case t.Ed25519ph:return 32;case t.ES384:return 48;case t.Ed448:return 57;case t.ES512:return 66;default:throw new Error("alg.DSize: unsupported algorithm: "+r)}}function B(r){switch(r){default:throw new Error("alg.Curve: unsupported algorithm: "+r);case t.ES224:return u.P224;case t.ES256:return u.P256;case t.ES384:return u.P384;case t.ES512:return u.P521;case t.Ed25519:case t.Ed25519ph:return u.Curve25519;case t.Ed448:return u.Curve448}}function J(r){switch(y(r)){default:throw new Error("alg.Use: unsupported algorithm: "+r);case o.EdDSA:case o.ECDSA:return H.Sig;case o.SHA2:case o.SHA3:return H.Hsh}}var w={ES224:BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"),ES256:BigInt("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"),ES384:BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973"),ES512:BigInt("0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409")},W={ES224:w.ES224>>BigInt(1),ES256:w.ES256>>BigInt(1),ES384:w.ES384>>BigInt(1),ES512:w.ES512>>BigInt(1)};function I(r){switch(r){default:throw new Error("CurveOrder: unsupported curve: "+r);case"ES224":case"ES256":case"ES384":case"ES512":return w[r]}}function U(r){switch(r){default:throw new Error("CurveHalfOrder: unsupported curve: "+r);case"ES224":case"ES256":case"ES384":case"ES512":return W[r]}}var s={New:async function(r){switch(i(r)&&(r=t.ES256),r){case t.ES256:case t.ES384:case t.ES512:return await window.crypto.subtle.generateKey({name:o.ECDSA,namedCurve:B(r)},!0,["sign","verify"]);default:throw new Error("CryptoKey.New: Unsupported key algorithm:"+r)}},FromCozeKey:async function(r,e){if(y(r.alg)!=o.ECDSA)throw new Error("CryptoKey.FromCozeKey: unsupported CryptoKey algorithm: "+r.alg);var a={};a.use=H.Sig,a.crv=B(r.alg),a.kty=d.EC;let n=T(r.alg)/2,f=await C(r.x);if(a.x=await c(f.slice(0,n)),a.y=await c(f.slice(n)),i(r.d)||e)var S="verify";else S="sign",a.d=r.d;return await crypto.subtle.importKey("jwk",a,{name:o.ECDSA,namedCurve:a.crv},!0,[S])},ToPublic:async function(r){delete r.d,r.key_ops=["verify"]},ToCozeKey:async function(r){let e=await window.crypto.subtle.exportKey("jwk",r);var a={};a.alg=await s.algFromCrv(e.crv);let n=C(e.x),f=C(e.y);var S=new Uint8Array([...n,...f]);return a.x=c(S.buffer),e.hasOwnProperty("d")&&(a.d=e.d),a.tmb=await F(a),a},SignBuffer:async function(r,e){let a=await s.algFromCrv(r.algorithm.namedCurve),n=await window.crypto.subtle.sign({name:o.ECDSA,hash:{name:l(a)}},r,e);return n=N(a,n),n},SignBufferB64:async function(r,e){return await c(await s.SignBuffer(r,e))},SignString:async function(r,e){return await s.SignBufferB64(r,await A(e))},VerifyArrayBuffer:async function(r,e,a,n){return await Y(r,n)?(await s.ToPublic(e),await window.crypto.subtle.verify({name:o.ECDSA,hash:{name:await s.GetSignHashAlgoFromCryptoKey(e)}},e,n,a)):!1},VerifyMsg:async function(r,e,a,n){return s.VerifyArrayBuffer(r,e,await A(a),await D(n))},GetSignHashAlgoFromCryptoKey:async function(r){return l(await s.algFromCrv(r.algorithm.namedCurve))},algFromCrv:async function(r){switch(r){case u.P224:var e=t.ES224;break;case u.P256:e=t.ES256;break;case u.P384:e=t.ES384;break;case u.P521:e=t.ES512;break;default:throw new Error("CryptoKey.ToCozeKey: Unsupported key algorithm.")}return e}};function z(r,e){if(typeof e!="bigint")throw new Error("IsLowS: s is not of type bigint");return U(r)>e}function Q(r,e){if(typeof e!="bigint")throw new Error("toLowS: s is not of type bigint");return z(r,e)?e:I(r)-e}async function sr(r,e){let a=await D(e),n=await N(r,a);return c(n)}async function Y(r,e){let a=await $(r,e);return z(r,a)}function $(r,e){let a=E(r)/2,n=e.slice(a);return O(n)}async function N(r,e){let a=E(r)/2,n=e.slice(0,a),f=e.slice(a),S=O(f),m=Q(r,S),h=q(E(r)/2,m);var g=new Uint8Array(n.byteLength+h.byteLength);return g.set(new Uint8Array(n),0),g.set(new Uint8Array(h),n.byteLength),e=g.buffer,e}function O(r){let e=0n,a=new Uint8Array(r);for(let n=0;n>=8n;while(r>0);return a}var rr=["alg","x"];async function ur(r){if(i(r)&&(r=t.ES256),y(r)==o.ECDSA)var e=await s.New(r);else throw new Error("Coze.NewKey: only ECDSA algs are currently supported.");let a=await s.ToCozeKey(e.privateKey);return a.iat=Math.floor(Date.now()/1e3),a.tmb=await F(a),a.kid="My Cyphr.me Key.",a}async function F(r){if(i(r.alg)||i(r.x))throw new Error("Coze.Thumbprint: alg or x is empty.");return p(r,await l(r.alg),rr)}async function fr(r){if(i(r.d))return console.error("Coze key missing `d`"),!1;try{let e="7AtyaCHO2BAG06z0W1tOQlZFWbhxGgqej4k9-HWP3DE-zshRbrE-69DIfgY704_FDYez7h_rEI1WQVKhv5Hd5Q",a=await v(e,r);return k(e,r,a)}catch{return!1}}async function Sr(r){if(typeof r!="object")return console.error("Correct: CozeKey must be passed in as an object."),!1;if(i(r.alg))return console.error("Correct: Alg must be set"),!1;let e=P(r.alg),a=i(r.tmb),n=i(r.x),f=i(r.d);if(a&&n&&f)return console.error("Correct: At least one of [x, tmb, d] must be set"),!1;if(n&&f)return a||r.tmb.length!==e.HashSizeB64?(console.error("Correct: Incorrect `tmb` size: ",r.tmb.length),!1):!0;if(!n&&r.x.length!==e.XSizeB64)return console.error("Correct: Incorrect x size: ",r.x.length),!1;if(!a&&!n){let S=await F(r);if(r.tmb!==S)return console.error("Correct: Incorrect given `tmb`: ",r.tmb),!1}if(!f&&!n){let S=await s.FromCozeKey(r),m=await A("Test Signing"),h=await s.SignBuffer(S,m),g=await s.FromCozeKey(r,!0);if(!await s.VerifyArrayBuffer(r.alg,g,m,h))return console.error("Correct: private key invalid."),!1}return!0}async function lr(r,e){if(i(r))throw new Error("CozeKey.Revoke: Private key not set. Cannot sign message");var a={};a.pay={},i(e)||(a.pay.msg=e),a.pay.rvk=Math.round(Date.now()/1e3);let n=r.rvk;return delete r.rvk,a=await V(a,r),n!==void 0?r.rvk=n:r.rvk=a.pay.rvk,a}function x(r){return!(i(r.rvk)||!(parseInt(r.rvk)>0))}var cr=["alg","iat","tmb","typ"];async function V(r,e,a){if(console.log(),x(e))throw new Error("SignCoze: Cannot sign with revoked key.");return r.pay.alg=e.alg,r.pay.tmb=await F(e),r.pay.iat=Math.round(Date.now()/1e3),i(a)||(r.pay=await b(r.pay,a)),r.sig=await v(JSON.stringify(r.pay),e),r}async function v(r,e){return s.SignBufferB64(await s.FromCozeKey(e),await A(r))}async function Ar(r,e,a){if(x(e))throw new Error("SignCozeRaw: Cannot sign with revoked key.");if(!i(r.pay.alg)&&r.pay.alg!==e.alg)throw new Error("SignCozeRaw: Coze key alg mismatch with coze.pay.alg.");if(!i(r.pay.tmb)&&r.pay.tmb!==e.tmb)throw new Error("SignCozeRaw: Coze key tmb mismatch with coze.pay.tmb.");return i(a)||(r.pay=await b(r.pay,a)),r.sig=await v(JSON.stringify(r.pay),e),r}async function yr(r,e){if(!i(r.pay.alg)&&r.pay.alg!==e.alg)throw new Error("VerifyCoze: Coze key alg mismatch with coze.pay.alg.");if(!i(r.pay.tmb)&&r.pay.tmb!==e.tmb)throw new Error("VerifyCoze: Coze key tmb mismatch with coze.pay.tmb.");return k(JSON.stringify(r.pay),e,r.sig)}async function k(r,e,a){return s.VerifyMsg(e.alg,await s.FromCozeKey(e,!0),r,a)}async function Fr(r,e){if(i(r.pay))throw new Error("Meta: coze.pay must exist.");let a={};if(i(e)){if(i(r.pay.alg))throw new Error("Meta: either coze.pay.alg or parameter alg must be set.");a.alg=r.pay.alg}else a.alg=e;if(!i(r.pay.alg)&&a.alg!==r.pay.alg)throw new Error(`Meta: coze.pay.alg (${r.pay.alg}) and parameter alg (${e}) do not match. `);return i(r.pay.iat)||(a.iat=r.pay.iat),i(r.pay.tmb)||(a.tmb=r.pay.tmb),i(r.pay.typ)||(a.typ=r.pay.typ),a.can=await L(r.pay),a.cad=await p(r.pay,l(a.alg)),i(r.sig)||(a.sig=r.sig,a.czd=await p({cad:a.cad,sig:a.sig},l(a.alg))),a}async function A(r){return new TextEncoder().encode(r).buffer}function D(r){return C(r).buffer}function C(r){if(r=r.replace(/-/g,"+").replace(/_/g,"/"),btoa(atob(r)).replace(/=/g,"")!==r)throw new Error("Non-canonical base64 string");return Uint8Array.from(atob(r),a=>a.charCodeAt(0))}function c(r){return btoa(String.fromCharCode.apply(null,new Uint8Array(r))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function i(r){return typeof r=="function"?!1:Array.isArray(r)&&r.length==0?!0:r===Object(r)?Object.keys(r).length===0:!er(r)}function er(r){return!(r===!1||r==="false"||r===void 0||r==="undefined"||r===""||r===0||r==="0"||r===null||r==="null"||r==="NaN"||Number.isNaN(r)||r===Object(r))}function L(r){return Object.keys(r)}async function b(r,e){if(i(e))return r;let a={};for(let n of e)a[n]=r[n];return a}async function tr(r,e){return JSON.stringify(await b(r,e))}async function ar(r,e,a){if(i(e))throw new Error("Hash is not given");return await crypto.subtle.digest(e,await A(await tr(r,a)))}async function p(r,e,a){return await c(await ar(r,e,a))}export{t as Algs,c as ArrayBufferTo64ut,C as B64ToUint8Array,D as B64uToArrayBuffer,L as Canon,b as Canonical,ar as CanonicalHash,p as CanonicalHash64,tr as CanonicalS,Sr as Correct,s as CryptoKey,B as Curve,U as CurveHalfOrder,I as CurveOrder,u as Curves,_ as DSize,d as FamAlgs,X as Family,o as GenAlgs,y as Genus,l as HashAlg,Z as HashSize,x as IsRevoked,Y as IsSigLowS,Fr as Meta,ur as NewKey,P as Params,cr as PayCanon,lr as Revoke,A as SToArrayBuffer,E as SigSize,sr as SigToLowS,V as Sign,Ar as SignCozeRaw,v as SignPay,F as Thumbprint,rr as TmbCanon,J as Use,H as Uses,fr as Valid,yr as Verify,k as VerifyPay,T as XSize,i as isEmpty}; +//# sourceMappingURL=coze.min.js.map diff --git a/extension/icons/star-48.png b/extension/icons/star-48.png new file mode 100644 index 0000000000000000000000000000000000000000..a0262ed65bfcae13ae8d15de7fff1dec342fb381 GIT binary patch literal 21803 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 JPDHLkV1hWzUV;Ds literal 0 HcmV?d00001 diff --git a/extension/manifest.json b/extension/manifest.json new file mode 100644 index 0000000..7702de1 --- /dev/null +++ b/extension/manifest.json @@ -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" + } +} diff --git a/extension/popup.html b/extension/popup.html new file mode 100644 index 0000000..77e4143 --- /dev/null +++ b/extension/popup.html @@ -0,0 +1,20 @@ + + + + + + + + +
+
+ +
+ + +
+
+ + + + diff --git a/extension/popup.js b/extension/popup.js new file mode 100644 index 0000000..5c48a0a --- /dev/null +++ b/extension/popup.js @@ -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; + }); + }); + }); + }); +}); diff --git a/extension/settings.html b/extension/settings.html new file mode 100644 index 0000000..77e4143 --- /dev/null +++ b/extension/settings.html @@ -0,0 +1,20 @@ + + + + + + + + +
+
+ +
+ + +
+
+ + + + diff --git a/extension/starpub.js b/extension/starpub.js new file mode 100644 index 0000000..1532781 --- /dev/null +++ b/extension/starpub.js @@ -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() {} +} + + diff --git a/extension/starpub.png b/extension/starpub.png new file mode 100644 index 0000000000000000000000000000000000000000..3f19e021d735752e5a81eb2e4f8b31380d6b1091 GIT binary patch literal 9776 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 literal 0 HcmV?d00001 diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..3748dac --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..60c9c0b --- /dev/null +++ b/flake.nix @@ -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 ])) + ]; + }; + }); + }; +} diff --git a/generate.go b/generate.go new file mode 100644 index 0000000..6bb883b --- /dev/null +++ b/generate.go @@ -0,0 +1,4 @@ +package main + +//go:generate ogen --target petstore --clean openapi.yaml +//go:generate sqlc generate diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5375002 --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9d59264 --- /dev/null +++ b/go.sum @@ -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= diff --git a/main.go b/main.go new file mode 100644 index 0000000..e007c35 --- /dev/null +++ b/main.go @@ -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) +} diff --git a/ogen.nix b/ogen.nix new file mode 100644 index 0000000..40f16bf --- /dev/null +++ b/ogen.nix @@ -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 ]; + }; +} diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..232be63 --- /dev/null +++ b/openapi.yaml @@ -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 diff --git a/queries.sql b/queries.sql new file mode 100644 index 0000000..0d02a5a --- /dev/null +++ b/queries.sql @@ -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 = , StopSel = ' + ) :: 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); diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..1322887 --- /dev/null +++ b/schema.sql @@ -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); diff --git a/sqlc.yaml b/sqlc.yaml new file mode 100644 index 0000000..899751d --- /dev/null +++ b/sqlc.yaml @@ -0,0 +1,12 @@ +version: 2 +sql: + - engine: "postgresql" + schema: "schema.sql" + queries: "queries.sql" + gen: + go: + sql_package: "pgx/v5" + package: "data" + out: "data" + emit_json_tags: true + emit_empty_slices: true