diff --git a/CLAUDE.md b/CLAUDE.md index 830dcc8..888b5d8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,8 +21,8 @@ Monorepo: `backend/` (Go) + `frontend/` (SvelteKit). ## Design reference -The `docs/reference/` directory contains the previous Python/Flask version. -Use its visual design as the basis for the new frontend: +Visual design tokens for the frontend (carried over from the previous +Python/Flask version): - Color palette: #312F45 (bg), #9592B5 (accent), #444455 (tag default), #111118 (elevated) - Font: Epilogue (variable weight) - Dark theme is primary @@ -55,4 +55,4 @@ npm run generate:types # regenerate API types from openapi.yaml - Git: conventional commits with scope — `type(scope): message` - `(backend)` for Go backend code - `(frontend)` for SvelteKit/TypeScript code - - `(project)` for root-level files (.gitignore, docs/reference, structure) + - `(project)` for root-level files (.gitignore, docs, structure) diff --git a/docs/reference/api/tfm_api.py b/docs/reference/api/tfm_api.py deleted file mode 100644 index f2d7825..0000000 --- a/docs/reference/api/tfm_api.py +++ /dev/null @@ -1,374 +0,0 @@ -from configparser import ConfigParser -from psycopg2.pool import ThreadedConnectionPool -from psycopg2.extras import RealDictCursor -from contextlib import contextmanager -from os import access, W_OK, makedirs, chmod, system -from os.path import isfile, join, basename -from shutil import move -from magic import Magic -from preview_generator.manager import PreviewManager - -conf = None - -mage = None -previewer = None - -db_pool = None - -DEFAULT_SORTING = { - "files": { - "key": "created", - "asc": False - }, - "tags": { - "key": "created", - "asc": False - }, - "categories": { - "key": "created", - "asc": False - }, - "pools": { - "key": "created", - "asc": False - }, -} - - -def Initialize(conf_path="/etc/tfm/tfm.conf"): - global mage, previewer - load_config(conf_path) - mage = Magic(mime=True) - previewer = PreviewManager(conf["Paths"]["Thumbs"]) - db_connect(conf["DB.limits"]["MinimumConnections"], conf["DB.limits"]["MaximumConnections"], **conf["DB.params"]) - - -def load_config(path): - global conf - conf = ConfigParser() - conf.read(path) - - -def db_connect(minconn, maxconn, **kwargs): - global db_pool - db_pool = ThreadedConnectionPool(minconn, maxconn, **kwargs) - - -@contextmanager -def _db_cursor(): - global db_pool - try: - conn = db_pool.getconn() - except: - raise RuntimeError("Database not connected") - try: - with conn.cursor(cursor_factory=RealDictCursor) as cur: - yield cur - conn.commit() - except: - conn.rollback() - raise - finally: - db_pool.putconn(conn) - - -def _validate_column_name(cur, table, column): - cur.execute("SELECT get_column_names(%s) AS name", (table,)) - if all([column!=col["name"] for col in cur.fetchall()]): - raise RuntimeError("Invalid column name") - - -def authorize(username, password, useragent): - with _db_cursor() as cur: - cur.execute("SELECT tfm_session_request(tfm_user_auth(%s, %s), %s) AS sid", (username, password, useragent)) - sid = cur.fetchone()["sid"] - return TSession(sid) - - -class TSession: - sid = None - - def __init__(self, sid): - with _db_cursor() as cur: - cur.execute("SELECT tfm_session_validate(%s) IS NOT NULL AS valid", (sid,)) - if not cur.fetchone()["valid"]: - raise RuntimeError("Invalid sid") - self.sid = sid - - def terminate(self): - with _db_cursor() as cur: - cur.execute("CALL tfm_session_terminate(%s)", (self.sid,)) - del self - - @property - def username(self): - with _db_cursor() as cur: - cur.execute("SELECT tfm_session_username(%s) AS name", (self.sid,)) - return cur.fetchone()["name"] - - @property - def is_admin(self): - with _db_cursor() as cur: - cur.execute("SELECT * FROM tfm_user_get_info(%s)", (self.sid,)) - return cur.fetchone()["can_edit"] - - def get_files(self, order_key=DEFAULT_SORTING["files"]["key"], order_asc=DEFAULT_SORTING["files"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_files", order_key) - cur.execute("SELECT * FROM tfm_get_files(%%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid,)) - return list(map(dict, cur.fetchall())) - - def get_files_by_filter(self, philter=None, order_key=DEFAULT_SORTING["files"]["key"], order_asc=DEFAULT_SORTING["files"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_files", order_key) - cur.execute("SELECT * FROM tfm_get_files_by_filter(%%s, %%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid, philter)) - return list(map(dict, cur.fetchall())) - - def get_tags(self, order_key=DEFAULT_SORTING["tags"]["key"], order_asc=DEFAULT_SORTING["tags"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_tags", order_key) - cur.execute("SELECT * FROM tfm_get_tags(%%s) ORDER BY %s %s, name ASC OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid,)) - return list(map(dict, cur.fetchall())) - - def get_categories(self, order_key=DEFAULT_SORTING["categories"]["key"], order_asc=DEFAULT_SORTING["categories"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_categories", order_key) - cur.execute("SELECT * FROM tfm_get_categories(%%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid,)) - return list(map(dict, cur.fetchall())) - - def get_pools(self, order_key=DEFAULT_SORTING["pools"]["key"], order_asc=DEFAULT_SORTING["pools"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_pools", order_key) - cur.execute("SELECT * FROM tfm_get_pools(%%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid,)) - return list(map(dict, cur.fetchall())) - - def get_autotags(self, order_key="child_id", order_asc=True, offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_autotags", order_key) - cur.execute("SELECT * FROM tfm_get_autotags(%%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid,)) - return list(map(dict, cur.fetchall())) - - def get_my_sessions(self, order_key="started", order_asc=False, offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_sessions", order_key) - cur.execute("SELECT * FROM tfm_get_my_sessions(%%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid,)) - return list(map(dict, cur.fetchall())) - - def get_tags_by_file(self, file_id, order_key=DEFAULT_SORTING["tags"]["key"], order_asc=DEFAULT_SORTING["tags"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_tags", order_key) - cur.execute("SELECT * FROM tfm_get_tags_by_file(%%s, %%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid, file_id)) - return list(map(dict, cur.fetchall())) - - def get_files_by_tag(self, tag_id, order_key=DEFAULT_SORTING["files"]["key"], order_asc=DEFAULT_SORTING["files"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_files", order_key) - cur.execute("SELECT * FROM tfm_get_files_by_tag(%%s, %%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid, tag_id)) - return list(map(dict, cur.fetchall())) - - def get_files_by_pool(self, pool_id, order_key=DEFAULT_SORTING["files"]["key"], order_asc=DEFAULT_SORTING["files"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_files", order_key) - cur.execute("SELECT * FROM tfm_get_files_by_pool(%%s, %%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid, pool_id)) - return list(map(dict, cur.fetchall())) - - def get_parent_tags(self, tag_id, order_key=DEFAULT_SORTING["tags"]["key"], order_asc=DEFAULT_SORTING["tags"]["asc"], offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_tags", order_key) - cur.execute("SELECT * FROM tfm_get_parent_tags(%%s, %%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid, tag_id)) - return list(map(dict, cur.fetchall())) - - def get_my_file_views(self, file_id=None, order_key="datetime", order_asc=False, offset=0, limit=None): - with _db_cursor() as cur: - _validate_column_name(cur, "v_files", order_key) - cur.execute("SELECT * FROM tfm_get_my_file_views(%%s, %%s) ORDER BY %s %s OFFSET %s LIMIT %s" % ( - order_key, - "ASC" if order_asc else "DESC", - int(offset), - int(limit) if limit is not None else "ALL" - ), (self.sid, file_id)) - return list(map(dict, cur.fetchall())) - - def get_file(self, file_id): - with _db_cursor() as cur: - cur.execute("SELECT * FROM tfm_get_files(%s) WHERE id=%s", (self.sid, file_id)) - return cur.fetchone() - - def get_tag(self, tag_id): - with _db_cursor() as cur: - cur.execute("SELECT * FROM tfm_get_tags(%s) WHERE id=%s", (self.sid, tag_id)) - return cur.fetchone() - - def get_category(self, category_id): - with _db_cursor() as cur: - cur.execute("SELECT * FROM tfm_get_categories(%s) WHERE id=%s", (self.sid, category_id)) - return cur.fetchone() - - def view_file(self, file_id): - with _db_cursor() as cur: - cur.execute("CALL tfm_view_file(%s, %s)", (self.sid, file_id)) - - def add_file(self, path, datetime=None, notes=None, is_private=None, orig_name=True): - if not isfile(path): - raise FileNotFoundError("No such file '%s'" % path) - if not access(conf["Paths"]["Files"], W_OK) or not access(conf["Paths"]["Thumbs"], W_OK): - raise PermissionError("Invalid directories for files and thumbs") - mime = mage.from_file(path) - if orig_name == True: - orig_name = basename(path) - with _db_cursor() as cur: - cur.execute("SELECT * FROM tfm_add_file(%s, %s, %s, %s, %s, %s)", (self.sid, mime, datetime, notes, is_private, orig_name)) - res = cur.fetchone() - file_id = res["f_id"] - ext = res["ext"] - file_path = join(conf["Paths"]["Files"], file_id) - move(path, file_path) - thumb_path = previewer.get_jpeg_preview(file_path, height=160, width=160) - preview_path = previewer.get_jpeg_preview(file_path, height=1080, width=1920) - chmod(file_path, 0o664) - chmod(thumb_path, 0o664) - chmod(preview_path, 0o664) - return file_id, ext - - def add_tag(self, name, notes=None, color=None, category_id=None, is_private=None): - if color is not None: - color = color.replace('#', '') - if not category_id: - category_id = None - with _db_cursor() as cur: - cur.execute("SELECT tfm_add_tag(%s, %s, %s, %s, %s, %s) AS id", (self.sid, name, notes, color, category_id, is_private)) - return cur.fetchone()["id"] - - def add_category(self, name, notes=None, color=None, is_private=None): - if color is not None: - color = color.replace('#', '') - with _db_cursor() as cur: - cur.execute("SELECT tfm_add_category(%s, %s, %s, %s, %s) AS id", (self.sid, name, notes, color, is_private)) - return cur.fetchone()["id"] - - def add_pool(self, name, notes=None, parent_id=None, is_private=None): - with _db_cursor() as cur: - cur.execute("SELECT tfm_add_pool(%s, %s, %s, %s, %s) AS id", (self.sid, name, notes, parent_id, is_private)) - return cur.fetchone()["id"] - - def add_autotag(self, child_id, parent_id, is_active=None, apply_to_existing=None): - with _db_cursor() as cur: - cur.execute("SELECT tfm_add_autotag(%s, %s, %s, %s, %s) AS added", (self.sid, child_id, parent_id, is_active, apply_to_existing)) - return cur.fetchone()["added"] - - def add_file_to_tag(self, file_id, tag_id): - with _db_cursor() as cur: - cur.execute("SELECT tfm_add_file_to_tag(%s, %s, %s) AS id", (self.sid, file_id, tag_id)) - return list(map(lambda t: t["id"], cur.fetchall())) - - def add_file_to_pool(self, file_id, pool_id): - with _db_cursor() as cur: - cur.execute("SELECT tfm_add_file_to_pool(%s, %s, %s) AS added", (self.sid, file_id, pool_id)) - return cur.fetchone()["added"] - - def edit_file(self, file_id, mime=None, datetime=None, notes=None, is_private=None): - with _db_cursor() as cur: - cur.execute("CALL tfm_edit_file(%s, %s, %s, %s, %s, %s)", (self.sid, file_id, mime, datetime, notes, is_private)) - - def edit_tag(self, tag_id, name=None, notes=None, color=None, category_id=None, is_private=None): - if color is not None: - color = color.replace('#', '') - if not category_id: - category_id = None - with _db_cursor() as cur: - cur.execute("CALL tfm_edit_tag(%s, %s, %s, %s, %s, %s, %s)", (self.sid, tag_id, name, notes, color, category_id, is_private)) - - def edit_category(self, category_id, name=None, notes=None, color=None, is_private=None): - if color is not None: - color = color.replace('#', '') - with _db_cursor() as cur: - cur.execute("CALL tfm_edit_category(%s, %s, %s, %s, %s, %s)", (self.sid, category_id, name, notes, color, is_private)) - - def edit_pool(self, pool_id, name=None, notes=None, parent_id=None, is_private=None): - with _db_cursor() as cur: - cur.execute("CALL tfm_edit_pool(%s, %s, %s, %s, %s, %s)", (self.sid, pool_id, name, notes, parent_id, is_private)) - - def remove_file(self, file_id): - with _db_cursor() as cur: - cur.execute("CALL tfm_remove_file(%s, %s)", (self.sid, file_id)) - if system("rm %s/%s*" % (conf["Paths"]["Files"], file_id)): - raise RuntimeError("Failed to remove file '%s'" % file_id) - - def remove_tag(self, tag_id): - with _db_cursor() as cur: - cur.execute("CALL tfm_remove_tag(%s, %s)", (self.sid, tag_id)) - - def remove_category(self, category_id): - with _db_cursor() as cur: - cur.execute("CALL tfm_remove_category(%s, %s)", (self.sid, category_id)) - - def remove_pool(self, pool_id): - with _db_cursor() as cur: - cur.execute("CALL tfm_remove_pool(%s, %s)", (self.sid, pool_id)) - - def remove_autotag(self, child_id, parent_id): - with _db_cursor() as cur: - cur.execute("CALL tfm_remove_autotag(%s, %s, %s)", (self.sid, child_id, parent_id)) - - def remove_file_to_tag(self, file_id, tag_id): - with _db_cursor() as cur: - cur.execute("CALL tfm_remove_file_to_tag(%s, %s, %s)", (self.sid, file_id, tag_id)) - - def remove_file_to_pool(self, file_id, pool_id): - with _db_cursor() as cur: - cur.execute("CALL tfm_remove_file_to_pool(%s, %s, %s)", (self.sid, file_id, pool_id)) diff --git a/docs/reference/backend/cmd/main.go b/docs/reference/backend/cmd/main.go deleted file mode 100644 index 92b9c84..0000000 --- a/docs/reference/backend/cmd/main.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "tanabata/internal/storage/postgres" -) - -func main() { - postgres.InitDB("postgres://hiko:taikibansei@192.168.0.25/Tanabata_new?application_name=Tanabata%20testing") - // test_json := json.RawMessage([]byte("{\"valery\": \"ponosoff\"}")) - // data, statusCode, err := db.FileGetSlice(1, "", "+2", -2, 0) - // data, statusCode, err := db.FileGet(1, "0197d056-cfb0-76b5-97e0-bd588826393c") - // data, statusCode, err := db.FileAdd(1, "ABOBA.png", "image/png", time.Now(), "slkdfjsldkflsdkfj;sldkf", test_json) - // statusCode, err := db.FileUpdate(2, "0197d159-bf3a-7617-a3a8-a4a9fc39eca6", map[string]interface{}{ - // "name": "ponos.png", - // }) - // statusCode, err := db.FileDelete(1, "0197d155-848f-7221-ba4a-4660f257c7d5") - // v, e, err := postgres.FileGetAccess(1, "0197d15a-57f9-712c-991e-c512290e774f") - // fmt.Printf("V: %s, E: %s\n", v, e) - // fmt.Printf("Status: %d\n", statusCode) - // fmt.Printf("Error: %s\n", err) - // fmt.Printf("%+v\n", data) -} diff --git a/docs/reference/backend/cmd/main.sync-conflict-20251008-195720-BYUI6ZC.go b/docs/reference/backend/cmd/main.sync-conflict-20251008-195720-BYUI6ZC.go deleted file mode 100644 index 0fd9c90..0000000 --- a/docs/reference/backend/cmd/main.sync-conflict-20251008-195720-BYUI6ZC.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "fmt" - - "tanabata/db" -) - -func main() { - db.InitDB("postgres://hiko:taikibansei@192.168.0.25/Tanabata_new?application_name=Tanabata%20testing") - // test_json := json.RawMessage([]byte("{\"valery\": \"ponosoff\"}")) - // data, statusCode, err := db.FileGetSlice(2, "", "+2", -2, 0) - // data, statusCode, err := db.FileGet(1, "0197d056-cfb0-76b5-97e0-bd588826393c") - // data, statusCode, err := db.FileAdd(1, "ABOBA.png", "image/png", time.Now(), "slkdfjsldkflsdkfj;sldkf", test_json) - // statusCode, err := db.FileUpdate(2, "0197d159-bf3a-7617-a3a8-a4a9fc39eca6", map[string]interface{}{ - // "name": "ponos.png", - // }) - statusCode, err := db.FileDelete(1, "0197d155-848f-7221-ba4a-4660f257c7d5") - fmt.Printf("Status: %d\n", statusCode) - fmt.Printf("Error: %s\n", err) - // fmt.Printf("%+v\n", data) -} diff --git a/docs/reference/backend/db/db.go b/docs/reference/backend/db/db.go deleted file mode 100644 index 6f1c7b7..0000000 --- a/docs/reference/backend/db/db.go +++ /dev/null @@ -1,79 +0,0 @@ -package db - -import ( - "context" - "errors" - "fmt" - "net/http" - "time" - - "github.com/jackc/pgx/v5" - "github.com/jackc/pgx/v5/pgconn" - "github.com/jackc/pgx/v5/pgxpool" -) - -var connPool *pgxpool.Pool - -func InitDB(connString string) error { - poolConfig, err := pgxpool.ParseConfig(connString) - if err != nil { - return fmt.Errorf("error while parsing connection string: %w", err) - } - - poolConfig.MaxConns = 100 - poolConfig.MinConns = 0 - poolConfig.MaxConnLifetime = time.Hour - poolConfig.HealthCheckPeriod = 30 * time.Second - - connPool, err = pgxpool.NewWithConfig(context.Background(), poolConfig) - if err != nil { - return fmt.Errorf("error while initializing DB connections pool: %w", err) - } - return nil -} - -func transaction(handler func(context.Context, pgx.Tx) (statusCode int, err error)) (statusCode int, err error) { - ctx := context.Background() - tx, err := connPool.Begin(ctx) - if err != nil { - statusCode = http.StatusInternalServerError - return - } - statusCode, err = handler(ctx, tx) - if err != nil { - tx.Rollback(ctx) - return - } - err = tx.Commit(ctx) - if err != nil { - statusCode = http.StatusInternalServerError - } - return -} - -// Handle database error -func handleDBError(errIn error) (statusCode int, err error) { - if errIn == nil { - statusCode = http.StatusOK - return - } - if errors.Is(errIn, pgx.ErrNoRows) { - err = fmt.Errorf("not found") - statusCode = http.StatusNotFound - return - } - var pgErr *pgconn.PgError - if errors.As(errIn, &pgErr) { - switch pgErr.Code { - case "22P02", "22007": // Invalid data format - err = fmt.Errorf("%s", pgErr.Message) - statusCode = http.StatusBadRequest - return - case "23505": // Unique constraint violation - err = fmt.Errorf("already exists") - statusCode = http.StatusConflict - return - } - } - return http.StatusInternalServerError, errIn -} diff --git a/docs/reference/backend/db/utils.go b/docs/reference/backend/db/utils.go deleted file mode 100644 index 2274099..0000000 --- a/docs/reference/backend/db/utils.go +++ /dev/null @@ -1,53 +0,0 @@ -package db - -import ( - "fmt" - "net/http" - "strconv" - "strings" -) - -// Convert "filter" URL param to SQL "WHERE" condition -func filterToSQL(filter string) (sql string, statusCode int, err error) { - // filterTokens := strings.Split(string(filter), ";") - sql = "(true)" - return -} - -// Convert "sort" URL param to SQL "ORDER BY" -func sortToSQL(sort string) (sql string, statusCode int, err error) { - if sort == "" { - return - } - sortOptions := strings.Split(sort, ",") - sql = " ORDER BY " - for i, sortOption := range sortOptions { - sortOrder := sortOption[:1] - sortColumn := sortOption[1:] - // parse sorting order marker - switch sortOrder { - case "+": - sortOrder = "ASC" - case "-": - sortOrder = "DESC" - default: - err = fmt.Errorf("invalid sorting order mark: %q", sortOrder) - statusCode = http.StatusBadRequest - return - } - // validate sorting column - var n int - n, err = strconv.Atoi(sortColumn) - if err != nil || n < 0 { - err = fmt.Errorf("invalid sorting column: %q", sortColumn) - statusCode = http.StatusBadRequest - return - } - // add sorting option to query - if i > 0 { - sql += "," - } - sql += fmt.Sprintf("%s %s NULLS LAST", sortColumn, sortOrder) - } - return -} diff --git a/docs/reference/backend/go.mod b/docs/reference/backend/go.mod deleted file mode 100644 index f4a63a1..0000000 --- a/docs/reference/backend/go.mod +++ /dev/null @@ -1,19 +0,0 @@ -module tanabata - -go 1.23.0 - -toolchain go1.23.10 - -require github.com/jackc/pgx/v5 v5.7.5 - -require ( - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx v3.6.2+incompatible // indirect - github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/stretchr/testify v1.9.0 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/text v0.24.0 // indirect -) diff --git a/docs/reference/backend/go.sum b/docs/reference/backend/go.sum deleted file mode 100644 index 744b3fd..0000000 --- a/docs/reference/backend/go.sum +++ /dev/null @@ -1,32 +0,0 @@ -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/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-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o= -github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= -github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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/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= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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/docs/reference/backend/internal/domain/domain.go b/docs/reference/backend/internal/domain/domain.go deleted file mode 100644 index 9b661eb..0000000 --- a/docs/reference/backend/internal/domain/domain.go +++ /dev/null @@ -1,122 +0,0 @@ -package domain - -import ( - "encoding/json" - "time" - - "github.com/jackc/pgx/v5/pgtype" -) - -type User struct { - Name string `json:"name"` - IsAdmin bool `json:"isAdmin"` - CanCreate bool `json:"canCreate"` -} - -type MIME struct { - Name string `json:"name"` - Extension string `json:"extension"` -} - -type ( - CategoryCore struct { - ID string `json:"id"` - Name string `json:"name"` - Color pgtype.Text `json:"color"` - } - CategoryItem struct { - CategoryCore - } - CategoryFull struct { - CategoryCore - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - Notes pgtype.Text `json:"notes"` - } -) - -type ( - FileCore struct { - ID string `json:"id"` - Name pgtype.Text `json:"name"` - MIME MIME `json:"mime"` - } - FileItem struct { - FileCore - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - } - FileFull struct { - FileCore - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - Notes pgtype.Text `json:"notes"` - Metadata json.RawMessage `json:"metadata"` - Tags []TagCore `json:"tags"` - Viewed int `json:"viewed"` - } -) - -type ( - TagCore struct { - ID string `json:"id"` - Name string `json:"name"` - Color pgtype.Text `json:"color"` - } - TagItem struct { - TagCore - Category CategoryCore `json:"category"` - } - TagFull struct { - TagCore - Category CategoryCore `json:"category"` - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - Notes pgtype.Text `json:"notes"` - UsedIncl int `json:"usedIncl"` - UsedExcl int `json:"usedExcl"` - } -) - -type Autotag struct { - TriggerTag TagCore `json:"triggerTag"` - AddTag TagCore `json:"addTag"` - IsActive bool `json:"isActive"` -} - -type ( - PoolCore struct { - ID string `json:"id"` - Name string `json:"name"` - } - PoolItem struct { - PoolCore - } - PoolFull struct { - PoolCore - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - Notes pgtype.Text `json:"notes"` - Viewed int `json:"viewed"` - } -) - -type Session struct { - ID int `json:"id"` - UserAgent string `json:"userAgent"` - StartedAt time.Time `json:"startedAt"` - ExpiresAt time.Time `json:"expiresAt"` - LastActivity time.Time `json:"lastActivity"` -} - -type Pagination struct { - Total int `json:"total"` - Offset int `json:"offset"` - Limit int `json:"limit"` - Count int `json:"count"` -} - -type Slice[T any] struct { - Pagination Pagination `json:"pagination"` - Data []T `json:"data"` -} diff --git a/docs/reference/backend/internal/storage/postgres/auth.go b/docs/reference/backend/internal/storage/postgres/auth.go deleted file mode 100644 index 3aaf07b..0000000 --- a/docs/reference/backend/internal/storage/postgres/auth.go +++ /dev/null @@ -1,16 +0,0 @@ -package postgres - -import "context" - -func UserLogin(ctx context.Context, name, password string) (user_id int, err error) { - row := connPool.QueryRow(ctx, "SELECT id FROM users WHERE name=$1 AND password=crypt($2, password)", name, password) - err = row.Scan(&user_id) - return -} - -func UserAuth(ctx context.Context, user_id int) (ok, isAdmin bool) { - row := connPool.QueryRow(ctx, "SELECT is_admin FROM users WHERE id=$1", user_id) - err := row.Scan(&isAdmin) - ok = (err == nil) - return -} diff --git a/docs/reference/backend/internal/storage/postgres/file.go b/docs/reference/backend/internal/storage/postgres/file.go deleted file mode 100644 index 91f968d..0000000 --- a/docs/reference/backend/internal/storage/postgres/file.go +++ /dev/null @@ -1,268 +0,0 @@ -package postgres - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/jackc/pgx/v5" - "github.com/jackc/pgx/v5/pgxpool" - - "tanabata/internal/domain" -) - -type FileStore struct { - db *pgxpool.Pool -} - -func NewFileStore(db *pgxpool.Pool) *FileStore { - return &FileStore{db: db} -} - -// Get user's access rights to file -func (s *FileStore) getAccess(user_id int, file_id string) (canView, canEdit bool, err error) { - ctx := context.Background() - row := connPool.QueryRow(ctx, ` - SELECT - COALESCE(a.view, FALSE) OR f.creator_id=$1 OR COALESCE(u.is_admin, FALSE), - COALESCE(a.edit, FALSE) OR f.creator_id=$1 OR COALESCE(u.is_admin, FALSE) - FROM data.files f - LEFT JOIN acl.files a ON a.file_id=f.id AND a.user_id=$1 - LEFT JOIN system.users u ON u.id=$1 - WHERE f.id=$2 - `, user_id, file_id) - err = row.Scan(&canView, &canEdit) - return -} - -// Get a set of files -func (s *FileStore) GetSlice(user_id int, filter, sort string, limit, offset int) (files domain.Slice[domain.FileItem], statusCode int, err error) { - filterCond, statusCode, err := filterToSQL(filter) - if err != nil { - return - } - sortExpr, statusCode, err := sortToSQL(sort) - if err != nil { - return - } - // prepare query - query := ` - SELECT - f.id, - f.name, - m.name, - m.extension, - uuid_extract_timestamp(f.id), - u.name, - u.is_admin - FROM data.files f - JOIN system.mime m ON m.id=f.mime_id - JOIN system.users u ON u.id=f.creator_id - WHERE NOT f.is_deleted AND (f.creator_id=$1 OR (SELECT view FROM acl.files WHERE file_id=f.id AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1)) AND - ` - query += filterCond - queryCount := query - query += sortExpr - if limit >= 0 { - query += fmt.Sprintf(" LIMIT %d", limit) - } - if offset > 0 { - query += fmt.Sprintf(" OFFSET %d", offset) - } - // execute query - statusCode, err = transaction(func(ctx context.Context, tx pgx.Tx) (statusCode int, err error) { - rows, err := tx.Query(ctx, query, user_id) - if err != nil { - statusCode, err = handleDBError(err) - return - } - defer rows.Close() - count := 0 - for rows.Next() { - var file domain.FileItem - err = rows.Scan(&file.ID, &file.Name, &file.MIME.Name, &file.MIME.Extension, &file.CreatedAt, &file.Creator.Name, &file.Creator.IsAdmin) - if err != nil { - statusCode = http.StatusInternalServerError - return - } - files.Data = append(files.Data, file) - count++ - } - err = rows.Err() - if err != nil { - statusCode = http.StatusInternalServerError - return - } - files.Pagination.Limit = limit - files.Pagination.Offset = offset - files.Pagination.Count = count - row := tx.QueryRow(ctx, fmt.Sprintf("SELECT COUNT(*) FROM (%s) tmp", queryCount), user_id) - err = row.Scan(&files.Pagination.Total) - if err != nil { - statusCode = http.StatusInternalServerError - } - return - }) - if err == nil { - statusCode = http.StatusOK - } - return -} - -// Get file -func (s *FileStore) Get(user_id int, file_id string) (file domain.FileFull, statusCode int, err error) { - ctx := context.Background() - row := connPool.QueryRow(ctx, ` - SELECT - f.id, - f.name, - m.name, - m.extension, - uuid_extract_timestamp(f.id), - u.name, - u.is_admin, - f.notes, - f.metadata, - (SELECT COUNT(*) FROM activity.file_views fv WHERE fv.file_id=$2 AND fv.user_id=$1) - FROM data.files f - JOIN system.mime m ON m.id=f.mime_id - JOIN system.users u ON u.id=f.creator_id - WHERE NOT f.is_deleted AND f.id=$2 AND (f.creator_id=$1 OR (SELECT view FROM acl.files WHERE file_id=$2 AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1)) - `, user_id, file_id) - err = row.Scan(&file.ID, &file.Name, &file.MIME.Name, &file.MIME.Extension, &file.CreatedAt, &file.Creator.Name, &file.Creator.IsAdmin, &file.Notes, &file.Metadata, &file.Viewed) - if err != nil { - statusCode, err = handleDBError(err) - return - } - rows, err := connPool.Query(ctx, ` - SELECT - t.id, - t.name, - COALESCE(t.color, c.color) - FROM data.tags t - LEFT JOIN data.categories c ON c.id=t.category_id - JOIN data.file_tag ft ON ft.tag_id=t.id - WHERE ft.file_id=$1 - `, file_id) - if err != nil { - statusCode, err = handleDBError(err) - return - } - defer rows.Close() - for rows.Next() { - var tag domain.TagCore - err = rows.Scan(&tag.ID, &tag.Name, &tag.Color) - if err != nil { - statusCode = http.StatusInternalServerError - return - } - file.Tags = append(file.Tags, tag) - } - err = rows.Err() - if err != nil { - statusCode = http.StatusInternalServerError - return - } - statusCode = http.StatusOK - return -} - -// Add file -func (s *FileStore) Add(user_id int, name, mime string, datetime time.Time, notes string, metadata json.RawMessage) (file domain.FileCore, statusCode int, err error) { - ctx := context.Background() - var mime_id int - var extension string - row := connPool.QueryRow(ctx, "SELECT id, extension FROM system.mime WHERE name=$1", mime) - err = row.Scan(&mime_id, &extension) - if err != nil { - if err == pgx.ErrNoRows { - err = fmt.Errorf("unsupported file type: %q", mime) - statusCode = http.StatusBadRequest - } else { - statusCode, err = handleDBError(err) - } - return - } - row = connPool.QueryRow(ctx, ` - INSERT INTO data.files (name, mime_id, datetime, creator_id, notes, metadata) - VALUES (NULLIF($1, ''), $2, $3, $4, NULLIF($5 ,''), $6) - RETURNING id - `, name, mime_id, datetime, user_id, notes, metadata) - err = row.Scan(&file.ID) - if err != nil { - statusCode, err = handleDBError(err) - return - } - file.Name.String = name - file.Name.Valid = (name != "") - file.MIME.Name = mime - file.MIME.Extension = extension - statusCode = http.StatusOK - return -} - -// Update file -func (s *FileStore) Update(user_id int, file_id string, updates map[string]interface{}) (statusCode int, err error) { - if len(updates) == 0 { - err = fmt.Errorf("no fields provided for update") - statusCode = http.StatusBadRequest - return - } - writableFields := map[string]bool{ - "name": true, - "datetime": true, - "notes": true, - "metadata": true, - } - query := "UPDATE data.files SET" - newValues := []interface{}{user_id} - count := 2 - for field, value := range updates { - if !writableFields[field] { - err = fmt.Errorf("invalid field: %q", field) - statusCode = http.StatusBadRequest - return - } - query += fmt.Sprintf(" %s=NULLIF($%d, '')", field, count) - newValues = append(newValues, value) - count++ - } - query += fmt.Sprintf( - " WHERE id=$%d AND (creator_id=$1 OR (SELECT edit FROM acl.files WHERE file_id=$%d AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1))", - count, count) - newValues = append(newValues, file_id) - ctx := context.Background() - commandTag, err := connPool.Exec(ctx, query, newValues...) - if err != nil { - statusCode, err = handleDBError(err) - return - } - if commandTag.RowsAffected() == 0 { - err = fmt.Errorf("not found") - statusCode = http.StatusNotFound - return - } - statusCode = http.StatusNoContent - return -} - -// Delete file -func (s *FileStore) Delete(user_id int, file_id string) (statusCode int, err error) { - ctx := context.Background() - commandTag, err := connPool.Exec(ctx, - "DELETE FROM data.files WHERE id=$2 AND (creator_id=$1 OR (SELECT edit FROM acl.files WHERE file_id=$2 AND user_id=$1) OR (SELECT is_admin FROM system.users WHERE id=$1))", - user_id, file_id) - if err != nil { - statusCode, err = handleDBError(err) - return - } - if commandTag.RowsAffected() == 0 { - err = fmt.Errorf("not found") - statusCode = http.StatusNotFound - return - } - statusCode = http.StatusNoContent - return -} diff --git a/docs/reference/backend/internal/storage/postgres/store.go b/docs/reference/backend/internal/storage/postgres/store.go deleted file mode 100644 index e255602..0000000 --- a/docs/reference/backend/internal/storage/postgres/store.go +++ /dev/null @@ -1,92 +0,0 @@ -package postgres - -import ( - "context" - "errors" - "fmt" - "net/http" - "time" - - "github.com/jackc/pgx/v5" - "github.com/jackc/pgx/v5/pgconn" - "github.com/jackc/pgx/v5/pgxpool" -) - -type Storage struct { - db *pgxpool.Pool -} - -var connPool *pgxpool.Pool - -// Initialize new database storage -func New(dbURL string) (*Storage, error) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - config, err := pgxpool.ParseConfig(dbURL) - if err != nil { - return nil, fmt.Errorf("failed to parse DB URL: %w", err) - } - config.MaxConns = 10 - config.MinConns = 2 - config.HealthCheckPeriod = time.Minute - db, err := pgxpool.NewWithConfig(ctx, config) - if err != nil { - return nil, fmt.Errorf("failed to connect to database: %w", err) - } - err = db.Ping(ctx) - if err != nil { - return nil, fmt.Errorf("database ping failed: %w", err) - } - return &Storage{db: db}, nil -} - -// Close database storage -func (s *Storage) Close() { - s.db.Close() -} - -// Run handler inside transaction -func (s *Storage) transaction(ctx context.Context, handler func(context.Context, pgx.Tx) (statusCode int, err error)) (statusCode int, err error) { - tx, err := connPool.Begin(ctx) - if err != nil { - statusCode = http.StatusInternalServerError - return - } - statusCode, err = handler(ctx, tx) - if err != nil { - tx.Rollback(ctx) - return - } - err = tx.Commit(ctx) - if err != nil { - statusCode = http.StatusInternalServerError - } - return -} - -// Handle database error -func (s *Storage) handleDBError(errIn error) (statusCode int, err error) { - if errIn == nil { - statusCode = http.StatusOK - return - } - if errors.Is(errIn, pgx.ErrNoRows) { - err = fmt.Errorf("not found") - statusCode = http.StatusNotFound - return - } - var pgErr *pgconn.PgError - if errors.As(errIn, &pgErr) { - switch pgErr.Code { - case "22P02", "22007": // Invalid data format - err = fmt.Errorf("%s", pgErr.Message) - statusCode = http.StatusBadRequest - return - case "23505": // Unique constraint violation - err = fmt.Errorf("already exists") - statusCode = http.StatusConflict - return - } - } - return http.StatusInternalServerError, errIn -} diff --git a/docs/reference/backend/internal/storage/postgres/utils.go b/docs/reference/backend/internal/storage/postgres/utils.go deleted file mode 100644 index f190f5a..0000000 --- a/docs/reference/backend/internal/storage/postgres/utils.go +++ /dev/null @@ -1,53 +0,0 @@ -package postgres - -import ( - "fmt" - "net/http" - "strconv" - "strings" -) - -// Convert "filter" URL param to SQL "WHERE" condition -func filterToSQL(filter string) (sql string, statusCode int, err error) { - // filterTokens := strings.Split(string(filter), ";") - sql = "(true)" - return -} - -// Convert "sort" URL param to SQL "ORDER BY" -func sortToSQL(sort string) (sql string, statusCode int, err error) { - if sort == "" { - return - } - sortOptions := strings.Split(sort, ",") - sql = " ORDER BY " - for i, sortOption := range sortOptions { - sortOrder := sortOption[:1] - sortColumn := sortOption[1:] - // parse sorting order marker - switch sortOrder { - case "+": - sortOrder = "ASC" - case "-": - sortOrder = "DESC" - default: - err = fmt.Errorf("invalid sorting order mark: %q", sortOrder) - statusCode = http.StatusBadRequest - return - } - // validate sorting column - var n int - n, err = strconv.Atoi(sortColumn) - if err != nil || n < 0 { - err = fmt.Errorf("invalid sorting column: %q", sortColumn) - statusCode = http.StatusBadRequest - return - } - // add sorting option to query - if i > 0 { - sql += "," - } - sql += fmt.Sprintf("%s %s NULLS LAST", sortColumn, sortOrder) - } - return -} diff --git a/docs/reference/backend/internal/storage/storage.go b/docs/reference/backend/internal/storage/storage.go deleted file mode 100644 index 369879d..0000000 --- a/docs/reference/backend/internal/storage/storage.go +++ /dev/null @@ -1,21 +0,0 @@ -package storage - -import ( - "encoding/json" - "time" - - "tanabata/internal/domain" -) - -type Storage interface { - FileRepository - Close() -} - -type FileRepository interface { - GetSlice(user_id int, filter, sort string, limit, offset int) (files domain.Slice[domain.FileItem], statusCode int, err error) - Get(user_id int, file_id string) (file domain.FileFull, statusCode int, err error) - Add(user_id int, name, mime string, datetime time.Time, notes string, metadata json.RawMessage) (file domain.FileCore, statusCode int, err error) - Update(user_id int, file_id string, updates map[string]interface{}) (statusCode int, err error) - Delete(user_id int, file_id string) (statusCode int, err error) -} diff --git a/docs/reference/backend/models/models.go b/docs/reference/backend/models/models.go deleted file mode 100644 index 7a66022..0000000 --- a/docs/reference/backend/models/models.go +++ /dev/null @@ -1,120 +0,0 @@ -package models - -import ( - "encoding/json" - "time" - - "github.com/jackc/pgx/v5/pgtype" -) - -type User struct { - Name string `json:"name"` - IsAdmin bool `json:"isAdmin"` - CanCreate bool `json:"canCreate"` -} - -type MIME struct { - Name string `json:"name"` - Extension string `json:"extension"` -} - -type ( - CategoryCore struct { - ID string `json:"id"` - Name string `json:"name"` - Color pgtype.Text `json:"color"` - } - CategoryItem struct { - CategoryCore - } - CategoryFull struct { - CategoryCore - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - Notes pgtype.Text `json:"notes"` - } -) - -type ( - FileCore struct { - ID string `json:"id"` - Name pgtype.Text `json:"name"` - MIME MIME `json:"mime"` - } - FileItem struct { - FileCore - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - } - FileFull struct { - FileCore - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - Notes pgtype.Text `json:"notes"` - Metadata json.RawMessage `json:"metadata"` - Tags []TagCore `json:"tags"` - Viewed int `json:"viewed"` - } -) - -type ( - TagCore struct { - ID string `json:"id"` - Name string `json:"name"` - Color pgtype.Text `json:"color"` - } - TagItem struct { - TagCore - Category CategoryCore `json:"category"` - } - TagFull struct { - TagCore - Category CategoryCore `json:"category"` - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - Notes pgtype.Text `json:"notes"` - } -) - -type Autotag struct { - TriggerTag TagCore `json:"triggerTag"` - AddTag TagCore `json:"addTag"` - IsActive bool `json:"isActive"` -} - -type ( - PoolCore struct { - ID string `json:"id"` - Name string `json:"name"` - } - PoolItem struct { - PoolCore - } - PoolFull struct { - PoolCore - CreatedAt time.Time `json:"createdAt"` - Creator User `json:"creator"` - Notes pgtype.Text `json:"notes"` - Viewed int `json:"viewed"` - } -) - -type Session struct { - ID int `json:"id"` - UserAgent string `json:"userAgent"` - StartedAt time.Time `json:"startedAt"` - ExpiresAt time.Time `json:"expiresAt"` - LastActivity time.Time `json:"lastActivity"` -} - -type Pagination struct { - Total int `json:"total"` - Offset int `json:"offset"` - Limit int `json:"limit"` - Count int `json:"count"` -} - -type Slice[T any] struct { - Pagination Pagination `json:"pagination"` - Data []T `json:"data"` -} diff --git a/docs/reference/schema.sql b/docs/reference/schema.sql deleted file mode 100644 index d8526fc..0000000 --- a/docs/reference/schema.sql +++ /dev/null @@ -1,2286 +0,0 @@ --- --- PostgreSQL database dump --- - --- Dumped from database version 14.20 (Ubuntu 14.20-1.pgdg22.04+1) --- Dumped by pg_dump version 17.4 - --- Started on 2026-03-31 00:31:48 - -SET statement_timeout = 0; -SET lock_timeout = 0; -SET idle_in_transaction_session_timeout = 0; -SET transaction_timeout = 0; -SET client_encoding = 'UTF8'; -SET standard_conforming_strings = on; -SELECT pg_catalog.set_config('search_path', '', false); -SET check_function_bodies = false; -SET xmloption = content; -SET client_min_messages = warning; -SET row_security = off; - --- --- TOC entry 7 (class 2615 OID 2200) --- Name: public; Type: SCHEMA; Schema: -; Owner: hiko --- - --- *not* creating schema, since initdb creates it - - -ALTER SCHEMA public OWNER TO hiko; - --- --- TOC entry 2 (class 3079 OID 16486) --- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public; - - --- --- TOC entry 3596 (class 0 OID 0) --- Dependencies: 2 --- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; - - --- --- TOC entry 3 (class 3079 OID 16475) --- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public; - - --- --- TOC entry 3597 (class 0 OID 0) --- Dependencies: 3 --- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; - - --- --- TOC entry 987 (class 1247 OID 28864) --- Name: file; Type: TYPE; Schema: public; Owner: hiko --- - -CREATE TYPE public.file AS ( - id uuid, - mime_id uuid, - mime_name character varying(127), - extension character varying(16), - orig_name character varying(256), - datetime timestamp with time zone, - notes character varying(1024), - created timestamp with time zone, - creator_id uuid, - creator_name character varying(32), - is_private boolean -); - - -ALTER TYPE public.file OWNER TO hiko; - --- --- TOC entry 315 (class 1255 OID 17507) --- Name: get_column_names(name, name); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.get_column_names(table_n name, schema_n name DEFAULT 'public'::name) RETURNS SETOF name - LANGUAGE sql SECURITY DEFINER - AS $$ -SELECT column_name FROM information_schema.columns WHERE table_name = table_n AND table_schema = schema_n; -$$; - - -ALTER FUNCTION public.get_column_names(table_n name, schema_n name) OWNER TO hiko; - --- --- TOC entry 322 (class 1255 OID 17546) --- Name: tfm__add_file_to_tag_recursive(uuid, uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm__add_file_to_tag_recursive(f_id uuid, t_id uuid) RETURNS SETOF uuid - LANGUAGE plpgsql - AS $$ -DECLARE - tmp uuid; - pt_id uuid; - ppt_id uuid; -BEGIN - INSERT INTO file_tag VALUES (f_id, t_id) ON CONFLICT DO NOTHING RETURNING tag_id INTO tmp; - IF tmp IS NULL THEN - RETURN; - END IF; - RETURN NEXT t_id; - FOR pt_id IN - SELECT a.parent_id FROM autotags a WHERE a.child_id=t_id AND a.is_active - LOOP - FOR ppt_id IN SELECT tfm__add_file_to_tag_recursive(f_id, pt_id) - LOOP - RETURN NEXT ppt_id; - END LOOP; - END LOOP; -END; -$$; - - -ALTER FUNCTION public.tfm__add_file_to_tag_recursive(f_id uuid, t_id uuid) OWNER TO hiko; - --- --- TOC entry 323 (class 1255 OID 17539) --- Name: tfm_add_autotag(uuid, uuid, uuid, boolean, boolean); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_add_autotag(s_id uuid, tc_id uuid, tp_id uuid, is_active boolean DEFAULT NULL::boolean, apply_to_existing boolean DEFAULT NULL::boolean) RETURNS boolean - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; - ct_id uuid; - pt_id uuid; - f_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - PERFORM FROM tags t WHERE t.id=tc_id AND (NOT t.is_private OR t.creator_id=u_id); - IF NOT FOUND THEN RAISE 'Invalid child tag id'; END IF; - PERFORM FROM tags t WHERE t.id=tp_id AND (NOT t.is_private OR t.creator_id=u_id); - IF NOT FOUND THEN RAISE 'Invalid parent tag id'; END IF; - EXECUTE 'INSERT INTO autotags(child_id, parent_id, is_active) VALUES (' || - quote_literal(tc_id) || ',' || - quote_literal(tp_id) || ',' || - CASE WHEN is_active IS NULL THEN 'DEFAULT' ELSE quote_literal(is_active) END || - ') ON CONFLICT DO NOTHING RETURNING child_id, parent_id' INTO ct_id, pt_id; - IF ct_id IS NOT NULL AND coalesce(apply_to_existing, true) THEN - FOR f_id IN - SELECT ft.file_id FROM file_tag ft WHERE ft.tag_id=tc_id - LOOP - PERFORM tfm__add_file_to_tag_recursive(f_id, tp_id); - END LOOP; - END IF; - RETURN (ct_id IS NOT NULL); -END; -$$; - - -ALTER FUNCTION public.tfm_add_autotag(s_id uuid, tc_id uuid, tp_id uuid, is_active boolean, apply_to_existing boolean) OWNER TO hiko; - --- --- TOC entry 298 (class 1255 OID 17380) --- Name: tfm_add_category(uuid, character varying, character varying, character, boolean); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_add_category(s_id uuid, c_name character varying, c_notes character varying DEFAULT NULL::character varying, c_color character DEFAULT NULL::bpchar, c_is_private boolean DEFAULT NULL::boolean, OUT c_id uuid) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - EXECUTE 'INSERT INTO categories(name, notes, color, creator_id, is_private) VALUES(' || - quote_literal(c_name) || ',' || - CASE WHEN c_notes IS NULL THEN 'DEFAULT' ELSE quote_literal(c_notes) END || ',' || - CASE WHEN c_color IS NULL THEN 'DEFAULT' ELSE quote_literal(c_color) END || ',' || - quote_literal(u_id) || ',' || - CASE WHEN c_is_private IS NULL THEN 'DEFAULT' ELSE quote_literal(c_is_private) END || - ') RETURNING id' INTO c_id; -END; -$$; - - -ALTER FUNCTION public.tfm_add_category(s_id uuid, c_name character varying, c_notes character varying, c_color character, c_is_private boolean, OUT c_id uuid) OWNER TO hiko; - --- --- TOC entry 330 (class 1255 OID 120220) --- Name: tfm_add_file(uuid, character varying, timestamp with time zone, character varying, boolean, character varying, jsonb); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_add_file(s_id uuid, f_mime character varying, f_datetime timestamp with time zone DEFAULT NULL::timestamp with time zone, f_notes character varying DEFAULT NULL::character varying, f_is_private boolean DEFAULT NULL::boolean, f_orig_name character varying DEFAULT NULL::character varying, f_metadata jsonb DEFAULT NULL::jsonb, OUT f_id uuid, OUT ext character varying) RETURNS record - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; - m_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - SELECT m.id, m.extension FROM mime m WHERE m.name=f_mime INTO m_id, ext; - IF m_id IS NULL THEN RAISE 'Unsupported MIME: %', f_mime; END IF; - EXECUTE 'INSERT INTO files(mime_id, datetime, notes, creator_id, is_private, orig_name, metadata) VALUES(' || - quote_literal(m_id) || ',' || - CASE WHEN f_datetime IS NULL THEN 'DEFAULT' ELSE quote_literal(f_datetime) END || ',' || - CASE WHEN f_notes IS NULL THEN 'DEFAULT' ELSE quote_literal(f_notes) END || ',' || - quote_literal(u_id) || ',' || - CASE WHEN f_is_private IS NULL THEN 'DEFAULT' ELSE quote_literal(f_is_private) END || ',' || - CASE WHEN f_orig_name IS NULL THEN 'DEFAULT' ELSE quote_literal(f_orig_name) END || ',' || - CASE WHEN f_metadata IS NULL THEN 'DEFAULT' ELSE quote_literal(f_metadata) END || - ') RETURNING id' INTO f_id; -END; -$$; - - -ALTER FUNCTION public.tfm_add_file(s_id uuid, f_mime character varying, f_datetime timestamp with time zone, f_notes character varying, f_is_private boolean, f_orig_name character varying, f_metadata jsonb, OUT f_id uuid, OUT ext character varying) OWNER TO hiko; - --- --- TOC entry 318 (class 1255 OID 17527) --- Name: tfm_add_file_to_pool(uuid, uuid, uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_add_file_to_pool(s_id uuid, f_id uuid, p_id uuid) RETURNS boolean - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; - tmp uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - PERFORM FROM files f WHERE f.id=f_id AND (NOT f.is_private OR f.creator_id=u_id); - IF NOT FOUND THEN RAISE 'Invalid file id'; END IF; - PERFORM FROM pools p WHERE p.id=p_id AND (NOT p.is_private OR p.creator_id=u_id); - IF NOT FOUND THEN RAISE 'Invalid pool id'; END IF; - INSERT INTO file_pool VALUES (f_id, p_id) ON CONFLICT DO NOTHING RETURNING pool_id INTO tmp; - RETURN (tmp IS NOT NULL); -END; -$$; - - -ALTER FUNCTION public.tfm_add_file_to_pool(s_id uuid, f_id uuid, p_id uuid) OWNER TO hiko; - --- --- TOC entry 309 (class 1255 OID 17461) --- Name: tfm_add_file_to_tag(uuid, uuid, uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_add_file_to_tag(s_id uuid, f_id uuid, t_id uuid) RETURNS SETOF uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - PERFORM FROM files f WHERE f.id=f_id AND (NOT f.is_private OR f.creator_id=u_id); - IF NOT FOUND THEN RAISE 'Invalid file id'; END IF; - PERFORM FROM tags t WHERE t.id=t_id AND (NOT t.is_private OR t.creator_id=u_id); - IF NOT FOUND THEN RAISE 'Invalid tag id'; END IF; - RETURN QUERY SELECT tfm__add_file_to_tag_recursive(f_id, t_id); -END; -$$; - - -ALTER FUNCTION public.tfm_add_file_to_tag(s_id uuid, f_id uuid, t_id uuid) OWNER TO hiko; - --- --- TOC entry 301 (class 1255 OID 17397) --- Name: tfm_add_pool(uuid, character varying, character varying, uuid, boolean); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_add_pool(s_id uuid, p_name character varying, p_notes character varying DEFAULT NULL::character varying, p_parent_id uuid DEFAULT NULL::uuid, p_is_private boolean DEFAULT NULL::boolean, OUT p_id uuid) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - EXECUTE 'INSERT INTO pools(name, notes, parent_id, creator_id, is_private) VALUES(' || - quote_literal(p_name) || ',' || - CASE WHEN p_notes IS NULL THEN 'DEFAULT' ELSE quote_literal(p_notes) END || ',' || - CASE WHEN p_parent_id IS NULL THEN 'DEFAULT' ELSE quote_literal(p_parent_id) END || ',' || - quote_literal(u_id) || ',' || - CASE WHEN p_is_private IS NULL THEN 'DEFAULT' ELSE quote_literal(p_is_private) END || - ') RETURNING id' INTO p_id; -END; -$$; - - -ALTER FUNCTION public.tfm_add_pool(s_id uuid, p_name character varying, p_notes character varying, p_parent_id uuid, p_is_private boolean, OUT p_id uuid) OWNER TO hiko; - --- --- TOC entry 305 (class 1255 OID 17381) --- Name: tfm_add_tag(uuid, character varying, character varying, character, uuid, boolean); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_add_tag(s_id uuid, t_name character varying, t_notes character varying DEFAULT NULL::character varying, t_color character DEFAULT NULL::bpchar, t_category_id uuid DEFAULT NULL::uuid, t_is_private boolean DEFAULT NULL::boolean, OUT t_id uuid) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - EXECUTE 'INSERT INTO tags(name, notes, color, category_id, creator_id, is_private) VALUES(' || - quote_literal(t_name) || ',' || - CASE WHEN t_notes IS NULL THEN 'DEFAULT' ELSE quote_literal(t_notes) END || ',' || - CASE WHEN t_color IS NULL THEN 'DEFAULT' ELSE quote_literal(t_color) END || ',' || - CASE WHEN t_category_id IS NULL THEN 'DEFAULT' ELSE quote_literal(t_category_id) END || ',' || - quote_literal(u_id) || ',' || - CASE WHEN t_is_private IS NULL THEN 'DEFAULT' ELSE quote_literal(t_is_private) END || - ') RETURNING id' INTO t_id; -END; -$$; - - -ALTER FUNCTION public.tfm_add_tag(s_id uuid, t_name character varying, t_notes character varying, t_color character, t_category_id uuid, t_is_private boolean, OUT t_id uuid) OWNER TO hiko; - --- --- TOC entry 312 (class 1255 OID 17497) --- Name: tfm_edit_category(uuid, uuid, character varying, character varying, character, boolean); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_edit_category(IN s_id uuid, IN c_id uuid, IN c_name character varying DEFAULT NULL::character varying, IN c_notes character varying DEFAULT NULL::character varying, IN c_color character DEFAULT NULL::bpchar, IN c_is_private boolean DEFAULT NULL::boolean) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT c.is_private OR c.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM categories c WHERE c.id=c_id) - THEN RAISE 'Not allowed'; END IF; - UPDATE categories SET - name = coalesce(c_name, name), - notes = coalesce(c_notes, notes), - color = CASE WHEN c_color=''::character THEN NULL ELSE coalesce(c_color, color) END, - is_private = coalesce(c_is_private, is_private) - WHERE id=c_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_edit_category(IN s_id uuid, IN c_id uuid, IN c_name character varying, IN c_notes character varying, IN c_color character, IN c_is_private boolean) OWNER TO hiko; - --- --- TOC entry 319 (class 1255 OID 17500) --- Name: tfm_edit_file(uuid, uuid, character varying, timestamp with time zone, character varying, boolean); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_edit_file(IN s_id uuid, IN f_id uuid, IN f_mime_name character varying DEFAULT NULL::character varying, IN f_datetime timestamp with time zone DEFAULT NULL::timestamp with time zone, IN f_notes character varying DEFAULT NULL::character varying, IN f_is_private boolean DEFAULT NULL::boolean) - LANGUAGE plpgsql SECURITY DEFINER - AS $$DECLARE - u_id uuid; - m_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT f.is_private OR f.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM files f WHERE f.id=f_id) - THEN RAISE 'Not allowed'; END IF; - IF f_mime_name IS NOT NULL THEN - SELECT m.id FROM mime m WHERE m.name=f_mime_name INTO m_id; - IF m_id IS NULL THEN RAISE 'Unsupported MIME'; END IF; - END IF; - UPDATE files SET - mime_id = coalesce(m_id, mime_id), - datetime = coalesce(f_datetime, datetime), - notes = coalesce(f_notes, notes), - is_private = coalesce(f_is_private, is_private) - WHERE id=f_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_edit_file(IN s_id uuid, IN f_id uuid, IN f_mime_name character varying, IN f_datetime timestamp with time zone, IN f_notes character varying, IN f_is_private boolean) OWNER TO hiko; - --- --- TOC entry 311 (class 1255 OID 17501) --- Name: tfm_edit_pool(uuid, uuid, character varying, character varying, uuid, boolean); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_edit_pool(IN s_id uuid, IN p_id uuid, IN p_name character varying DEFAULT NULL::character varying, IN p_notes character varying DEFAULT NULL::character varying, IN p_parent_id uuid DEFAULT NULL::uuid, IN p_is_private boolean DEFAULT NULL::boolean) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT p.is_private OR p.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM pools p WHERE p.id=p_id) - THEN RAISE 'Not allowed'; END IF; - UPDATE pools p SET - p.name = coalesce(p_name, p.name), - p.notes = coalesce(p_notes, p.notes), - p.parent_id = CASE WHEN p_parent_id=uuid_nil() THEN NULL ELSE coalesce(p_parent_id, p.parent_id) END, - p.is_private = coalesce(p_is_private, p.is_private) - WHERE p.id=p_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_edit_pool(IN s_id uuid, IN p_id uuid, IN p_name character varying, IN p_notes character varying, IN p_parent_id uuid, IN p_is_private boolean) OWNER TO hiko; - --- --- TOC entry 320 (class 1255 OID 17502) --- Name: tfm_edit_tag(uuid, uuid, character varying, character varying, character, uuid, boolean); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_edit_tag(IN s_id uuid, IN t_id uuid, IN t_name character varying DEFAULT NULL::character varying, IN t_notes character varying DEFAULT NULL::character varying, IN t_color character DEFAULT NULL::bpchar, IN t_category_id uuid DEFAULT NULL::uuid, IN t_is_private boolean DEFAULT NULL::boolean) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT t.is_private OR t.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM tags t WHERE t.id=t_id) - THEN RAISE 'Not allowed'; END IF; - UPDATE tags SET - name = coalesce(t_name, name), - notes = coalesce(t_notes, notes), - color = CASE WHEN t_color=''::character THEN NULL ELSE coalesce(t_color, color) END, - category_id = CASE WHEN t_category_id=uuid_nil() THEN NULL ELSE coalesce(t_category_id, category_id) END, - is_private = coalesce(t_is_private, is_private) - WHERE id=t_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_edit_tag(IN s_id uuid, IN t_id uuid, IN t_name character varying, IN t_notes character varying, IN t_color character, IN t_category_id uuid, IN t_is_private boolean) OWNER TO hiko; - -SET default_tablespace = ''; - -SET default_table_access_method = heap; - --- --- TOC entry 220 (class 1259 OID 17064) --- Name: autotags; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.autotags ( - parent_id uuid NOT NULL, - child_id uuid NOT NULL, - is_active boolean DEFAULT true NOT NULL -); - - -ALTER TABLE public.autotags OWNER TO hiko; - --- --- TOC entry 228 (class 1259 OID 17474) --- Name: v_autotags; Type: VIEW; Schema: public; Owner: hiko --- - -CREATE VIEW public.v_autotags AS - SELECT a.child_id, - a.parent_id, - a.is_active - FROM public.autotags a; - - -ALTER VIEW public.v_autotags OWNER TO hiko; - --- --- TOC entry 307 (class 1255 OID 17478) --- Name: tfm_get_autotags(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_autotags(s_id uuid) RETURNS SETOF public.v_autotags - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; - u_is_admin boolean; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - SELECT u.is_admin FROM users u WHERE u.id=u_id INTO u_is_admin; - RETURN QUERY - SELECT a.* FROM v_autotags a - JOIN tags tc ON a.child_id=tc.id AND (NOT tc.is_private OR tc.creator_id=u_id OR u_is_admin) - JOIN tags tp ON a.parent_id=tp.id AND (NOT tp.is_private OR tp.creator_id=u_id OR u_is_admin); -END; -$$; - - -ALTER FUNCTION public.tfm_get_autotags(s_id uuid) OWNER TO hiko; - --- --- TOC entry 331 (class 1255 OID 139011) --- Name: uuid_v7(timestamp with time zone); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.uuid_v7(cts timestamp with time zone DEFAULT clock_timestamp()) RETURNS uuid - LANGUAGE plpgsql - AS $$ -DECLARE - state text = current_setting('uuidv7.old_tp',true); - old_tp text = split_part(state, ':',1); - base int = coalesce(nullif(split_part(state,':',4),'')::int,(random()*16777215/2-1)::int); - tp text; - entropy text; - seq text=base; - seqn int=split_part(state,':',2); - ver text = coalesce(split_part(state,':',3),to_hex(8+(random()*3)::int)); -BEGIN - base = (random()*16777215/2-1)::int; - tp = lpad(to_hex(floor(extract(epoch from cts)*1000)::int8),12,'0')||'7'; - if tp is distinct from old_tp then - old_tp = tp; - ver = to_hex(8+(random()*3)::int); - base = (random()*16777215/2-1)::int; - seqn = base; - else - seqn = seqn+(random()*1000)::int; - end if; - perform set_config('uuidv7.old_tp',old_tp||':'||seqn||':'||ver||':'||base, false); - entropy = md5(gen_random_uuid()::text); - seq = lpad(to_hex(seqn),6,'0'); - return (tp || substring(seq from 1 for 3) || ver || substring(seq from 4 for 3) || - substring(entropy from 1 for 12))::uuid; -END -$$; - - -ALTER FUNCTION public.uuid_v7(cts timestamp with time zone) OWNER TO hiko; - --- --- TOC entry 222 (class 1259 OID 17164) --- Name: categories; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.categories ( - id uuid DEFAULT public.uuid_v7() NOT NULL, - name character varying(256) NOT NULL, - notes character varying(1024) DEFAULT ''::character varying NOT NULL, - color character(6), - created timestamp with time zone DEFAULT statement_timestamp() NOT NULL, - creator_id uuid NOT NULL, - is_private boolean DEFAULT true NOT NULL -); - - -ALTER TABLE public.categories OWNER TO hiko; - --- --- TOC entry 213 (class 1259 OID 16523) --- Name: users; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.users ( - id uuid DEFAULT public.uuid_generate_v4() NOT NULL, - name character varying(32) NOT NULL, - password text NOT NULL, - is_admin boolean DEFAULT false NOT NULL, - can_edit boolean DEFAULT false NOT NULL -); - - -ALTER TABLE public.users OWNER TO hiko; - --- --- TOC entry 226 (class 1259 OID 17441) --- Name: v_categories; Type: VIEW; Schema: public; Owner: hiko --- - -CREATE VIEW public.v_categories AS - SELECT c.id, - c.name, - c.notes, - c.color, - c.created, - c.creator_id, - u.name AS creator_name, - c.is_private - FROM (public.categories c - JOIN public.users u ON ((c.creator_id = u.id))); - - -ALTER VIEW public.v_categories OWNER TO hiko; - --- --- TOC entry 299 (class 1255 OID 17456) --- Name: tfm_get_categories(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_categories(s_id uuid) RETURNS SETOF public.v_categories - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - RETURN QUERY - SELECT c.* FROM v_categories c - WHERE NOT c.is_private OR c.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_categories(s_id uuid) OWNER TO hiko; - --- --- TOC entry 328 (class 1255 OID 28865) --- Name: tfm_get_files(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_files(s_id uuid) RETURNS SETOF public.file - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - RETURN QUERY - SELECT - f.id, - f.mime_id, - m.name AS mime_name, - m.extension, - f.orig_name, - f.datetime, - f.notes, - f.created, - f.creator_id, - u.name AS creator_name, - f.is_private - FROM files f - JOIN mime m ON f.mime_id = m.id - JOIN users u ON f.creator_id = u.id - WHERE NOT f.is_private OR f.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_files(s_id uuid) OWNER TO hiko; - --- --- TOC entry 288 (class 1255 OID 17615) --- Name: tfm_get_files_1(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_files_1(s_id uuid) RETURNS SETOF record - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - RETURN QUERY - SELECT f.* FROM v_files f - WHERE NOT f.is_private OR f.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_files_1(s_id uuid) OWNER TO hiko; - --- --- TOC entry 329 (class 1255 OID 28866) --- Name: tfm_get_files_by_filter(uuid, character varying[]); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_files_by_filter(s_id uuid, filters character varying[] DEFAULT NULL::character varying[]) RETURNS SETOF public.file - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; - query_text text = 'SELECT * FROM (SELECT DISTINCT f.*, array_agg(ft.tag_id) OVER (PARTITION BY f.id) AS tags_list FROM tfm_get_files(' || quote_literal(s_id) || ') f ' || - 'LEFT JOIN file_tag ft ON ft.file_id=f.id) _f ' || - 'WHERE'; - _filter character varying; - tmp text; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - RAISE NOTICE '%', filters IS NULL; - IF filters IS NULL OR cardinality(filters) = 0 THEN - RETURN QUERY SELECT * FROM tfm_get_files(s_id); - RETURN; - END IF; - -- there was ',tags_list' in the end of columns to select - query_text := 'SELECT id,mime_id,mime_name,extension,orig_name,datetime,notes,created,creator_id,creator_name,is_private FROM (SELECT DISTINCT f.*, array_agg(ft.tag_id) OVER (PARTITION BY f.id) AS tags_list FROM tfm_get_files(' || quote_literal(s_id) || ') f ' || - 'LEFT JOIN file_tag ft ON ft.file_id=f.id) _f ' || - 'WHERE'; - FOREACH _filter IN ARRAY filters LOOP - IF _filter IN ('(', ')') THEN - query_text := query_text || _filter; - ELSIF _filter='&' THEN - query_text := query_text || ' AND'; - ELSIF _filter='|' THEN - query_text := query_text || ' OR'; - ELSIF _filter='!' THEN - query_text := query_text || ' NOT'; - ELSIF _filter='t=' || uuid_nil() THEN - query_text := query_text || ' (tags_list=array[NULL]::uuid[] OR ''d6d8129a-984d-4451-8c83-d04523ced8a8''=ANY(tags_list))'; - ELSIF _filter LIKE 't=%' THEN - query_text := query_text || ' ' || quote_literal(substring(_filter, 3)) || '=ANY(tags_list)'; - ELSIF _filter LIKE 'm=%' THEN - query_text := query_text || ' mime_id=' || quote_literal(substring(_filter, 3)); - ELSIF _filter LIKE 'm~%' THEN - query_text := query_text || ' mime_name LIKE ' || quote_literal(substring(_filter, 3)); - ELSE - RAISE 'Invalid condition'; - END IF; - END LOOP; - RETURN QUERY EXECUTE query_text; -END; -$$; - - -ALTER FUNCTION public.tfm_get_files_by_filter(s_id uuid, filters character varying[]) OWNER TO hiko; - --- --- TOC entry 223 (class 1259 OID 17186) --- Name: files; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.files ( - id uuid DEFAULT public.uuid_v7() NOT NULL, - mime_id uuid NOT NULL, - datetime timestamp with time zone DEFAULT statement_timestamp() NOT NULL, - notes character varying(1024) DEFAULT ''::character varying NOT NULL, - created timestamp with time zone DEFAULT statement_timestamp() NOT NULL, - creator_id uuid NOT NULL, - is_private boolean DEFAULT true NOT NULL, - orig_name character varying(256), - metadata jsonb -); - - -ALTER TABLE public.files OWNER TO hiko; - --- --- TOC entry 3603 (class 0 OID 0) --- Dependencies: 223 --- Name: COLUMN files.orig_name; Type: COMMENT; Schema: public; Owner: hiko --- - -COMMENT ON COLUMN public.files.orig_name IS 'Original filename'; - - --- --- TOC entry 215 (class 1259 OID 16543) --- Name: mime; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.mime ( - id uuid DEFAULT public.uuid_generate_v4() NOT NULL, - name character varying(127) NOT NULL, - extension character varying(16) NOT NULL -); - - -ALTER TABLE public.mime OWNER TO hiko; - --- --- TOC entry 230 (class 1259 OID 17670) --- Name: v_files; Type: VIEW; Schema: public; Owner: hiko --- - -CREATE VIEW public.v_files AS - SELECT f.id, - f.mime_id, - m.name AS mime_name, - m.extension, - f.datetime, - f.notes, - f.created, - f.creator_id, - u.name AS creator_name, - f.is_private - FROM ((public.files f - JOIN public.mime m ON ((f.mime_id = m.id))) - JOIN public.users u ON ((f.creator_id = u.id))); - - -ALTER VIEW public.v_files OWNER TO hiko; - --- --- TOC entry 324 (class 1255 OID 17676) --- Name: tfm_get_files_by_pool(uuid, uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_files_by_pool(s_id uuid, p_id uuid) RETURNS SETOF public.v_files - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; - u_is_admin boolean; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - SELECT u.is_admin FROM users u WHERE u.id=u_id INTO u_is_admin; - PERFORM FROM pools p WHERE p.id=p_id AND (NOT p.is_private OR p.creator_id=u_id OR u_is_admin); - IF NOT FOUND THEN RAISE 'Invalid pool id'; END IF; - RETURN QUERY - SELECT f.* FROM v_files f - JOIN file_pool fp ON f.id=fp.file_id AND fp.pool_id=p_id - WHERE NOT f.is_private OR f.creator_id=u_id OR u_is_admin; -END; -$$; - - -ALTER FUNCTION public.tfm_get_files_by_pool(s_id uuid, p_id uuid) OWNER TO hiko; - --- --- TOC entry 325 (class 1255 OID 17677) --- Name: tfm_get_files_by_tag(uuid, uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_files_by_tag(s_id uuid, t_id uuid) RETURNS SETOF public.v_files - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - PERFORM FROM tags t WHERE t.id=t_id; - IF NOT FOUND THEN RAISE 'Invalid tag id'; END IF; - RETURN QUERY - SELECT f.* FROM v_files f - JOIN file_tag ft ON f.id=ft.file_id AND ft.tag_id=t_id - WHERE NOT f.is_private OR f.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_files_by_tag(s_id uuid, t_id uuid) OWNER TO hiko; - --- --- TOC entry 317 (class 1255 OID 17517) --- Name: tfm_get_my_file_views(uuid, uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_my_file_views(s_id uuid, f_id uuid DEFAULT NULL::uuid) RETURNS TABLE(file_id uuid, datetime timestamp with time zone) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - RETURN QUERY - SELECT fv.file_id, fv.datetime FROM file_views fv - WHERE fv.user_id=u_id AND (f_id IS NULL OR fv.file_id=f_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_my_file_views(s_id uuid, f_id uuid) OWNER TO hiko; - --- --- TOC entry 214 (class 1259 OID 16534) --- Name: sessions; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.sessions ( - id uuid DEFAULT public.uuid_generate_v4() NOT NULL, - user_id uuid NOT NULL, - user_agent_id uuid NOT NULL, - started timestamp with time zone DEFAULT statement_timestamp() NOT NULL, - expires timestamp with time zone, - last_seen timestamp with time zone -); - - -ALTER TABLE public.sessions OWNER TO hiko; - --- --- TOC entry 218 (class 1259 OID 16820) --- Name: user_agents; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.user_agents ( - id uuid DEFAULT public.uuid_generate_v4() NOT NULL, - name character varying(64) NOT NULL -); - - -ALTER TABLE public.user_agents OWNER TO hiko; - --- --- TOC entry 229 (class 1259 OID 17508) --- Name: v_sessions; Type: VIEW; Schema: public; Owner: hiko --- - -CREATE VIEW public.v_sessions AS - SELECT s.id, - s.user_id, - u.name AS user_name, - s.user_agent_id, - ua.name AS user_agent_name, - s.started, - s.expires, - s.last_seen - FROM ((public.sessions s - JOIN public.users u ON ((s.user_id = u.id))) - JOIN public.user_agents ua ON ((s.user_agent_id = ua.id))); - - -ALTER VIEW public.v_sessions OWNER TO hiko; - --- --- TOC entry 300 (class 1255 OID 17512) --- Name: tfm_get_my_sessions(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_my_sessions(s_id uuid) RETURNS SETOF public.v_sessions - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - RETURN QUERY - SELECT s.* FROM v_sessions s - WHERE s.user_id=u_id; -END; -$$; - - -ALTER FUNCTION public.tfm_get_my_sessions(s_id uuid) OWNER TO hiko; - --- --- TOC entry 221 (class 1259 OID 17125) --- Name: tags; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.tags ( - id uuid DEFAULT public.uuid_v7() NOT NULL, - name character varying(256) NOT NULL, - notes character varying(1024) DEFAULT ''::character varying NOT NULL, - color character(6), - category_id uuid, - created timestamp with time zone DEFAULT statement_timestamp() NOT NULL, - creator_id uuid NOT NULL, - is_private boolean DEFAULT true NOT NULL -); - - -ALTER TABLE public.tags OWNER TO hiko; - --- --- TOC entry 225 (class 1259 OID 17432) --- Name: v_tags; Type: VIEW; Schema: public; Owner: hiko --- - -CREATE VIEW public.v_tags AS - SELECT t.id, - t.name, - t.notes, - t.color, - t.category_id, - c.name AS category_name, - c.color AS category_color, - t.created, - t.creator_id, - u.name AS creator_name, - t.is_private - FROM ((public.tags t - LEFT JOIN public.categories c ON ((t.category_id = c.id))) - JOIN public.users u ON ((t.creator_id = u.id))); - - -ALTER VIEW public.v_tags OWNER TO hiko; - --- --- TOC entry 321 (class 1255 OID 17538) --- Name: tfm_get_parent_tags(uuid, uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_parent_tags(s_id uuid, t_id uuid) RETURNS SETOF public.v_tags - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - PERFORM FROM tags t WHERE t.id=t_id; - IF NOT FOUND THEN RAISE 'Invalid tag id'; END IF; - RETURN QUERY - SELECT t.* FROM v_tags t - JOIN autotags a ON t.id=a.parent_id AND a.child_id=t_id - WHERE NOT t.is_private OR t.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_parent_tags(s_id uuid, t_id uuid) OWNER TO hiko; - --- --- TOC entry 224 (class 1259 OID 17314) --- Name: pools; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.pools ( - id uuid DEFAULT public.uuid_generate_v4() NOT NULL, - name character varying(256) NOT NULL, - notes character varying(1024) DEFAULT ''::character varying NOT NULL, - created timestamp with time zone DEFAULT statement_timestamp() NOT NULL, - parent_id uuid, - creator_id uuid NOT NULL, - is_private boolean DEFAULT true NOT NULL -); - - -ALTER TABLE public.pools OWNER TO hiko; - --- --- TOC entry 227 (class 1259 OID 17445) --- Name: v_pools; Type: VIEW; Schema: public; Owner: hiko --- - -CREATE VIEW public.v_pools AS - SELECT p.id, - p.name, - p.notes, - p.created, - p.parent_id, - pp.name AS parent_name, - p.creator_id, - u.name AS creator_name, - p.is_private - FROM ((public.pools p - LEFT JOIN public.pools pp ON ((p.parent_id = pp.id))) - JOIN public.users u ON ((p.creator_id = u.id))); - - -ALTER VIEW public.v_pools OWNER TO hiko; - --- --- TOC entry 303 (class 1255 OID 17514) --- Name: tfm_get_pools(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_pools(s_id uuid) RETURNS SETOF public.v_pools - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - RETURN QUERY - SELECT p.* FROM v_pools p - WHERE NOT p.is_private OR p.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_pools(s_id uuid) OWNER TO hiko; - --- --- TOC entry 304 (class 1255 OID 17515) --- Name: tfm_get_tags(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_tags(s_id uuid) RETURNS SETOF public.v_tags - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - RETURN QUERY - SELECT t.* FROM v_tags t - WHERE NOT t.is_private OR t.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_tags(s_id uuid) OWNER TO hiko; - --- --- TOC entry 316 (class 1255 OID 17516) --- Name: tfm_get_tags_by_file(uuid, uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_get_tags_by_file(s_id uuid, f_id uuid) RETURNS SETOF public.v_tags - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - PERFORM FROM files f WHERE f.id=f_id; - IF NOT FOUND THEN RAISE 'Invalid file id'; END IF; - RETURN QUERY - SELECT t.* FROM v_tags t - JOIN file_tag ft ON t.id=ft.tag_id AND ft.file_id=f_id - WHERE NOT t.is_private OR t.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id); -END; -$$; - - -ALTER FUNCTION public.tfm_get_tags_by_file(s_id uuid, f_id uuid) OWNER TO hiko; - --- --- TOC entry 294 (class 1255 OID 17484) --- Name: tfm_remove_autotag(uuid, uuid, uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_remove_autotag(IN s_id uuid, IN tc_id uuid, IN tp_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - DELETE FROM autotags a WHERE a.child_id=tc_id AND a.parent_id=tp_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_remove_autotag(IN s_id uuid, IN tc_id uuid, IN tp_id uuid) OWNER TO hiko; - --- --- TOC entry 296 (class 1255 OID 17481) --- Name: tfm_remove_category(uuid, uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_remove_category(IN s_id uuid, IN c_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT c.is_private OR c.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM categories c WHERE c.id=c_id) - THEN RAISE 'Not allowed'; END IF; - DELETE FROM categories c WHERE c.id=c_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_remove_category(IN s_id uuid, IN c_id uuid) OWNER TO hiko; - --- --- TOC entry 310 (class 1255 OID 17479) --- Name: tfm_remove_file(uuid, uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_remove_file(IN s_id uuid, IN f_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT f.is_private OR f.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM files f WHERE f.id=f_id) - THEN RAISE 'Not allowed'; END IF; - DELETE FROM files f WHERE f.id=f_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_remove_file(IN s_id uuid, IN f_id uuid) OWNER TO hiko; - --- --- TOC entry 308 (class 1255 OID 17485) --- Name: tfm_remove_file_to_pool(uuid, uuid, uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_remove_file_to_pool(IN s_id uuid, IN f_id uuid, IN p_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT p.is_private OR p.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM pools p WHERE p.id=p_id) - THEN RAISE 'Not allowed'; END IF; - DELETE FROM file_pool fp WHERE fp.file_id=f_id AND fp.pool_id=p_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_remove_file_to_pool(IN s_id uuid, IN f_id uuid, IN p_id uuid) OWNER TO hiko; - --- --- TOC entry 293 (class 1255 OID 17483) --- Name: tfm_remove_file_to_tag(uuid, uuid, uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_remove_file_to_tag(IN s_id uuid, IN f_id uuid, IN t_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) THEN RAISE 'Not allowed'; END IF; - DELETE FROM file_tag ft WHERE ft.file_id=f_id AND ft.tag_id=t_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_remove_file_to_tag(IN s_id uuid, IN f_id uuid, IN t_id uuid) OWNER TO hiko; - --- --- TOC entry 297 (class 1255 OID 17482) --- Name: tfm_remove_pool(uuid, uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_remove_pool(IN s_id uuid, IN p_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT p.is_private OR p.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM pools p WHERE p.id=p_id) - THEN RAISE 'Not allowed'; END IF; - DELETE FROM pools p WHERE p.id=p_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_remove_pool(IN s_id uuid, IN p_id uuid) OWNER TO hiko; - --- --- TOC entry 295 (class 1255 OID 17480) --- Name: tfm_remove_tag(uuid, uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_remove_tag(IN s_id uuid, IN t_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - IF - NOT (SELECT u.can_edit FROM users u WHERE u.id=u_id) - OR - NOT (SELECT (NOT t.is_private OR t.creator_id=u_id OR (SELECT u.is_admin FROM users u WHERE u.id=u_id)) FROM tags t WHERE t.id=t_id) - THEN RAISE 'Not allowed'; END IF; - DELETE FROM tags t WHERE t.id=t_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_remove_tag(IN s_id uuid, IN t_id uuid) OWNER TO hiko; - --- --- TOC entry 291 (class 1255 OID 16848) --- Name: tfm_session_request(uuid, text); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_session_request(u_id uuid, u_agent text, OUT s_id uuid) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - ua_id UUID; -BEGIN - PERFORM FROM users WHERE id=u_id; - IF NOT FOUND THEN RAISE 'User not exists'; END IF; - SELECT id FROM user_agents WHERE name=u_agent INTO ua_id; - IF NOT FOUND THEN RAISE 'Unsupported user agent'; END IF; - INSERT INTO sessions(user_id, user_agent_id) VALUES(u_id, ua_id) RETURNING id INTO s_id; -END; -$$; - - -ALTER FUNCTION public.tfm_session_request(u_id uuid, u_agent text, OUT s_id uuid) OWNER TO hiko; - --- --- TOC entry 313 (class 1255 OID 17503) --- Name: tfm_session_terminate(uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_session_terminate(IN s_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE - u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - DELETE FROM sessions s WHERE s.id=s_id; -END; -$$; - - -ALTER PROCEDURE public.tfm_session_terminate(IN s_id uuid) OWNER TO hiko; - --- --- TOC entry 314 (class 1255 OID 17504) --- Name: tfm_session_username(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_session_username(s_id uuid) RETURNS character varying - LANGUAGE sql SECURITY DEFINER - AS $$ -SELECT u.name FROM users u JOIN sessions s ON s.user_id=u.id; -$$; - - -ALTER FUNCTION public.tfm_session_username(s_id uuid) OWNER TO hiko; - --- --- TOC entry 326 (class 1255 OID 26730) --- Name: tfm_session_validate(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_session_validate(s_id uuid) RETURNS uuid - LANGUAGE sql SECURITY DEFINER PARALLEL SAFE - AS $$ -UPDATE sessions SET last_seen=statement_timestamp() WHERE id=s_id RETURNING user_id; -$$; - - -ALTER FUNCTION public.tfm_session_validate(s_id uuid) OWNER TO hiko; - --- --- TOC entry 292 (class 1255 OID 16801) --- Name: tfm_user_auth(character varying, text); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_user_auth(u_name character varying, u_password text) RETURNS uuid - LANGUAGE plpgsql STABLE SECURITY DEFINER - AS $$ -DECLARE - selected_user users%ROWTYPE; -BEGIN -SELECT * FROM users -WHERE users.name=u_name -INTO selected_user; -IF selected_user.password=crypt(u_password, selected_user.password) THEN - RETURN selected_user.id; -END IF; -RAISE 'Authorization failed'; -END; -$$; - - -ALTER FUNCTION public.tfm_user_auth(u_name character varying, u_password text) OWNER TO hiko; - --- --- TOC entry 3615 (class 0 OID 0) --- Dependencies: 292 --- Name: FUNCTION tfm_user_auth(u_name character varying, u_password text); Type: COMMENT; Schema: public; Owner: hiko --- - -COMMENT ON FUNCTION public.tfm_user_auth(u_name character varying, u_password text) IS 'Returns user UUID if username and password are valid otherwise nil UUID.'; - - --- --- TOC entry 306 (class 1255 OID 17451) --- Name: tfm_user_create(text, text, boolean, boolean); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_user_create(u_name text, u_password text, u_is_admin boolean DEFAULT false, u_can_edit boolean DEFAULT false, OUT u_id uuid) RETURNS uuid - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -BEGIN - INSERT INTO users(name, password, is_admin, can_edit) VALUES(u_name, crypt(u_password, gen_salt('bf')), u_is_admin, u_can_edit) RETURNING id INTO u_id; -END; -$$; - - -ALTER FUNCTION public.tfm_user_create(u_name text, u_password text, u_is_admin boolean, u_can_edit boolean, OUT u_id uuid) OWNER TO hiko; - --- --- TOC entry 327 (class 1255 OID 27477) --- Name: tfm_user_get_info(uuid); Type: FUNCTION; Schema: public; Owner: hiko --- - -CREATE FUNCTION public.tfm_user_get_info(s_id uuid, OUT user_info public.users) RETURNS public.users - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - SELECT * FROM users WHERE id=u_id INTO user_info; -END; -$$; - - -ALTER FUNCTION public.tfm_user_get_info(s_id uuid, OUT user_info public.users) OWNER TO hiko; - --- --- TOC entry 302 (class 1255 OID 17420) --- Name: tfm_view_file(uuid, uuid); Type: PROCEDURE; Schema: public; Owner: hiko --- - -CREATE PROCEDURE public.tfm_view_file(IN s_id uuid, IN f_id uuid) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -DECLARE u_id uuid; -BEGIN - SELECT tfm_session_validate(s_id) INTO u_id; - IF u_id IS NULL THEN RAISE 'Invalid session id'; END IF; - PERFORM FROM files f WHERE f.id=f_id AND (NOT f.is_private OR f.creator_id=u_id); - IF NOT FOUND THEN RAISE 'Invalid file id'; END IF; - INSERT INTO file_views(file_id, user_id) VALUES(f_id, u_id); -END; -$$; - - -ALTER PROCEDURE public.tfm_view_file(IN s_id uuid, IN f_id uuid) OWNER TO hiko; - --- --- TOC entry 232 (class 1259 OID 34878) --- Name: acl; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.acl ( - user_id uuid NOT NULL, - object_id uuid NOT NULL, - read boolean DEFAULT true NOT NULL, - write boolean DEFAULT false NOT NULL -); - - -ALTER TABLE public.acl OWNER TO hiko; - --- --- TOC entry 217 (class 1259 OID 16610) --- Name: file_pool; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.file_pool ( - file_id uuid NOT NULL, - pool_id uuid NOT NULL -); - - -ALTER TABLE public.file_pool OWNER TO hiko; - --- --- TOC entry 216 (class 1259 OID 16605) --- Name: file_tag; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.file_tag ( - file_id uuid NOT NULL, - tag_id uuid NOT NULL -); - - -ALTER TABLE public.file_tag OWNER TO hiko; - --- --- TOC entry 219 (class 1259 OID 17032) --- Name: file_views; Type: TABLE; Schema: public; Owner: hiko --- - -CREATE TABLE public.file_views ( - file_id uuid NOT NULL, - datetime timestamp with time zone DEFAULT statement_timestamp() NOT NULL, - user_id uuid NOT NULL -); - - -ALTER TABLE public.file_views OWNER TO hiko; - --- --- TOC entry 3366 (class 2606 OID 17357) --- Name: categories chk__categories__color__hex; Type: CHECK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE public.categories - ADD CONSTRAINT chk__categories__color__hex CHECK (((color IS NULL) OR (color ~* '^[A-Fa-f0-9]{6}$'::text))) NOT VALID; - - --- --- TOC entry 3621 (class 0 OID 0) --- Dependencies: 3366 --- Name: CONSTRAINT chk__categories__color__hex ON categories; Type: COMMENT; Schema: public; Owner: hiko --- - -COMMENT ON CONSTRAINT chk__categories__color__hex ON public.categories IS 'Check if `color` is a valid HEX color'; - - --- --- TOC entry 3365 (class 2606 OID 17356) --- Name: tags chk__tags___color__hex; Type: CHECK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE public.tags - ADD CONSTRAINT chk__tags___color__hex CHECK (((color IS NULL) OR (color ~* '^[A-Fa-f0-9]{6}$'::text))) NOT VALID; - - --- --- TOC entry 3622 (class 0 OID 0) --- Dependencies: 3365 --- Name: CONSTRAINT chk__tags___color__hex ON tags; Type: COMMENT; Schema: public; Owner: hiko --- - -COMMENT ON CONSTRAINT chk__tags___color__hex ON public.tags IS 'Check if `color` is a valid HEX color'; - - --- --- TOC entry 3426 (class 2606 OID 34884) --- Name: acl prm__acl; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.acl - ADD CONSTRAINT prm__acl PRIMARY KEY (user_id, object_id); - - --- --- TOC entry 3399 (class 2606 OID 17070) --- Name: autotags prm__autotags; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.autotags - ADD CONSTRAINT prm__autotags PRIMARY KEY (child_id, parent_id); - - --- --- TOC entry 3410 (class 2606 OID 17173) --- Name: categories prm__categories; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.categories - ADD CONSTRAINT prm__categories PRIMARY KEY (id); - - --- --- TOC entry 3386 (class 2606 OID 16614) --- Name: file_pool prm__file_pool; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_pool - ADD CONSTRAINT prm__file_pool PRIMARY KEY (file_id, pool_id); - - --- --- TOC entry 3382 (class 2606 OID 16609) --- Name: file_tag prm__file_tag; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_tag - ADD CONSTRAINT prm__file_tag PRIMARY KEY (file_id, tag_id); - - --- --- TOC entry 3395 (class 2606 OID 17037) --- Name: file_views prm__file_views; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_views - ADD CONSTRAINT prm__file_views PRIMARY KEY (file_id, datetime, user_id); - - --- --- TOC entry 3418 (class 2606 OID 17196) --- Name: files prm__files; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.files - ADD CONSTRAINT prm__files PRIMARY KEY (id); - - --- --- TOC entry 3376 (class 2606 OID 16548) --- Name: mime prm__mime; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.mime - ADD CONSTRAINT prm__mime PRIMARY KEY (id); - - --- --- TOC entry 3422 (class 2606 OID 17323) --- Name: pools prm__pools; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.pools - ADD CONSTRAINT prm__pools PRIMARY KEY (id); - - --- --- TOC entry 3374 (class 2606 OID 16542) --- Name: sessions prm__sessions; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.sessions - ADD CONSTRAINT prm__sessions PRIMARY KEY (id); - - --- --- TOC entry 3404 (class 2606 OID 17135) --- Name: tags prm__tags; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.tags - ADD CONSTRAINT prm__tags PRIMARY KEY (id); - - --- --- TOC entry 3388 (class 2606 OID 16824) --- Name: user_agents prm__user_agents; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.user_agents - ADD CONSTRAINT prm__user_agents PRIMARY KEY (id); - - --- --- TOC entry 3368 (class 2606 OID 16531) --- Name: users prm__users; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.users - ADD CONSTRAINT prm__users PRIMARY KEY (id); - - --- --- TOC entry 3412 (class 2606 OID 17175) --- Name: categories uni__categories__name; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.categories - ADD CONSTRAINT uni__categories__name UNIQUE (name); - - --- --- TOC entry 3378 (class 2606 OID 16550) --- Name: mime uni__mime__name; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.mime - ADD CONSTRAINT uni__mime__name UNIQUE (name); - - --- --- TOC entry 3424 (class 2606 OID 17325) --- Name: pools uni__pools__name; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.pools - ADD CONSTRAINT uni__pools__name UNIQUE (name); - - --- --- TOC entry 3406 (class 2606 OID 17137) --- Name: tags uni__tags__name; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.tags - ADD CONSTRAINT uni__tags__name UNIQUE (name); - - --- --- TOC entry 3390 (class 2606 OID 16826) --- Name: user_agents uni__user_agents__name; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.user_agents - ADD CONSTRAINT uni__user_agents__name UNIQUE (name); - - --- --- TOC entry 3370 (class 2606 OID 16533) --- Name: users uni__users__name; Type: CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.users - ADD CONSTRAINT uni__users__name UNIQUE (name); - - --- --- TOC entry 3396 (class 1259 OID 27375) --- Name: idx__autotags__child_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__autotags__child_id ON public.autotags USING hash (child_id); - - --- --- TOC entry 3397 (class 1259 OID 27376) --- Name: idx__autotags__parent_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__autotags__parent_id ON public.autotags USING hash (parent_id); - - --- --- TOC entry 3407 (class 1259 OID 27389) --- Name: idx__categories__created; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__categories__created ON public.categories USING btree (created DESC NULLS LAST); - - --- --- TOC entry 3408 (class 1259 OID 27390) --- Name: idx__categories__creator_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__categories__creator_id ON public.categories USING hash (creator_id); - - --- --- TOC entry 3383 (class 1259 OID 27379) --- Name: idx__file_pool__file_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__file_pool__file_id ON public.file_pool USING hash (file_id); - - --- --- TOC entry 3384 (class 1259 OID 27380) --- Name: idx__file_pool__pool_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__file_pool__pool_id ON public.file_pool USING hash (pool_id); - - --- --- TOC entry 3379 (class 1259 OID 27377) --- Name: idx__file_tag__file_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__file_tag__file_id ON public.file_tag USING hash (file_id); - - --- --- TOC entry 3380 (class 1259 OID 27378) --- Name: idx__file_tag__tag_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__file_tag__tag_id ON public.file_tag USING hash (tag_id); - - --- --- TOC entry 3391 (class 1259 OID 27382) --- Name: idx__file_views__datetime; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__file_views__datetime ON public.file_views USING btree (datetime DESC NULLS LAST); - - --- --- TOC entry 3392 (class 1259 OID 27381) --- Name: idx__file_views__file_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__file_views__file_id ON public.file_views USING hash (file_id); - - --- --- TOC entry 3393 (class 1259 OID 27391) --- Name: idx__file_views__user_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__file_views__user_id ON public.file_views USING hash (user_id); - - --- --- TOC entry 3413 (class 1259 OID 27385) --- Name: idx__files__created; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__files__created ON public.files USING btree (created DESC NULLS LAST); - - --- --- TOC entry 3414 (class 1259 OID 27392) --- Name: idx__files__creator_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__files__creator_id ON public.files USING hash (creator_id); - - --- --- TOC entry 3415 (class 1259 OID 27384) --- Name: idx__files__datetime; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__files__datetime ON public.files USING btree (datetime DESC NULLS LAST); - - --- --- TOC entry 3416 (class 1259 OID 27383) --- Name: idx__files__mime_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__files__mime_id ON public.files USING hash (mime_id); - - --- --- TOC entry 3419 (class 1259 OID 27386) --- Name: idx__pools__created; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__pools__created ON public.pools USING btree (created DESC NULLS LAST); - - --- --- TOC entry 3420 (class 1259 OID 27393) --- Name: idx__pools__creator_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__pools__creator_id ON public.pools USING hash (creator_id); - - --- --- TOC entry 3371 (class 1259 OID 63917) --- Name: idx__sessions__user_agent_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__sessions__user_agent_id ON public.sessions USING hash (user_agent_id); - - --- --- TOC entry 3372 (class 1259 OID 63916) --- Name: idx__sessions__user_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__sessions__user_id ON public.sessions USING hash (user_id); - - --- --- TOC entry 3400 (class 1259 OID 27388) --- Name: idx__tags__category_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__tags__category_id ON public.tags USING hash (category_id); - - --- --- TOC entry 3401 (class 1259 OID 27387) --- Name: idx__tags__created; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__tags__created ON public.tags USING btree (created DESC NULLS LAST); - - --- --- TOC entry 3402 (class 1259 OID 63915) --- Name: idx__tags__creator_id; Type: INDEX; Schema: public; Owner: hiko --- - -CREATE INDEX idx__tags__creator_id ON public.tags USING hash (creator_id); - - --- --- TOC entry 3444 (class 2606 OID 34885) --- Name: acl frn__acl__user_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.acl - ADD CONSTRAINT frn__acl__user_id FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3435 (class 2606 OID 17159) --- Name: autotags frn__autotags__child_id_; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.autotags - ADD CONSTRAINT frn__autotags__child_id_ FOREIGN KEY (child_id) REFERENCES public.tags(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3436 (class 2606 OID 17154) --- Name: autotags frn__autotags__parent_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.autotags - ADD CONSTRAINT frn__autotags__parent_id FOREIGN KEY (parent_id) REFERENCES public.tags(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3439 (class 2606 OID 17176) --- Name: categories frn__categories__creator_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.categories - ADD CONSTRAINT frn__categories__creator_id FOREIGN KEY (creator_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE RESTRICT; - - --- --- TOC entry 3431 (class 2606 OID 17222) --- Name: file_pool frn__file_pool__file_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_pool - ADD CONSTRAINT frn__file_pool__file_id FOREIGN KEY (file_id) REFERENCES public.files(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3432 (class 2606 OID 17336) --- Name: file_pool frn__file_pool__pool_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_pool - ADD CONSTRAINT frn__file_pool__pool_id FOREIGN KEY (pool_id) REFERENCES public.pools(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3429 (class 2606 OID 17212) --- Name: file_tag frn__file_tag__file_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_tag - ADD CONSTRAINT frn__file_tag__file_id FOREIGN KEY (file_id) REFERENCES public.files(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3430 (class 2606 OID 17149) --- Name: file_tag frn__file_tag__tag_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_tag - ADD CONSTRAINT frn__file_tag__tag_id FOREIGN KEY (tag_id) REFERENCES public.tags(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3433 (class 2606 OID 17217) --- Name: file_views frn__file_views__file_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_views - ADD CONSTRAINT frn__file_views__file_id FOREIGN KEY (file_id) REFERENCES public.files(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3434 (class 2606 OID 17043) --- Name: file_views frn__file_views__user_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.file_views - ADD CONSTRAINT frn__file_views__user_id FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3440 (class 2606 OID 17389) --- Name: files frn__files__creator_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.files - ADD CONSTRAINT frn__files__creator_id FOREIGN KEY (creator_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE RESTRICT; - - --- --- TOC entry 3441 (class 2606 OID 17202) --- Name: files frn__files__mime_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.files - ADD CONSTRAINT frn__files__mime_id FOREIGN KEY (mime_id) REFERENCES public.mime(id) ON UPDATE CASCADE ON DELETE RESTRICT; - - --- --- TOC entry 3442 (class 2606 OID 17326) --- Name: pools frn__pools__creator_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.pools - ADD CONSTRAINT frn__pools__creator_id FOREIGN KEY (creator_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE RESTRICT; - - --- --- TOC entry 3443 (class 2606 OID 17331) --- Name: pools frn__pools__parent_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.pools - ADD CONSTRAINT frn__pools__parent_id FOREIGN KEY (parent_id) REFERENCES public.pools(id) ON UPDATE CASCADE ON DELETE SET NULL; - - --- --- TOC entry 3427 (class 2606 OID 17001) --- Name: sessions frn__sessions__user_agent_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.sessions - ADD CONSTRAINT frn__sessions__user_agent_id FOREIGN KEY (user_agent_id) REFERENCES public.user_agents(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3428 (class 2606 OID 17006) --- Name: sessions frn__sessions__user_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.sessions - ADD CONSTRAINT frn__sessions__user_id FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE; - - --- --- TOC entry 3437 (class 2606 OID 17181) --- Name: tags frn__tags__category_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.tags - ADD CONSTRAINT frn__tags__category_id FOREIGN KEY (category_id) REFERENCES public.categories(id) ON UPDATE CASCADE ON DELETE SET NULL; - - --- --- TOC entry 3438 (class 2606 OID 17143) --- Name: tags frn__tags__creator_id; Type: FK CONSTRAINT; Schema: public; Owner: hiko --- - -ALTER TABLE ONLY public.tags - ADD CONSTRAINT frn__tags__creator_id FOREIGN KEY (creator_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE RESTRICT; - - --- --- TOC entry 3595 (class 0 OID 0) --- Dependencies: 7 --- Name: SCHEMA public; Type: ACL; Schema: -; Owner: hiko --- - -REVOKE USAGE ON SCHEMA public FROM PUBLIC; -GRANT ALL ON SCHEMA public TO PUBLIC; - - --- --- TOC entry 3598 (class 0 OID 0) --- Dependencies: 220 --- Name: TABLE autotags; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.autotags FROM hiko; -GRANT SELECT ON TABLE public.autotags TO grafana; - - --- --- TOC entry 3599 (class 0 OID 0) --- Dependencies: 228 --- Name: TABLE v_autotags; Type: ACL; Schema: public; Owner: hiko --- - -GRANT SELECT ON TABLE public.v_autotags TO grafana; - - --- --- TOC entry 3600 (class 0 OID 0) --- Dependencies: 222 --- Name: TABLE categories; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.categories FROM hiko; -GRANT SELECT ON TABLE public.categories TO grafana; - - --- --- TOC entry 3601 (class 0 OID 0) --- Dependencies: 213 --- Name: TABLE users; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.users FROM hiko; -GRANT SELECT ON TABLE public.users TO grafana; - - --- --- TOC entry 3602 (class 0 OID 0) --- Dependencies: 226 --- Name: TABLE v_categories; Type: ACL; Schema: public; Owner: hiko --- - -GRANT SELECT ON TABLE public.v_categories TO grafana; - - --- --- TOC entry 3604 (class 0 OID 0) --- Dependencies: 223 --- Name: TABLE files; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.files FROM hiko; -GRANT SELECT ON TABLE public.files TO grafana; - - --- --- TOC entry 3605 (class 0 OID 0) --- Dependencies: 215 --- Name: TABLE mime; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mime FROM hiko; -GRANT SELECT ON TABLE public.mime TO tanabata; -GRANT SELECT ON TABLE public.mime TO grafana; - - --- --- TOC entry 3606 (class 0 OID 0) --- Dependencies: 230 --- Name: TABLE v_files; Type: ACL; Schema: public; Owner: hiko --- - -GRANT SELECT ON TABLE public.v_files TO grafana; - - --- --- TOC entry 3607 (class 0 OID 0) --- Dependencies: 214 --- Name: TABLE sessions; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.sessions FROM hiko; -GRANT SELECT ON TABLE public.sessions TO grafana; - - --- --- TOC entry 3608 (class 0 OID 0) --- Dependencies: 218 --- Name: TABLE user_agents; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.user_agents FROM hiko; -GRANT SELECT ON TABLE public.user_agents TO tanabata; -GRANT SELECT ON TABLE public.user_agents TO grafana; - - --- --- TOC entry 3609 (class 0 OID 0) --- Dependencies: 229 --- Name: TABLE v_sessions; Type: ACL; Schema: public; Owner: hiko --- - -GRANT SELECT ON TABLE public.v_sessions TO grafana; - - --- --- TOC entry 3610 (class 0 OID 0) --- Dependencies: 221 --- Name: TABLE tags; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.tags FROM hiko; -GRANT SELECT ON TABLE public.tags TO grafana; - - --- --- TOC entry 3611 (class 0 OID 0) --- Dependencies: 225 --- Name: TABLE v_tags; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.v_tags FROM hiko; -GRANT SELECT ON TABLE public.v_tags TO grafana; - - --- --- TOC entry 3612 (class 0 OID 0) --- Dependencies: 224 --- Name: TABLE pools; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pools FROM hiko; -GRANT SELECT ON TABLE public.pools TO grafana; - - --- --- TOC entry 3613 (class 0 OID 0) --- Dependencies: 227 --- Name: TABLE v_pools; Type: ACL; Schema: public; Owner: hiko --- - -GRANT SELECT ON TABLE public.v_pools TO grafana; - - --- --- TOC entry 3614 (class 0 OID 0) --- Dependencies: 291 --- Name: FUNCTION tfm_session_request(u_id uuid, u_agent text, OUT s_id uuid); Type: ACL; Schema: public; Owner: hiko --- - -GRANT ALL ON FUNCTION public.tfm_session_request(u_id uuid, u_agent text, OUT s_id uuid) TO tanabata; - - --- --- TOC entry 3616 (class 0 OID 0) --- Dependencies: 292 --- Name: FUNCTION tfm_user_auth(u_name character varying, u_password text); Type: ACL; Schema: public; Owner: hiko --- - -GRANT ALL ON FUNCTION public.tfm_user_auth(u_name character varying, u_password text) TO tanabata; - - --- --- TOC entry 3617 (class 0 OID 0) --- Dependencies: 232 --- Name: TABLE acl; Type: ACL; Schema: public; Owner: hiko --- - -GRANT SELECT ON TABLE public.acl TO grafana; - - --- --- TOC entry 3618 (class 0 OID 0) --- Dependencies: 217 --- Name: TABLE file_pool; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.file_pool FROM hiko; -GRANT SELECT ON TABLE public.file_pool TO grafana; - - --- --- TOC entry 3619 (class 0 OID 0) --- Dependencies: 216 --- Name: TABLE file_tag; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.file_tag FROM hiko; -GRANT SELECT ON TABLE public.file_tag TO grafana; - - --- --- TOC entry 3620 (class 0 OID 0) --- Dependencies: 219 --- Name: TABLE file_views; Type: ACL; Schema: public; Owner: hiko --- - -REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.file_views FROM hiko; -GRANT SELECT ON TABLE public.file_views TO tanabata; -GRANT SELECT ON TABLE public.file_views TO grafana; - - --- --- TOC entry 2192 (class 826 OID 61487) --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: -; Owner: hiko --- - -ALTER DEFAULT PRIVILEGES FOR ROLE hiko REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES FROM hiko; - - --- Completed on 2026-03-31 00:31:49 - --- --- PostgreSQL database dump complete --- - diff --git a/docs/reference/web/static/css/auth.css b/docs/reference/web/static/css/auth.css deleted file mode 100644 index 2dfcdfc..0000000 --- a/docs/reference/web/static/css/auth.css +++ /dev/null @@ -1,36 +0,0 @@ -body { - justify-content: center; - align-items: center; -} - -.decoration { - position: absolute; - top: 0; -} - -.decoration.left { - left: 0; - width: 20vw; -} - -.decoration.right { - right: 0; - width: 20vw; -} - -#auth { - max-width: 100%; -} - -#auth h1 { - margin-bottom: 28px; -} - -#auth .form-control { - margin: 14px 0; - border-radius: 14px; -} - -#login { - margin-top: 20px; -} diff --git a/docs/reference/web/static/css/general.css b/docs/reference/web/static/css/general.css deleted file mode 100644 index f4d3bf5..0000000 --- a/docs/reference/web/static/css/general.css +++ /dev/null @@ -1,54 +0,0 @@ -html, body { - margin: 0; - padding: 0; -} - -body { - background-color: #312F45; - color: #f0f0f0; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - min-height: 100vh; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: stretch; - font-family: Epilogue; - overflow-x: hidden; -} - -.btn { - height: 50px; - width: 100%; - border-radius: 14px; - font-size: 1.5rem; - font-weight: 500; -} - -.btn-primary { - background-color: #9592B5; - border-color: #454261; -} - -.btn-primary:hover { - background-color: #7D7AA4; - border-color: #454261; -} - -.btn-danger { - background-color: #DB6060; - border-color: #851E1E; -} - -.btn-danger:hover { - background-color: #D64848; - border-color: #851E1E; -} - -.btn-row { - display: flex; - justify-content: space-between; -} diff --git a/docs/reference/web/static/css/interface.css b/docs/reference/web/static/css/interface.css deleted file mode 100644 index 1b2d3b1..0000000 --- a/docs/reference/web/static/css/interface.css +++ /dev/null @@ -1,388 +0,0 @@ -header, footer { - margin: 0; - padding: 10px; - box-sizing: border-box; -} - -header { - padding: 20px; - box-shadow: 0 5px 5px #0004; -} - -.icon-header { - height: .8em; -} - -#select { - cursor: pointer; -} - -.sorting { - position: relative; - display: flex; - justify-content: space-between; -} - -#sorting { - cursor: pointer; -} - -.highlighted { - color: #9999AD; -} - -#icon-expand { - height: 10px; -} - -#sorting-options { - position: absolute; - right: 0; - top: 114%; - padding: 4px 10px; - box-sizing: border-box; - background-color: #111118; - border-radius: 10px; - text-align: left; - box-shadow: 0 0 10px black; - z-index: 9999; -} - -.sorting-option { - padding: 4px 0; - display: flex; - justify-content: space-between; -} - -.sorting-option input[type="radio"] { - float: unset; - margin-left: 1.8em; -} - -.filtering-wrapper { - margin-top: 10px; -} - -.filtering-block { - position: absolute; - top: 128px; - left: 14px; - right: 14px; - padding: 14px; - background-color: #111118; - border-radius: 10px; - box-shadow: 0 0 10px 4px #0004; - z-index: 9998; -} - -main { - margin: 0; - padding: 10px; - height: 100%; - display: flex; - justify-content: space-between; - align-content: flex-start; - align-items: flex-start; - flex-wrap: wrap; - box-sizing: border-box; - overflow-x: hidden; - overflow-y: scroll; -} - -main:after { - content: ""; - flex: auto; -} - -.item-preview { - position: relative; -} - -.item-selected:after { - content: ""; - display: block; - position: absolute; - top: 10px; - right: 10px; - width: 100%; - height: 50%; - background-image: url("/static/images/icon-select.svg"); - background-size: contain; - background-position: right; - background-repeat: no-repeat; -} - -.file-preview { - margin: 1px 0; - padding: 0; - width: 160px; - height: 160px; - max-width: calc(33vw - 7px); - max-height: calc(33vw - 7px); - overflow: hidden; - cursor: pointer; -} - -.file-thumb { - width: 100%; - height: 100%; - object-fit: contain; - object-position: center; -} - -.file-preview .overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: #0002; -} - -.file-preview:hover .overlay { - background-color: #0004; -} - -.tag-preview, .filtering-token { - margin: 5px 5px; - padding: 5px 10px; - border-radius: 5px; - background-color: #444455; - cursor: pointer; -} - -.category-preview { - margin: 5px 5px; - padding: 5px 10px; - border-radius: 5px; - background-color: #444455; - cursor: pointer; -} - -.file { - margin: 0; - padding: 0; - min-width: 100vw; - min-height: 100vh; - max-width: 100vw; - max-height: 100vh; - display: flex; - justify-content: center; - align-items: center; - background-color: black; -} - -.file .preview-img { - max-width: 100vw; - max-height: 100vh; -} - -.selection-manager { - position: fixed; - left: 10px; - right: 10px; - bottom: 65px; - box-sizing: border-box; - max-height: 40vh; - display: flex; - flex-direction: column; - padding: 15px 10px; - background-color: #181721; - border-radius: 10px; - box-shadow: 0 0 5px #0008; -} - -.selection-manager hr { - margin: 5px 0; -} - -.selection-header { - display: flex; - justify-content: space-between; -} - -.selection-header > * { - cursor: pointer; -} - -#selection-edit-tags { - color: #4DC7ED; -} - -#selection-add-to-pool { - color: #F5E872; -} - -#selection-delete { - color: #DB6060; -} - -.selection-tags { - max-height: 100%; - overflow-x: hidden; - overflow-y: scroll; -} - -input[type="color"] { - width: 100%; -} - -.tags-container, .filtering-operators, .filtering-tokens { - padding: 5px; - background-color: #212529; - border: 1px solid #495057; - border-radius: .375rem; - display: flex; - justify-content: space-between; - align-content: flex-start; - align-items: flex-start; - flex-wrap: wrap; - box-sizing: border-box; -} - -.filtering-operators { - margin-bottom: 15px; -} - -.tags-container, .filtering-tokens { - margin: 15px 0; - height: 200px; - overflow-x: hidden; - overflow-y: scroll; -} - -.tags-container:after, .filtering-tokens:after { - content: ""; - flex: auto; -} - -.tags-container-selected { - height: 100px; -} - -#files-filter { - margin-bottom: 0; - height: 56px; -} - -.viewer-wrapper { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - background-color: #000a; - display: flex; - justify-content: center; -/* overflow-y: scroll;*/ -} - -.viewer-nav { - position: absolute; - top: 0; - bottom: 0; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; -} - -.viewer-nav:hover { - background-color: #b4adff40; -} - -.viewer-nav-prev { - left: 0; - right: 80vw; -} - -.viewer-nav-next { - left: 80vw; - right: 0; -} - -.viewer-nav-close { - left: 0; - right: 0; - bottom: unset; - height: 15vh; -} - -.viewer-nav-icon { - width: 20px; - height: 32px; -} - -.viewer-nav-close > .viewer-nav-icon { - width: 16px; - height: 16px; -} - -#viewer { - width: 100%; - height: 100%; - max-width: 100%; -} - -.sessions-wrapper { - padding: 14px; - background-color: #111118; - border-radius: 10px; -} - -.btn-terminate { - height: 20px; - cursor: pointer; -} - -footer { - display: flex; - justify-content: space-between; - align-items: center; - background-color: #0007; -} - -.nav { - display: flex; - justify-content: center; - align-items: center; - height: 40px; - width: 18vw; - background: transparent; - border: 0; - border-radius: 10px; - outline: 0; -} - -.nav.curr, .nav:hover { - background-color: #343249; -} - -.navicon { - display: block; - height: 30px; -} - -#loader { - position: fixed; - left: 0; - top: 0; - right: 0; - bottom: 0; - display: flex; - justify-content: center; - align-items: center; - background-color: #000a; - z-index: 9999; -} - -.loader-wrapper { - padding: 15px; - border-radius: 12px; - background-color: white; -} - -.loader-img { - max-width: 20vw; - max-height: 20vh; -} diff --git a/docs/reference/web/static/fonts/Epilogue-VariableFont_wght.ttf b/docs/reference/web/static/fonts/Epilogue-VariableFont_wght.ttf deleted file mode 100644 index 7c3d128..0000000 Binary files a/docs/reference/web/static/fonts/Epilogue-VariableFont_wght.ttf and /dev/null differ diff --git a/docs/reference/web/static/images/android-icon-144x144.png b/docs/reference/web/static/images/android-icon-144x144.png deleted file mode 100644 index 34eb091..0000000 Binary files a/docs/reference/web/static/images/android-icon-144x144.png and /dev/null differ diff --git a/docs/reference/web/static/images/android-icon-192x192.png b/docs/reference/web/static/images/android-icon-192x192.png deleted file mode 100644 index e3444e9..0000000 Binary files a/docs/reference/web/static/images/android-icon-192x192.png and /dev/null differ diff --git a/docs/reference/web/static/images/android-icon-36x36.png b/docs/reference/web/static/images/android-icon-36x36.png deleted file mode 100644 index 5788f0f..0000000 Binary files a/docs/reference/web/static/images/android-icon-36x36.png and /dev/null differ diff --git a/docs/reference/web/static/images/android-icon-48x48.png b/docs/reference/web/static/images/android-icon-48x48.png deleted file mode 100644 index ed5aef8..0000000 Binary files a/docs/reference/web/static/images/android-icon-48x48.png and /dev/null differ diff --git a/docs/reference/web/static/images/android-icon-72x72.png b/docs/reference/web/static/images/android-icon-72x72.png deleted file mode 100644 index a57649b..0000000 Binary files a/docs/reference/web/static/images/android-icon-72x72.png and /dev/null differ diff --git a/docs/reference/web/static/images/android-icon-96x96.png b/docs/reference/web/static/images/android-icon-96x96.png deleted file mode 100644 index 71c2493..0000000 Binary files a/docs/reference/web/static/images/android-icon-96x96.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-114x114.png b/docs/reference/web/static/images/apple-icon-114x114.png deleted file mode 100644 index 7c706ab..0000000 Binary files a/docs/reference/web/static/images/apple-icon-114x114.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-120x120.png b/docs/reference/web/static/images/apple-icon-120x120.png deleted file mode 100644 index 5c65977..0000000 Binary files a/docs/reference/web/static/images/apple-icon-120x120.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-144x144.png b/docs/reference/web/static/images/apple-icon-144x144.png deleted file mode 100644 index 34eb091..0000000 Binary files a/docs/reference/web/static/images/apple-icon-144x144.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-152x152.png b/docs/reference/web/static/images/apple-icon-152x152.png deleted file mode 100644 index 0096fc7..0000000 Binary files a/docs/reference/web/static/images/apple-icon-152x152.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-180x180.png b/docs/reference/web/static/images/apple-icon-180x180.png deleted file mode 100644 index 72852d7..0000000 Binary files a/docs/reference/web/static/images/apple-icon-180x180.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-57x57.png b/docs/reference/web/static/images/apple-icon-57x57.png deleted file mode 100644 index f69ff76..0000000 Binary files a/docs/reference/web/static/images/apple-icon-57x57.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-60x60.png b/docs/reference/web/static/images/apple-icon-60x60.png deleted file mode 100644 index 6a584fd..0000000 Binary files a/docs/reference/web/static/images/apple-icon-60x60.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-72x72.png b/docs/reference/web/static/images/apple-icon-72x72.png deleted file mode 100644 index a57649b..0000000 Binary files a/docs/reference/web/static/images/apple-icon-72x72.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-76x76.png b/docs/reference/web/static/images/apple-icon-76x76.png deleted file mode 100644 index aa260fa..0000000 Binary files a/docs/reference/web/static/images/apple-icon-76x76.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon-precomposed.png b/docs/reference/web/static/images/apple-icon-precomposed.png deleted file mode 100644 index a15a379..0000000 Binary files a/docs/reference/web/static/images/apple-icon-precomposed.png and /dev/null differ diff --git a/docs/reference/web/static/images/apple-icon.png b/docs/reference/web/static/images/apple-icon.png deleted file mode 100644 index a15a379..0000000 Binary files a/docs/reference/web/static/images/apple-icon.png and /dev/null differ diff --git a/docs/reference/web/static/images/favicon-16x16.png b/docs/reference/web/static/images/favicon-16x16.png deleted file mode 100644 index 4860d9f..0000000 Binary files a/docs/reference/web/static/images/favicon-16x16.png and /dev/null differ diff --git a/docs/reference/web/static/images/favicon-32x32.png b/docs/reference/web/static/images/favicon-32x32.png deleted file mode 100644 index d51d3d3..0000000 Binary files a/docs/reference/web/static/images/favicon-32x32.png and /dev/null differ diff --git a/docs/reference/web/static/images/favicon-96x96.png b/docs/reference/web/static/images/favicon-96x96.png deleted file mode 100644 index 8a67b3d..0000000 Binary files a/docs/reference/web/static/images/favicon-96x96.png and /dev/null differ diff --git a/docs/reference/web/static/images/favicon-bg.png b/docs/reference/web/static/images/favicon-bg.png deleted file mode 100644 index 4c52e1a..0000000 Binary files a/docs/reference/web/static/images/favicon-bg.png and /dev/null differ diff --git a/docs/reference/web/static/images/favicon.png b/docs/reference/web/static/images/favicon.png deleted file mode 100644 index 2ffd366..0000000 Binary files a/docs/reference/web/static/images/favicon.png and /dev/null differ diff --git a/docs/reference/web/static/images/icon-add.svg b/docs/reference/web/static/images/icon-add.svg deleted file mode 100644 index 2d66071..0000000 --- a/docs/reference/web/static/images/icon-add.svg +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/docs/reference/web/static/images/icon-category.svg b/docs/reference/web/static/images/icon-category.svg deleted file mode 100644 index c43ee57..0000000 --- a/docs/reference/web/static/images/icon-category.svg +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/docs/reference/web/static/images/icon-expand.svg b/docs/reference/web/static/images/icon-expand.svg deleted file mode 100644 index 1606ddb..0000000 --- a/docs/reference/web/static/images/icon-expand.svg +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/docs/reference/web/static/images/icon-file.svg b/docs/reference/web/static/images/icon-file.svg deleted file mode 100644 index 07848b1..0000000 --- a/docs/reference/web/static/images/icon-file.svg +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/docs/reference/web/static/images/icon-pool.svg b/docs/reference/web/static/images/icon-pool.svg deleted file mode 100644 index 035e4ce..0000000 --- a/docs/reference/web/static/images/icon-pool.svg +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/docs/reference/web/static/images/icon-select.svg b/docs/reference/web/static/images/icon-select.svg deleted file mode 100644 index 6505e0a..0000000 --- a/docs/reference/web/static/images/icon-select.svg +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/docs/reference/web/static/images/icon-settings.svg b/docs/reference/web/static/images/icon-settings.svg deleted file mode 100644 index 25aebd2..0000000 --- a/docs/reference/web/static/images/icon-settings.svg +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/docs/reference/web/static/images/icon-tag.svg b/docs/reference/web/static/images/icon-tag.svg deleted file mode 100644 index 89b1e4b..0000000 --- a/docs/reference/web/static/images/icon-tag.svg +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/docs/reference/web/static/images/icon-terminate.svg b/docs/reference/web/static/images/icon-terminate.svg deleted file mode 100644 index 0e0b527..0000000 --- a/docs/reference/web/static/images/icon-terminate.svg +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/docs/reference/web/static/images/layer-controls.png b/docs/reference/web/static/images/layer-controls.png deleted file mode 100644 index 4e97926..0000000 Binary files a/docs/reference/web/static/images/layer-controls.png and /dev/null differ diff --git a/docs/reference/web/static/images/loader.gif b/docs/reference/web/static/images/loader.gif deleted file mode 100644 index d15dcdb..0000000 Binary files a/docs/reference/web/static/images/loader.gif and /dev/null differ diff --git a/docs/reference/web/static/images/ms-icon-144x144.png b/docs/reference/web/static/images/ms-icon-144x144.png deleted file mode 100644 index 103a7f1..0000000 Binary files a/docs/reference/web/static/images/ms-icon-144x144.png and /dev/null differ diff --git a/docs/reference/web/static/images/ms-icon-150x150.png b/docs/reference/web/static/images/ms-icon-150x150.png deleted file mode 100644 index 7d71939..0000000 Binary files a/docs/reference/web/static/images/ms-icon-150x150.png and /dev/null differ diff --git a/docs/reference/web/static/images/ms-icon-310x310.png b/docs/reference/web/static/images/ms-icon-310x310.png deleted file mode 100644 index 1a8c389..0000000 Binary files a/docs/reference/web/static/images/ms-icon-310x310.png and /dev/null differ diff --git a/docs/reference/web/static/images/ms-icon-70x70.png b/docs/reference/web/static/images/ms-icon-70x70.png deleted file mode 100644 index c3e84df..0000000 Binary files a/docs/reference/web/static/images/ms-icon-70x70.png and /dev/null differ diff --git a/docs/reference/web/static/images/tanabata-left.png b/docs/reference/web/static/images/tanabata-left.png deleted file mode 100644 index dd15de2..0000000 Binary files a/docs/reference/web/static/images/tanabata-left.png and /dev/null differ diff --git a/docs/reference/web/static/images/tanabata-right.png b/docs/reference/web/static/images/tanabata-right.png deleted file mode 100644 index 1a6d484..0000000 Binary files a/docs/reference/web/static/images/tanabata-right.png and /dev/null differ diff --git a/docs/reference/web/static/js/add-category.js b/docs/reference/web/static/js/add-category.js deleted file mode 100644 index 90ea61a..0000000 --- a/docs/reference/web/static/js/add-category.js +++ /dev/null @@ -1,22 +0,0 @@ -$(document).on("submit", "#object-add", function (e) { - e.preventDefault(); - $("#loader").css("display", ""); - $.ajax({ - url: location.pathname, - type: "POST", - data: $(this).serialize(), - dataType: "json", - success: function (resp) { - $("#loader").css("display", "none"); - if (resp.status) { - location.href = location.pathname.substring(0, location.pathname.lastIndexOf("/")); - } else { - alert(resp.error); - } - }, - failure: function (err) { - $("#loader").css("display", "none"); - alert(err); - } - }); -}); diff --git a/docs/reference/web/static/js/add-tag.js b/docs/reference/web/static/js/add-tag.js deleted file mode 100644 index 90ea61a..0000000 --- a/docs/reference/web/static/js/add-tag.js +++ /dev/null @@ -1,22 +0,0 @@ -$(document).on("submit", "#object-add", function (e) { - e.preventDefault(); - $("#loader").css("display", ""); - $.ajax({ - url: location.pathname, - type: "POST", - data: $(this).serialize(), - dataType: "json", - success: function (resp) { - $("#loader").css("display", "none"); - if (resp.status) { - location.href = location.pathname.substring(0, location.pathname.lastIndexOf("/")); - } else { - alert(resp.error); - } - }, - failure: function (err) { - $("#loader").css("display", "none"); - alert(err); - } - }); -}); diff --git a/docs/reference/web/static/js/auth.js b/docs/reference/web/static/js/auth.js deleted file mode 100644 index f256d8e..0000000 --- a/docs/reference/web/static/js/auth.js +++ /dev/null @@ -1,19 +0,0 @@ -$("#auth").on("submit", function submit(e) { - e.preventDefault(); - $.ajax({ - url: "/auth", - type: "POST", - data: $("#auth").serialize(), - dataType: "json", - success: function(resp) { - if (resp.status) { - location.reload(); - } else { - alert(resp.error); - } - }, - failure: function(err) { - alert(err); - } - }); -}); diff --git a/docs/reference/web/static/js/category.js b/docs/reference/web/static/js/category.js deleted file mode 100644 index 8226c20..0000000 --- a/docs/reference/web/static/js/category.js +++ /dev/null @@ -1,20 +0,0 @@ -$(document).on("submit", "#object-edit", function (e) { - e.preventDefault(); - $("#loader").css("display", ""); - $.ajax({ - url: location.pathname + "/edit", - type: "POST", - data: $(this).serialize(), - dataType: "json", - success: function (resp) { - $("#loader").css("display", "none"); - if (!resp.status) { - alert(resp.error); - } - }, - failure: function (err) { - $("#loader").css("display", "none"); - alert(err); - } - }); -}); diff --git a/docs/reference/web/static/js/file.js b/docs/reference/web/static/js/file.js deleted file mode 100644 index 0286b01..0000000 --- a/docs/reference/web/static/js/file.js +++ /dev/null @@ -1,88 +0,0 @@ -$(document).on("input", "#file-tags-filter", function (e) { - let filter = $(this).val().toLowerCase(); - let unfiltered = $("#file-tags-other > .tag-preview"); - if (filter === "") { - unfiltered.css("display", ""); - return; - } - unfiltered.each((index, element) => { - let current = $(element); - if (current.text().toLowerCase().includes(filter)) { - current.css("display", ""); - } else { - current.css("display", "none"); - } - }); -}); - -$(document).on("click", "#file-tags-other > .tag-preview", function (e) { - $("#loader").css("display", ""); - $.ajax({ - url: location.pathname + "/tag", - type: "POST", - contentType: "application/json", - data: JSON.stringify({add: true, tag_id: $(this).attr("tag_id")}), - dataType: "json", - success: function (resp) { - $("#loader").css("display", "none"); - if (resp.status) { - resp.tags.forEach((tag_id) => { - $(`#file-tags-other > .tag-preview[tag_id='${tag_id}']`).css("display", "none"); - $(`#file-tags-selected > .tag-preview[tag_id='${tag_id}']`).css("display", ""); - }); - } else { - alert(resp.error); - } - }, - failure: function (err) { - $("#loader").css("display", "none"); - alert(err); - } - }); -}); - -$(document).on("click", "#file-tags-selected > .tag-preview", function (e) { - $("#loader").css("display", ""); - let tag_id = $(this).attr("tag_id"); - $.ajax({ - url: location.pathname + "/tag", - type: "POST", - contentType: "application/json", - data: JSON.stringify({add: false, tag_id: $(this).attr("tag_id")}), - dataType: "json", - success: function (resp) { - $("#loader").css("display", "none"); - if (resp.status) { - $(`#file-tags-selected > .tag-preview[tag_id='${tag_id}']`).css("display", "none"); - $(`#file-tags-other > .tag-preview[tag_id='${tag_id}']`).css("display", ""); - } else { - alert(resp.error); - } - }, - failure: function (err) { - $("#loader").css("display", "none"); - alert(err); - } - }); -}); - -$(document).on("submit", "#object-edit", function (e) { - e.preventDefault(); - $("#loader").css("display", ""); - $.ajax({ - url: location.pathname + "/edit", - type: "POST", - data: $(this).serialize(), - dataType: "json", - success: function (resp) { - $("#loader").css("display", "none"); - if (!resp.status) { - alert(resp.error); - } - }, - failure: function (err) { - $("#loader").css("display", "none"); - alert(err); - } - }); -}); diff --git a/docs/reference/web/static/js/files.js b/docs/reference/web/static/js/files.js deleted file mode 100644 index dc311da..0000000 --- a/docs/reference/web/static/js/files.js +++ /dev/null @@ -1,130 +0,0 @@ -var curr_page = 0; -var load_lock = false; -var init_filter = null; - -function files_load() { - if (load_lock) { - return; - } - load_lock = true; - let container = $("main"); - $("#loader").css("display", ""); - $.ajax({ - url: "/api/files?limit=50&offset=" + curr_page*50 + (init_filter ? "&filter=" + encodeURIComponent("{" + init_filter + "}") : ""), - type: "GET", - contentType: "application/json", - async: false, - success: function (resp) { - $("#loader").css("display", "none"); - resp.forEach((file) => { - container.append(`
-
-
-
-
-
diff --git a/docs/reference/web/templates/head.html b/docs/reference/web/templates/head.html
deleted file mode 100644
index 1e37514..0000000
--- a/docs/reference/web/templates/head.html
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/docs/reference/web/templates/new-category.html b/docs/reference/web/templates/new-category.html
deleted file mode 100644
index b33f34e..0000000
--- a/docs/reference/web/templates/new-category.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
- {% include 'head.html' %}
-
- | User agent | -Started | -Expires | -Terminate | -
|---|