Commit Graph
15 Commits
Author SHA1 Message Date
2c8e2cc837 feat: v2.3.0 T1 — 站内信系统 (in-app notifications) (#307)
* feat: add in-app notification system (站内信) — schema, service, API, Bell UI

- Add `notifications` table to DB schema with userId/type/title/body/refType/refId/metadata/readAt/createdAt fields; two indexes for list & unread queries
- Migration `0010_notifications.sql` created manually (drizzle-kit requires TTY)
- Service layer: createNotification, listNotifications (paginated + unreadOnly filter), markAsRead (idempotent, owner-only), markAllAsRead, unreadCount
- REST API at `/api/notifications`: list + unreadCount, GET unread-count, POST :id/read (204), POST read-all
- Shared `Notification` type, `listNotificationsQuerySchema`, RPC client export
- NotificationBell (badge, 30s polling), NotificationDropdown, NotificationItem components injected into AppSidebar footer
- Bell badge capped at "9+"; unread items bold; click marks read + navigates via refType/refId
- i18n: en + zh translations for all notification keys
- 26 Node integration tests + 5 CF smoke tests; all 1884 + 26 tests pass

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* test: add notification API wrapper tests and component logic tests; fix dead condition

- Add tests for listNotifications, getUnreadCount, markNotificationRead, markAllNotificationsRead in api.test.ts
- Add notification-bell.test.ts: badge label logic (0/5/"9+" cap) and polling interval
- Add notification-dropdown.test.ts: mark-all-read visibility, empty state, query key
- Add notification-item.test.ts: resolveHref (share token nav, malformed JSON), diffMinutes, isUnread, title style
- Fix dead condition in markNotificationRead: simplify `!res.ok && res.status !== 204` → `!res.ok`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: trigger CI check run for test coverage fixes

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 01:23:54 -04:00
Jasper VanandBob 2affe21c45 feat(shares): add save-to-drive — cross-workspace copy with quota and name conflict handling (#310)
- POST /api/shares/:token/save endpoint on Hono router
- resolveShareByToken distinguishes not_found/revoked (404) from matter_trashed (410)
- same-storage S3 CopyObject; cross-storage server-side streamCopy via GetObject→PutObject
- pre-flight quota check (computeSourceBytes + isQuotaSufficient) → 400 QUOTA_EXCEEDED
- BFS recursive folder copy with onConflict:'rename' for all created matters
- activity event recorded per saved file with action:'save_from_share'
- integration tests (Node/SQLite) and CF Worker tests (D1)

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-20 00:00:02 -04:00
Jasper VanandBob de4938e75c feat: add shares schema, service layer, and lifecycle integration (#308)
- Add `shares` and `share_recipients` tables to Drizzle schema with indices
- Add migration 0010_shares.sql for shares/share_recipients tables
- Add `server/lib/password.ts` extracting scrypt hash/verify from auth.ts
  to eliminate duplicate crypto params across services
- Add `server/services/share.ts` implementing full CRUD + atomic counters:
  createShare, getShareByToken, incrementViews, incrementDownloadsAtomic
  (atomic SQL UPDATE), listSharesByCreator, revokeShare, cascadeDeleteByMatter
- Add `shared/schemas/share.ts` Zod validation schemas
- Export Share, ShareKind, ShareRecipient from shared/types
- Extend `purge.ts` to cascade-delete shares on matter hard-delete
- Add 38 integration tests and CF Workers atomic counter race tests

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-19 23:23:48 -04:00
saltboandClaude Opus 4.7 50c92d63ac fix(files): enforce unique names + Finder-style conflict resolver
Duplicate folder/file names silently created duplicates under the same
parent. Add DB-level partial unique index on (org_id, parent, LOWER(name))
for active rows, plus a centralized plan/commit helper threaded through
create, rename, move, copy, upload-confirm, and restore. 409 responses
open a Keep Both / Replace / Cancel dialog with sticky "apply to all"
for batch operations; case-insensitive match matches OS conventions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:28:40 -04:00
Jasper VanandBob 060a8fd9b1 refactor: remove isPublic from matters, simplify user share homepage (#301)
* refactor: remove isPublic from matters, simplify user share homepage

- Remove is_public column from matters table and migration 0006
- Remove batchVisibilitySchema and BatchVisibilityInput from shared schemas
- Remove isPublic field from StorageObject type
- Remove /api/objects/batch/visibility endpoint
- Remove batchUpdateVisibility service function
- Simplify profile service to keep only getUserByUsername, getUserOrgId, buildBreadcrumb
- Simplify profile route to return empty shares (v2.3 share system pending)
- Add try-catch error handling to profile routes (DB errors return 500)
- Replace PublicProfileSection file-checkbox UI with link + hint text
- Simplify /u/:username page to show user info and empty state
- Remove browseProfile API client function
- Delete .codecov.yml
- Update i18n: add publicProfile.hint key, remove obsolete keys
- Trim integration tests to user-lookup cases only

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: remove 0006_public_profile from migration journal

The SQL file was deleted but the journal entry remained, causing the
drizzle migrator to fail on fresh databases when starting the Node server.
Removing the entry ensures the migrator no longer tries to find the deleted file.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: restore codecov config excluding React UI files from coverage

React route and component files require a DOM environment and cannot
be unit-tested via the server test runner. The exclusion is legitimate
and not tied to the removed isPublic feature.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: remove dead code and redundant try-catch from profile routes

- Remove getUserOrgId (dead code, never called after refactor)
- Remove unused findPersonalOrg import
- Remove try-catch wrappers in profile routes (Hono handles
  uncaught errors via its default error handler — centralized
  error handling, no defensive noise per coding principles)

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-14 13:30:17 -04:00
Jasper VanandBob d5033150fc feat: add activity feed for team workspaces (#298)
Add activity_events table, migration, activity service, teams API route,
integration tests, and frontend activity timeline page. Tracks file
operations (upload, create, delete, rename, move, restore) per team.

- server/db/schema.ts: activityEvents table
- migrations/0007_activity_feed.sql + _journal.json: migration
- server/services/activity.ts: recordActivity + listActivities
- server/services/matter.ts: instrument with optional userId param
- server/routes/objects.ts: pass userId to matter service calls
- server/routes/teams.ts: GET /api/teams/:teamId/activity endpoint
- server/routes/teams.integration.test.ts: full integration test coverage
- server/test/setup.ts: add activity_events table to test schema
- shared/types/index.ts: ActivityEvent type
- src/lib/rpc.ts + api.ts: teamsApi client + listTeamActivities()
- src/routes/.../activity.tsx: timeline page with infinite scroll
- src/routeTree.gen.ts: register new route
- i18n: EN + ZH strings for activity feed

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-14 13:22:54 -04:00
Jasper VanandClaude Sonnet 4.6 f1604c7c68 feat: user public share homepage (/u/:username) (#294)
* feat: add user public share homepage (/u/:username)

- Add isPublic boolean field to matters table (migration 0006)
- Create public profile API (/api/profiles/:username) without auth
- Add directory browse endpoint (/api/profiles/:username/browse)
- Add batch visibility update endpoint (/api/objects/batch/visibility)
- Create public profile page at /u/$username with breadcrumb navigation
- Add Public Profile section to settings page for managing shared files
- Update shared types and schemas to include isPublic field
- Register /u/$username route in TanStack Router route tree

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

* test: add integration tests for profile routes and services

- Test GET /api/profiles/:username (404 for missing user, public shares, no-auth)
- Test GET /api/profiles/:username/browse (public folder browsing, access control)
- Test buildBreadcrumb and isPublicPath unit cases
- 20 tests, 95%+ line coverage on profile.ts and profile service

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

* test: add coverage for getProfile, browseProfile, batchUpdateVisibility

Cover the new public profile API functions in src/lib/api.ts to meet
codecov patch thresholds.

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

* ci: trigger test suite for coverage commit

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

* test: add file comment to api.test.ts

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

* test: add pure-logic tests for public profile page and settings

Add unit tests for extractable logic in src/routes/u/$username.tsx
(folder detection, navigation path, breadcrumb, loading/items state)
and src/routes/_authenticated/settings/index.tsx (display name
validation, password match, toggleId set logic, visibility batch
split). Extend vitest coverage include to report on these route files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: exclude React route and component files from codecov

These files cannot be unit-tested without a DOM/jsdom environment.
Pure logic from each component is tested in co-located *.test.ts
files. Excluding them prevents false coverage failures on patch and
project checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: add patch path exclusions for React files in codecov

The patch check must also exclude src/routes and src/components
since these files cannot be measured without a DOM environment.
The project check was already fixed; this fixes the patch check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add integration tests for POST /batch/visibility endpoint

Covers the happy path (set public, set private) and error cases
(invalid input, unauthenticated) for the new batch visibility route,
ensuring patch coverage passes on the new endpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 09:10:35 -04:00
Jasper Van 98059b29b5 feat: add organizationClient plugin to auth-client (#292)
Add organizationClient from better-auth/client/plugins to enable
Organization API access. Export convenience methods for team
management (create, list, invite, remove, update role, set active).
Add Invitation type to shared/types for completeness.

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c
2026-04-14 03:34:14 -04:00
Jasper VanandBob e40ba0785e feat: remove Name field from registration form (#291)
* feat: remove Name field from registration form

- Remove name from signUpSchema; send empty string to satisfy DB NOT NULL
- Reorder sign-up form: Email → Username → Password → Invite Code
- createPersonalOrg falls back to username when name is empty
- Add username to UserWithOrg type (server and frontend), typed as string
- Sidebar and admin users table show username when name is empty
- Update all e2e helpers and specs to remove Name field interactions

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: update signUpSchema tests to match new schema (email, username, password)

Remove name field tests; add username validation tests.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-14 01:19:16 -04:00
Jasper VanandBob bd30514d5b feat(auth): sign-in/sign-up UI overhaul (#286)
* feat(auth): overhaul sign-in/sign-up UI with username login, OAuth buttons, and invite codes

- Sign-in: accept email or username (auto-detected via regex), show OAuth provider buttons
- Sign-up: add username field, conditional invite code field (invite_only mode), closed mode message
- New shared OAuthButtons component fetching enabled providers via Hono RPC
- Extend useSiteOptions hook with authSignupMode
- Add AuthProvider type to shared/types, listAuthProviders API via Hono RPC client
- Add i18n keys for new UI elements (en + zh)
- Hide sign-up link on sign-in page when registration is closed
- Add try/finally to form submissions to prevent stuck loading state

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix(e2e): update E2E tests for new sign-in/sign-up form fields

- Sign-up: fill new Username field, use exact match for Name label
- Sign-in: update label selector from "Email" to "Email or Username"

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix(e2e): update responsive-auth test for new form field labels

The /name/i regex matched both "Username" and "Name" fields, causing
strict mode violations. Use exact label matchers instead.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-13 19:55:00 -04:00
364cbc2aee feat(auth): add registration mode control and email verification (#283)
Enforce signup gating via auth_signup_mode system option (open/invite_only/closed).
Invite codes are atomically redeemed during signup. Email verification is
conditionally enabled when an email provider is configured.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 11:19:54 -04:00
4a5fbb7f4a feat(auth): add dynamic OAuth provider system (#282)
* feat(auth): add dynamic OAuth provider system

Admin can configure OAuth/OIDC providers in the database via API.
All 35 built-in better-auth providers are registered as async functions
that read config from system_options at runtime. Custom OIDC providers
use the genericOAuth plugin with configs loaded at auth init time.

New endpoints:
- GET /api/auth-providers (public, enabled only, no secrets)
- GET /api/auth-providers/admin (admin, all configs, masked secrets)
- PUT /api/auth-providers/admin/:providerId (admin, upsert)
- DELETE /api/auth-providers/admin/:providerId (admin, remove)

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: async createTestApp compat in email and invite test files

createAuth became async in the OAuth PR, which made createTestApp async.
Email and invite code test files need await + Awaited<> type wrappers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 21:46:11 -04:00
Jasper VanandBob 689d9cc74f refactor: remove custom filePath, enforce tenant-isolated storage path (#278)
* refactor: remove custom filePath, enforce tenant-isolated storage path

Replace user-customizable filePath template with a hardcoded
tenant-isolated pattern ($ORG_ID/$UID/$NOW_DATE/$RAND_16KEY$RAW_EXT).
This ensures proper tenant isolation and removes unnecessary complexity.
The DB column is preserved to avoid migration; code simply stops
reading/writing it.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* refactor: clean up dead tokens, locale keys, and simplify path template

Remove unused template tokens ($UUID, $RAW_NAME, $NOW_YEAR, $NOW_MONTH,
$NOW_DAY) and corresponding TemplateVars fields (uuid, rawName) since
the hardcoded template doesn't use them. Remove orphaned i18n keys for
fieldFilePath. Update DB schema default to empty string. Add storage
service unit tests.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-12 14:09:29 -04:00
Jasper VanandClaude Opus 4.6 3b2fe7f9c7 feat(admin): add storage capacity setting to storage form (#275)
Admin can now set the available space for each storage backend with
MB/GB/TB unit selector. 0 means unlimited. Also added capacity/used
fields to the shared Storage type.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 00:11:26 -04:00
saltboandClaude Opus 4.6 310e4950b3 refactor: flatten monorepo to single-package CF Pages Functions structure
Replace pnpm workspace monorepo (packages/server, packages/web, packages/shared)
with a flat single-package layout following Hono's pages-stack pattern. Switch from
pnpm to npm and from Workers+Assets to CF Pages Functions deployment model.

- Move source: packages/server/src/ → server/, packages/web/src/ → src/, packages/shared/src/ → shared/
- Add functions/api/[[route]].ts as CF Pages Functions entry (replaces entry-cloudflare.ts)
- Update 22 import paths: server uses relative, web uses @shared/@server aliases
- Merge three package.json into one, switch to npm
- Update wrangler.toml: remove main/assets (Pages auto-detects functions/ dir)
- Add per-directory tsconfig.json for VS Code type resolution
- Simplify Dockerfile for flat layout
- Fix react-pdf CSS import path (dist/esm/ → dist/)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 00:27:38 -04:00