Commit graph

21 commits

Author SHA1 Message Date
Till JS
8fe16b20f4 feat(infra): Phase 5 — consolidate to single web container
Remove 20 standalone web containers, simplify tunnel and auth config:

docker-compose.macmini.yml (-579 lines):
- Remove chat-web, todo-web, calendar-web, clock-web, contacts-web,
  zitare-web, storage-web, presi-web, cards-web, nutriphi-web,
  skilltree-web, photos-web, mukke-web, citycorners-web, picture-web,
  inventar-web, calc-web, times-web, uload-web, memoro-web
- Keep: mana-web (unified), element-web, matrix-web, arcade-web, manavoxel-web
- Update mana-web with all backend API URLs, increase mem_limit to 256m

cloudflared-config.yml (-60 lines):
- Remove all *.mana.how web subdomains (now served at mana.how/*)
- Keep backend API subdomains (*-api.mana.how)

mana-auth trustedOrigins (30 → 8 origins):
- Only mana.how + games/matrix subdomains that remain separate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:17:38 +02:00
Till JS
cb85fba820 feat(todo/web, shared-i18n): complete i18n for Todo web app + add missing common translations
Extract ~120 hardcoded German strings from 14 Svelte components into i18n locale
files using svelte-i18n $t() calls. Add new translation sections (taskForm, filters,
tags, subtasks, durationPicker, kanban, toolbar) across all 5 languages (de/en/fr/es/it).

Also add missing shared common translations for Spanish, French, and Italian
(150+ keys each) in packages/shared-i18n.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 14:19:48 +02:00
Till JS
75a3ea2957 refactor: rename ManaDeck to Cards across entire monorepo
Rename the flashcard/deck management app from ManaDeck to Cards:
- Directory: apps/manadeck → apps/cards, packages/manadeck-database → packages/cards-database
- Packages: @manadeck/* → @cards/*, @manacore/manadeck-database → @manacore/cards-database
- Domain: manadeck.mana.how → cards.mana.how
- Storage: manadeck-storage → cards-storage
- Database: manadeck → cards
- All shared packages, infra configs, services, i18n, and docs updated
- 244 files changed, zero remaining manadeck references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:45:21 +02:00
Till JS
c6448a63bc fix(mana-auth): avoid error.body access in login catch — triggers async stream read
Accessing (error as any)?.body?.code on a Better Auth APIError triggers an internal
async stream read. When the request body contains special chars like '!', the deferred
JSON parse fails as an unhandled rejection that races with the response, causing 500.

Use only error.status === 'FORBIDDEN' which is a simple string property.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 21:41:06 +02:00
Till JS
259253e7b3 feat(auth): show resend verification panel when registering with existing unverified email
- auth.ts: catch USER_ALREADY_EXISTS and return EMAIL_ALREADY_REGISTERED (409)
- authService: map 409 with EMAIL_ALREADY_REGISTERED code to typed error
- RegisterPage: show amber warning panel + resend + go-to-login for existing emails
- translations: add emailAlreadyRegistered, emailAlreadyRegisteredMessage, goToLogin (en/de)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:44:01 +02:00
Till JS
d23ef52839 fix(mana-auth): set callbackURL instead of redirectTo for email verification redirect
Better Auth uses callbackURL to determine the post-verification redirect target.
Setting only redirectTo left callbackURL=/ which resolved to auth.mana.how/ (404).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:34:56 +02:00
Till JS
bdf76cb24d fix(mana-auth): remove debug log, finalize EMAIL_NOT_VERIFIED detection
APIError.status is string 'FORBIDDEN', body.code is 'EMAIL_NOT_VERIFIED'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:07:57 +02:00
Till JS
504f77a60c debug: log login error shape 2026-03-31 18:05:41 +02:00
Till JS
36922cc946 fix(mana-auth): robust email-not-verified detection
Better Auth throws APIError.from(FORBIDDEN, EMAIL_NOT_VERIFIED).
Check status 403, body.code, and lowercased message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:01:24 +02:00
Till JS
b1af506b99 fix(auth): surface email-not-verified error and detect needsVerification on signup
- mana-auth login route: catch Better Auth's email verification error and
  return 403 EMAIL_NOT_VERIFIED instead of 401 Invalid credentials
- shared-auth signUp: detect emailVerified:false in register response and
  return needsVerification:true so the UI shows the verification prompt
- shared-auth-ui LoginPage: map INVALID_CREDENTIALS error code to friendly message
- shared-i18n: add invalidCredentials translation (de/en)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:52:47 +02:00
Till JS
14df2cd9e2 fix(auth): declare accessTier as additionalField so Better Auth includes it in user object
Without this, Better Auth's definePayload receives a user object
without the custom accessTier column, causing the JWT tier claim
to always default to 'public'.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 12:46:29 +02:00
Till JS
8ffd2ce774 fix(mana-auth): add session-to-token endpoint and return JWT from login
The client (shared-auth) calls /api/v1/auth/session-to-token for SSO and
2FA flows, but this endpoint was never implemented. Also, the login endpoint
returned raw Better Auth session data instead of the expected
{ accessToken, refreshToken } format.

- Add POST /api/v1/auth/session-to-token endpoint
- Fix login to generate JWT via Better Auth's /api/auth/token
- Fix refresh to return JWT instead of raw session data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:56:35 +02:00
Till JS
0d6005dbcc fix(inventar): import FeedbackPage from @manacore/feedback, not shared-ui
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:56:19 +02:00
Till JS
b737240ec1 feat(auth): add access tier system for phased app releases
Introduces a tiered access control system so apps can be released
gradually (founder → alpha → beta → public) without extra infrastructure.
Users are gated at the AuthGate level based on their tier vs the app's
requiredTier. All apps remain deployed and reachable, but only users
with sufficient tier can enter.

- Add accessTier enum + column to users schema (default: 'public')
- Add tier claim to JWT payload in better-auth config
- Add requiredTier field to ManaApp interface + all 25 apps
- Add hasAppAccess(), getAccessibleManaApps(), ACCESS_TIER_LABELS
- Update AuthGate with tier check + access denied screen
- Update getPillAppItems + Home page to filter by user tier
- Update all 22 app layouts to pass user tier to PillNav
- Add admin API: GET/PUT /api/v1/admin/users/:id/tier
- Document access tier system in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:50:06 +02:00
Till JS
c33339b0cf rename(taktik): rebrand to Times
Rename taktik → times across the entire app: package names (@taktik →
@times), appId, localStorage keys, export filenames, type names
(TaktikSettings → TimesSettings), monorepo scripts, shared-branding,
mana-auth trustedOrigins, docker-compose, and documentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 15:44:18 +02:00
Till JS
bc428729b0 deploy(taktik): add Dockerfile, docker-compose, SSO config
- Dockerfile based on sveltekit-base pattern (port 5027)
- docker-compose.macmini.yml: taktik-web service with auth + sync
- mana-auth: added taktik.mana.how to trustedOrigins
- CORS_ORIGINS: added taktik.mana.how

Remaining manual steps on Mac Mini:
- cloudflared tunnel route: taktik.mana.how -> localhost:5027
- ./scripts/mac-mini/build-app.sh taktik-web

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 08:55:30 +02:00
Till JS
cb549776ac deploy(calc): add docker-compose, cloudflared tunnel, SSO config
- docker-compose.macmini.yml: calc-web service on port 5026
- cloudflared-config.yml: calc.mana.how → localhost:5026
- mana-auth trusted origins: add https://calc.mana.how
- mana-credits & mana-user CORS: add https://calc.mana.how
- Dockerfile port updated from 5018 to 5026 (5018 used by zitare)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 08:53:41 +02:00
Till JS
9276d9a212 feat: GPU offload, signup limit, load tests & capacity planning
- Route all AI workloads (Ollama, STT, TTS, Image Gen) to GPU server
  (192.168.178.11) via LAN instead of host.docker.internal
- Upgrade default model to gemma3:12b and max concurrent to 5
- Add daily signup limit service (MAX_DAILY_SIGNUPS env var)
- Add GET /api/v1/auth/signup-status public endpoint
- Add k6 load test suite (web-apps, auth, sync-websocket, ollama)
- Add capacity planning documentation
- Fix: add eslint-config to sveltekit-base and calendar Dockerfiles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 21:14:24 +01:00
Till JS
09ccf32091 fix(mana-auth): fix schema import paths (.schema → .ts)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 18:17:57 +01:00
Till JS
4318948980 feat(mana-auth): add guilds, api-keys, me, security, auth routes
Complete the mana-auth Hono service with all remaining endpoints
from mana-core-auth.

Added:
- routes/auth.ts: Full auth flow (register, login, logout, validate,
  password reset, profile, change-password, account deletion,
  security events) with lockout + security event logging
- routes/guilds.ts: Guild CRUD, member management, invitations
  (delegates to Better Auth org plugin + mana-credits for pools)
- routes/api-keys.ts: API key generation, listing, revocation,
  validation (sk_live_* format, SHA-256 hashed)
- routes/me.ts: GDPR data export/delete (Articles 17 & 20)
- services/security.ts: SecurityEventsService (fire-and-forget audit)
  + AccountLockoutService (5 failures/15min → 30min lockout)
- services/api-keys.ts: Key generation, validation, scope checks

Updated:
- index.ts: Wire all routes with proper middleware (JWT, service auth)

Service now has ~1,900 LOC covering all functionality from the
original ~11,500 LOC NestJS mana-core-auth (83% reduction).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 02:57:22 +01:00
Till JS
61ee1ae269 feat(services): create mana-auth (Hono + Bun) — Phase 5 auth rewrite
Rewrite the central authentication service from NestJS to Hono + Bun.
Uses Better Auth's native fetch-based handler — no Express conversion.

Key architecture changes:
- Better Auth handler mounted directly on Hono (app.all('/api/auth/*'))
- No NestJS DI, modules, guards, decorators — plain TypeScript
- JWT validation via jose (same as extracted services)
- Email via nodemailer (simplified, German templates)
- ~1,400 LOC vs ~11,500 LOC in NestJS (88% reduction)

Service structure:
- auth/better-auth.config.ts — copied from mana-core-auth (framework-agnostic)
- auth/stores.ts — in-memory stores for email redirect URLs
- email/send.ts — nodemailer email functions
- middleware/ — JWT auth, service auth, error handler (shared pattern)
- db/schema/ — copied from mana-core-auth (Drizzle, framework-agnostic)

Port: 3001 (same as mana-core-auth — drop-in replacement)
Database: mana_auth (same DB, same schemas)

Better Auth plugins: Organization, JWT (EdDSA), OIDC Provider,
Two-Factor (TOTP), Magic Link

Note: This is the initial version. Guilds, API keys, Me (GDPR),
security (lockout/audit), and admin endpoints will be added
incrementally. The old mana-core-auth remains until fully replaced.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 02:43:44 +01:00