50 Commits

Author SHA1 Message Date
8cfcd39ab6 feat(backend): apply tag rules retroactively to existing files on activation
Extend PATCH /tags/{id}/rules/{then_id} to accept apply_to_existing bool.
When a rule is activated with apply_to_existing=true, a single recursive
CTE retroactively inserts the full transitive expansion of then_tag into
data.file_tag for all files already carrying when_tag:

  WITH RECURSIVE expansion(tag_id) AS (
      SELECT then_tag_id
      UNION
      SELECT r.then_tag_id FROM data.tag_rules r
      JOIN expansion e ON r.when_tag_id = e.tag_id
      WHERE r.is_active = true
  )
  INSERT INTO data.file_tag ... ON CONFLICT DO NOTHING

Changes:
- port/repository.go: add applyToExisting param to TagRuleRepo.SetActive
- db/postgres/tag_repo.go: implement recursive CTE retroactive apply
- service/tag_service.go: thread applyToExisting through SetRuleActive
- handler/tag_handler.go: parse apply_to_existing from PATCH body
- openapi.yaml: document apply_to_existing on PATCH endpoint
- integration test: add TestTagRuleActivateApplyToExisting covering
  no-op when false, direct+transitive apply when true

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 00:00:45 +03:00
6da25dc696 feat(frontend): implement settings page
- Profile editor: name and optional password change with confirm field,
  saves via PATCH /users/me and updates auth store
- Appearance: theme toggle button (dark/light) with sun/moon icon
- App cache: PWA reset — unregisters service workers and clears caches
- Sessions: list active sessions with parsed user agent, start date,
  expiry, current badge, and terminate button per session
- Add mock handlers: PATCH /users/me, DELETE /auth/sessions/{id}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:37:44 +03:00
9b1aa40522 feat(frontend): implement bulk tag editing for multi-file selection
- Add BulkTagEditor component: loads common/partial tags via
  POST /files/bulk/common-tags and applies changes via POST /files/bulk/tags
- Common tags shown solid with × to remove from all files
- Partial tags shown with dashed border and ~ indicator; clicking promotes
  to common (adds to files that are missing it)
- Wire "Edit tags" button in SelectionBar to a bottom sheet with the editor
- Add mock handlers for /files/bulk/common-tags and /files/bulk/tags

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:40:53 +03:00
d79e76e9b7 feat(frontend): implement pool views and add-to-pool from file list
- Add /pools list page with search, sort, load-more pagination
- Add /pools/new create form (name, notes, public toggle)
- Add /pools/[id] detail page: metadata editing, ordered file grid,
  drag-to-reorder, filter bar, file selection/removal, add-files overlay
- Add pool sort store (poolSorting) to sorting.ts
- Wire "Add to pool" button in SelectionBar: bottom-sheet pool picker
  loads pool list, user picks one, selected files are POSTed to pool
- Add full pool mock API handlers in vite-mock-plugin.ts (CRUD + file
  management: add, remove, reorder with cursor pagination)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 08:31:06 +03:00
1f591f3a3f feat(frontend): replace JS confirm() with native dialog component
- ConfirmDialog: centered <dialog> with backdrop blur, cancel + confirm (danger variant)
- tags/[id]: delete tag uses ConfirmDialog
- categories/[id]: delete category uses ConfirmDialog
- files: bulk delete calls POST /files/bulk/delete, removes files from list,
  text updated to "Move to trash" (soft delete)
- mock: add POST /files/bulk/delete handler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:48:21 +03:00
1931adcd38 feat(frontend): implement category list, create, and edit pages
- /categories: list with colored pills, search + clear, sort/order controls
- /categories/new: create form with name, color picker, notes, is_public
- /categories/[id]: edit form + tags-in-category section with load more
- sorting.ts: add categorySorting store (name/color/created, persisted)
- mock: category CRUD, GET /categories/{id}/tags, search/sort/offset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:38:52 +03:00
21f3acadf0 feat: add PATCH /tags/{id}/rules/{then_id} to activate/deactivate rules
- openapi.yaml: new PATCH endpoint with is_active body, returns TagRule
- backend/service: SetRuleActive calls repo.SetActive then returns updated rule
- backend/handler: PatchRule validates body and delegates to service
- backend/router: register PATCH /:tag_id/rules/:then_tag_id
- frontend: TagRuleEditor uses PATCH instead of delete+recreate
- mock: handle PATCH /tags/{id}/rules/{then_id}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:31:12 +03:00
871250345a feat(frontend): add activate/deactivate toggle for tag rules
- Toggle button (filled/hollow circle) on each rule row
- Inactive rules dim to 45% opacity
- Toggle via delete + recreate with new is_active value
- Mock: track is_active per rule (Map instead of Set)
- Show all available tags by default in add-rule picker

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:26:07 +03:00
6e24060d99 feat(frontend): add clear button to TagPicker search input
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:16:49 +03:00
f7d7e8ce37 feat(frontend): implement tag list, create, and edit pages
- /tags: list with search + clear button, sort/order controls, offset pagination
  Fix infinite requests when search matches no tags (track initialLoaded flag)
- /tags/new: create form with name, notes, color picker, category, is_public
- /tags/[id]: edit form + TagRuleEditor for implied-tag rules + delete
- TagBadge: colored pill with optional onclick and size prop
- TagRuleEditor: manage implied-tag rules (search to add, × to remove)
- Mock: tag/category CRUD, rules CRUD, search/sort, 5 mock categories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 23:14:04 +03:00
b9cace2997 feat(frontend): implement file upload with drag-and-drop and per-file progress
- client.ts: add uploadWithProgress() using XHR for upload progress events
- FileUpload.svelte: drag-drop zone wrapper, multi-file queue with individual
  progress bars, success/error status, MIME rejection message, dismiss panel
- Header.svelte: optional onUpload prop renders upload icon button
- files/+page.svelte: wire upload button, prepend uploaded files to grid
- vite-mock-plugin.ts: handle POST /files, unshift new file into mock array
- Fix crypto.randomUUID() crash on non-secure HTTP context (use Date.now + Math.random)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:02:26 +03:00
a5b610d472 feat(frontend): implement file viewer page with metadata editing and tag picker
- files/[id]/+page.svelte: full-screen preview (100dvh), sticky top bar,
  prev/next nav via anchor API, notes/datetime/is_public editing, TagPicker,
  EXIF display, keyboard navigation (←/→/Esc)
- TagPicker.svelte: assigned tags with remove, searchable available tags to add
- Fix infinite request loop: previewSrc read inside $effect tracked as dependency;
  wrapped in untrack() to prevent re-triggering on blob URL assignment
- vite-mock-plugin: add GET/PATCH /files/{id}, preview endpoint, tags CRUD,
  anchor-based pagination, in-memory mutable state for file overrides and tags
- files/+page.svelte: migrate from deprecated $app/stores to $app/state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 13:55:04 +03:00
84c47d0282 feat(frontend): expand mock tags to 207 entries for filter bar testing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 13:35:05 +03:00
6fa340b17c feat(frontend): make header and filter bar sticky on scroll
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 13:32:35 +03:00
aebf7127af feat(frontend): implement file selection with long-press, shift+click, and touch drag
- selection.ts: store with select/deselect/toggle/enter/exit, derived count and active
- FileCard: long-press (400ms) enters selection mode, shows check overlay, blocks context menu
- Header: Select/Cancel button toggles selection mode
- SelectionBar: floating bar above navbar with count, Edit tags, Add to pool, Delete
- Shift+click range-selects between last and current index (desktop)
- Touch drag-to-select/deselect after long-press; non-passive touchmove blocks scroll only during drag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 13:30:26 +03:00
63ea1a4d6a feat(frontend): make filter expression tokens draggable for reordering
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 12:57:45 +03:00
27d8215a0a feat(frontend): add header, filter bar, and sorting store for files page
- sorting.ts: per-section sort store (sort field + order) persisted to localStorage
- dsl.ts: build/parse DSL filter strings ({t=uuid,&,|,!,...})
- Header.svelte: sort dropdown, asc/desc toggle, filter toggle button
- FilterBar.svelte: tag token picker with operator buttons, search, apply/reset
- files/+page.svelte: wired header + filter bar, resets pagination on sort/filter change
- vite-mock-plugin.ts: added 5 mock tags for filter bar development

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 12:47:18 +03:00
e72d4822e9 feat(frontend): implement file gallery page with infinite scroll
Adds InfiniteScroll component (IntersectionObserver, 300px margin,
CSS spinner). Adds FileCard component (fetch thumbnail with JWT auth
header, blob URL, shimmer placeholder). Adds files/+page.svelte with
160×160 flex-wrap grid and cursor pagination. Updates mock plugin with
75 sample files, cursor pagination, and colored SVG thumbnail handler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 03:34:33 +03:00
9e341a0fc6 feat(frontend): add dev mock API plugin
Adds a Vite dev-only middleware that intercepts /api/v1/* requests
and returns mock responses for auth, users, files, tags, categories,
and pools. Login with any username and password "password".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 03:26:03 +03:00
7770960cbf feat(frontend): add root layout with auth guard and bottom navbar
Adds +layout.ts auth guard (redirects to /login when no token).
Adds bottom navbar with inline SVGs for Categories/Tags/Files/Pools/
Settings, active-route highlight (#343249), muted-to-bright color
transition. Adds theme store (dark/light, persisted to localStorage,
applies data-theme attribute). Hides navbar on /login route.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 03:21:00 +03:00
e21d0ef67b feat(frontend): implement auth store and login page
Rewrites auth store with typed AuthUser shape (id, name, isAdmin) and
localStorage persistence. Adds login page with tanabata decorative
images, centered form, purple primary button matching the reference
design.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 03:06:32 +03:00
fde8672bb1 feat(frontend): implement API client and auth module
Adds the base fetch wrapper (client.ts) with JWT auth headers,
automatic token refresh on 401 with request deduplication, and
typed ApiError. Adds auth.ts with login/logout/refresh/listSessions/
terminateSession. Adds authStore (stores/auth.ts) persisted to
localStorage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 03:02:35 +03:00
071829a79e fix(backend): fix file upload and integration test suite
- Make data.files.exif column nullable (was NOT NULL but service passes nil
  for files without EXIF data, causing a constraint violation on upload)
- FileRepo.Create: include id in INSERT so disk storage path and DB record
  share the same UUID (previously DB generated its own UUID, causing a mismatch)
- Integration test: use correct filter DSL format {t=<uuid>} instead of tag:<uuid>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 02:56:04 +03:00
0784605267 feat(backend): add integration tests with testcontainers-go
Add internal/integration/server_test.go covering the full happy-path
flow (admin login, user create, upload, tag assign, tag filter, ACL
grant, pool create/add/reorder, trash/restore/permanent-delete, audit
log). Also add targeted tests for blocked-user login prevention, pool
reorder, and tag auto-rules. Uses a disposable postgres:16-alpine
container via testcontainers-go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 02:34:16 +03:00
e767b07b23 feat(backend): implement user, ACL, and audit stacks
Add UserService (GetMe, UpdateMe, admin CRUD with block/unblock),
UserHandler (/users, /users/me), ACLHandler (GET/PUT /acl/:type/:id),
AuditHandler (GET /audit with all filters). Fix UserRepo.Update to
include is_blocked. Wire all remaining routes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 02:25:16 +03:00
3a49036507 feat(backend): implement pool stack
Add pool repo (gap-based position ordering, cursor pagination, add/remove/reorder
files), service, handler, and wire all /pools endpoints including
/pools/:id/files, /pools/:id/files/remove, and /pools/:id/files/reorder.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 22:04:27 +03:00
21debf626d feat(backend): implement category stack
Add category repo, service, handler, and wire all /categories endpoints
including list, create, get, update, delete, and list-tags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 21:50:57 +03:00
04d2dfa16e docs(project): document scoped commit naming convention in CLAUDE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 21:41:22 +03:00
595b8fa671 feat(backend): implement full tag stack (repo, service, handler, routes)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 21:29:20 +03:00
5050dbea3c feat(backend): implement file handler and wire all /files endpoints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 18:40:04 +03:00
99508cdbf8 feat(backend): implement file service with upload, CRUD, ACL, and audit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 18:28:59 +03:00
0ae8b81a0b feat(backend): seed MIME types and support all image/video formats
007_seed_data.sql: insert 10 MIME types (4 image, 6 video) with their
canonical extensions into core.mime_types.

disk.go: register golang.org/x/image/webp decoder so imaging.Open
handles WebP still images. Videos (mp4, mov, avi, webm, 3gp, m4v)
continue to go through the ffmpeg frame-extraction path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 18:21:27 +03:00
fae87ad05c feat(backend): implement DiskStorage with on-demand thumbnail/preview cache
Files stored as {files_path}/{id} (no extension). The ext parameter
is removed from Save/Read/Delete in both the port interface and
the implementation.

Thumbnail and Preview both use imaging.Thumbnail (fit within
configured max bounds, never upscale, never crop) — the config
values THUMB_WIDTH/HEIGHT and PREVIEW_WIDTH/HEIGHT are upper limits,
not forced dimensions.

Non-decodable files (video, etc.) receive a #444455 placeholder.
Cache writes use atomic temp→rename; on cache failure the generated
image is served from memory so the request still succeeds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 18:11:54 +03:00
1a873949f4 feat(backend): implement FileRepo and filter DSL parser
filter_parser.go — recursive-descent parser for the {token,...} DSL.
Tokens: t=UUID (tag), m=INT (MIME exact), m~PATTERN (MIME LIKE),
operators & | ! ( ) with standard NOT>AND>OR precedence.
All values go through pgx parameters ($N) — SQL injection impossible.

file_repo.go — full FileRepo:
- Create/GetByID/Update via CTE RETURNING with JOIN for one round-trip
- SoftDelete/Restore/DeletePermanent with RowsAffected guards
- SetTags: full replace (DELETE + INSERT per tag)
- ListTags: delegates to loadTagsBatch (single query for N files)
- List: keyset cursor pagination (bidirectional), anchor mode,
  filter DSL, search ILIKE, trash flag, 4 sort columns.
  Cursor is base64url(JSON) encoding sort position; backward
  pagination fetches in reversed ORDER BY then reverses the slice.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 16:55:23 +03:00
0724892e29 feat(backend): implement audit repo and service
AuditRepo.Log resolves action_type_id/object_type_id via SQL subqueries.
AuditRepo.List supports dynamic filtering by user, action, object type/ID,
and date range with COUNT(*) OVER() for total count.
AuditService.Log reads user from context, marshals details to JSON,
and delegates to the repo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 01:19:24 +03:00
559f891d8d feat(backend): implement ACL repo and service
Add postgres ACLRepo (List/Get/Set) and ACLService with CanView/CanEdit
checks (admin bypass, public flag, creator shortcut, explicit grants)
and GetPermissions/SetPermissions for the /acl endpoints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 01:13:21 +03:00
5a617af22c fix(backend): wire handler layer in main.go and fix migration issues
cmd/server/main.go: replace stub router with full wiring —
  UserRepo, SessionRepo, AuthService, AuthMiddleware, AuthHandler,
  NewRouter; use postgres.NewPool instead of pgxpool.New directly.

migrations/001_init_schemas.sql: wrap uuid_v7 and uuid_extract_timestamp
  function bodies with goose StatementBegin/End so semicolons inside
  dollar-quoted strings are not treated as statement separators.

migrations/007_seed_data.sql: add seed admin user (admin/admin,
  bcrypt cost 10, is_admin=true, can_create=true) for manual testing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:54:54 +03:00
1766dc2b3c feat(backend): implement auth handler, middleware, and router
domain/context.go: extend WithUser/UserFromContext with session ID

handler/response.go: respondJSON/respondError with domain→HTTP status
  mapping (404/403/401/409/400/415/500)
handler/middleware.go: Bearer JWT extraction, ParseAccessToken,
  domain.WithUser injection; aborts with 401 JSON on failure
handler/auth_handler.go: Login, Refresh, Logout, ListSessions,
  TerminateSession
handler/router.go: /health, /api/v1/auth routes; login and refresh
  are public, session routes protected by AuthMiddleware

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:43:41 +03:00
277f42035c feat(backend): implement auth service with JWT and session management
Login: bcrypt credential validation, session creation, JWT pair issuance.
Logout/TerminateSession: soft-delete session (is_active = false).
Refresh: token rotation — deactivate old session, issue new pair.
ListSessions: marks IsCurrent by comparing session IDs.
ParseAccessToken: for use by auth middleware.

Claims carry uid (int16), adm (bool), sid (int). Refresh tokens are
stored as SHA-256 hashes; raw tokens never reach the database.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:38:21 +03:00
0e9b4637b0 feat(backend): implement db helpers and postgres pool/transactor
- Add is_blocked to core.users (002_core_tables.sql)
- Add is_active to activity.sessions for soft deletes (005_activity_tables.sql)
- Implement UserRepo: List, GetByID, GetByName, Create, Update, Delete
- Implement MimeRepo: List, GetByID, GetByName
- Implement SessionRepo: Create, GetByTokenHash, ListByUser,
  UpdateLastActivity, Delete, DeleteByUserID
- Session deletes are soft (SET is_active = false); is_active is a
  SQL-only filter, not mapped to the domain type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:34:45 +03:00
2c83073903 feat(backend): implement db helpers and postgres pool/transactor
db/db.go: TxFromContext/ContextWithTx for transaction propagation,
Querier interface (QueryRow/Query/Exec), ScanRow generic helper,
ClampLimit/ClampOffset pagination guards.

db/postgres/postgres.go: NewPool with ping validation, Transactor
backed by pgxpool (BeginTx → fn → commit/rollback), connOrTx helper
that returns the active transaction from context or falls back to pool.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:15:17 +03:00
83fda85bea feat(backend): implement port interfaces (repository and storage)
Define all repository interfaces in port/repository.go:
FileRepo, TagRepo, TagRuleRepo, CategoryRepo, PoolRepo, UserRepo,
SessionRepo, ACLRepo, AuditRepo, MimeRepo, and Transactor.
Add OffsetParams and PoolFileListParams as shared parameter structs.

Define FileStorage interface in port/storage.go with Save, Read,
Delete, Thumbnail, and Preview methods.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:11:06 +03:00
1e2a2a61de refactor(backend): strengthen domain layer types and add missing page types
- DomainError struct with Code() string method replaces plain errors.New
  sentinels; errors.Is() still works via pointer equality
- UUIDCreatedAt(uuid.UUID) time.Time helper extracts timestamp from UUID v7
- Add TagOffsetPage, CategoryOffsetPage, PoolOffsetPage
- FileListParams fields grouped with comments matching openapi.yaml params
- Fix mismatched comment on UserPage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:06:44 +03:00
36d9488f21 feat(frontend): initialize SvelteKit frontend with Tailwind and OpenAPI types
- SvelteKit SPA mode with adapter-static (index.html fallback)
- Tailwind CSS v4 via @tailwindcss/vite with custom color palette
- CSS custom properties for dark/light theme (dark is default)
- Epilogue variable font with preload
- openapi-typescript generates src/lib/api/schema.ts from openapi.yaml
- Friendly domain type aliases in src/lib/api/types.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 00:00:26 +03:00
8565bf9200 feat(backend): config, migrations embed, and server entrypoint
- internal/config: typed Config struct loaded from env vars via godotenv;
  all fields from docs (listen addr, JWT, DB, storage, thumbs, import)
- migrations/embed.go: embed FS so goose SQL files are baked into the binary
- cmd/server/main.go: load config → connect pgxpool → goose migrations
  (embedded) → Gin server with GET /health returning 200 OK
- .env.example: documents all required and optional env vars
- go.mod: bump to Go 1.26, add gin/pgx/goose/godotenv as direct deps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:57:17 +03:00
ecad017274 refactor(backend): split monolithic migration into 7 goose files
001_init_schemas  — extensions, schemas, uuid_v7 functions
002_core_tables   — core.users, mime_types, object_types
003_data_tables   — data.categories, tags, tag_rules, files, file_tag, pools, file_pool
004_acl_tables    — acl.permissions
005_activity_tables — activity.action_types, sessions, file_views, pool_views, tag_uses, audit_log
006_indexes       — all indexes across all schemas
007_seed_data     — object_types and action_types reference rows

Each file has -- +goose Up / Down annotations; downs drop in reverse
dependency order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:40:36 +03:00
a2823337b6 docs(project): add reference Python/Flask implementation
Previous version of Tanabata used as visual and functional reference
for the new Go + SvelteKit rewrite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:36:05 +03:00
4c10553549 chore(project): add .gitignore and .gitattributes
.gitignore covers env/secrets, OS files, IDE, Go build artifacts,
frontend build output, data dirs, and vendored reference libs.
.gitattributes enforces LF line endings, marks binaries, configures
diff drivers per language, and sets Linguist hints for repo stats.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:35:22 +03:00
1d341eef24 feat(backend): initialize Go module and implement domain layer
- Add go.mod (module tanabata/backend, Go 1.21) with uuid dependency
- Implement internal/domain: File, Tag, TagRule, Category, Pool, PoolFile,
  User, Session, Permission, ObjectType, AuditEntry + all pagination types
- Add domain error sentinels (ErrNotFound, ErrForbidden, etc.)
- Add context helpers WithUser/UserFromContext for JWT propagation
- Fix migration: remove redundant DEFAULT on exif jsonb column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 18:28:33 +03:00
dbdc80b3a0 chore(project): initial project structure 2026-04-01 16:17:37 +03:00