Note: merge from main aborted due to unrelated histories and many conflicts#121
Conversation
Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
ottabase.config.ts as the single user-owned config file
…v vars to ottabase.config.ts Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
ottabase.config.ts as the single user-owned config fileottabase.config.ts as the only user-owned config file
* fix: complete config pipeline for email/authBehavior + add package route guards The shareable monorepo config migration was incomplete in two key areas: 1. Config pipeline gap: email and authBehavior settings defined in ottabase.config.ts were silently ignored because AppConfig, ConfigOptions, createAppConfig(), and userConfigToOptions() all lacked support for these fields. Now the full chain works: ottabase.config.ts → userConfigToOptions → createAppConfig → AppConfig with env var overrides for backward compat. 2. Package route guarding: disabling a package via packages.ottablog: false only removed its DB tables from migrations but left API routes active, causing 500 errors on access. Now all package-specific routes in the worker router check PACKAGES toggles and return a clear 404 with PACKAGE_DISABLED code when the package is off. Additional fixes: - Migrate queue/handlers.ts from raw env vars to worker-config imports - Update bootstrap wizard to reflect EMAIL_FROM moving to ottabase.config.ts - Update email route provider info to reference config instead of env vars - Add DEFAULT_EMAIL_CONFIG, DEFAULT_AUTH_BEHAVIOR_CONFIG constants - Export AUTH_BEHAVIOR_CONFIG and EMAIL_CONFIG from app.config.ts - Add PACKAGES toggle export to worker-config.ts https://claude.ai/code/session_01VrsgTM282BW4TFws7ZfnkE * fix: replace hardcoded app branding with config-driven values Framework files contained 'Ottabase' and 'ottabase-template-app-tanstack' literals that forced users to hand-edit framework code to rebrand — exactly what the shareable monorepo design was meant to avoid. Changes: - log.config.ts: 3 occurrences of hardcoded app name → APP_ID from config - appState.ts: hardcoded appName 'Ottabase' → APP_NAME from config - router.ts: /api/health name + analytics defaultAppId → APP_NAME/APP_ID - BrandFooter.tsx: 'Built with Ottabase' → 'Built with {APP_NAME}' - docs.config.ts: 'Ottabase Docs' → '{APP_NAME} Docs' Now users only edit ottabase.config.ts to rebrand the entire app. https://claude.ai/code/session_01VrsgTM282BW4TFws7ZfnkE * Fix unused imports, restore email provider API backward compat, add authBehavior/email config tests (#124) * fix: address PR review comments - unused imports, email API backward compat, add config tests --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Introduces a single user-owned ottabase.config.ts as the main configuration source for the template app/monorepo, and plumbs that config through @ottabase/config, migrations, and Cloudflare Worker code to reduce reliance on scattered env vars and framework-file edits.
Changes:
- Add
OttabaseUserConfig+ helpers (defineOttabaseConfig,userConfigToOptions) and extend app config withemailandauthBehavior. - Add worker-side build-time config accessor (
worker/lib/worker-config.ts) and update routes/bootstrap/email/auth logic to use it. - Drive package toggles and migration registry behavior off
ottabase.config.ts, and update docs/examples accordingly.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/config/src/types.ts | Adds new config types (EmailConfig, AuthBehaviorConfig) and introduces OttabaseUserConfig shape. |
| packages/config/src/index.ts | Re-exports new types/functions and adds default constants + env key constants. |
| packages/config/src/createAppConfig.ts | Extends generated AppConfig with features.authBehavior and email; adds user-config bridge helpers. |
| packages/config/src/tests/config.test.ts | Adds tests covering the new config helpers and env/default behavior for email/authBehavior. |
| packages/config/README.md | Updates package README to recommend ottabase.config.ts + helper usage. |
| apps/ottabase-template-app-tanstack/worker/routes/router.ts | Reads app identity and package toggles from worker-config; guards package routes when disabled. |
| apps/ottabase-template-app-tanstack/worker/routes/ottaorm-init.ts | Uses configured APP_ID / APP_NAME when seeding defaults. |
| apps/ottabase-template-app-tanstack/worker/routes/email.ts | Switches email “from”/SES region to config-driven values and updates provider notes. |
| apps/ottabase-template-app-tanstack/worker/routes/auth.ts | Switches auth behavior flags and appId usage to config-driven constants. |
| apps/ottabase-template-app-tanstack/worker/lib/worker-config.ts | New build-time accessor for config constants sourced from ottabase.config.ts. |
| apps/ottabase-template-app-tanstack/worker/lib/auth-utils.ts | Updates mailer + auth options to align with config-driven auth behavior and email settings. |
| apps/ottabase-template-app-tanstack/worker/bootstrap/routes.ts | Uses configured app identity in bootstrap seeding/provisioning. |
| apps/ottabase-template-app-tanstack/worker/bootstrap/pages.ts | Updates wizard copy to reflect email settings moving to ottabase.config.ts. |
| apps/ottabase-template-app-tanstack/src/pages/docs/docs.config.ts | Uses configured app name in docs title. |
| apps/ottabase-template-app-tanstack/src/ottabase/state/appState.ts | Uses configured APP_NAME for app state initialization. |
| apps/ottabase-template-app-tanstack/src/ottabase/config/log.config.ts | Uses configured APP_ID for log context fields. |
| apps/ottabase-template-app-tanstack/src/ottabase/config/app.config.ts | Derives app config from ottabase.config.ts via userConfigToOptions(). |
| apps/ottabase-template-app-tanstack/src/ottabase/components/layout/BrandFooter.tsx | Renders configured app name in the footer. |
| apps/ottabase-template-app-tanstack/ottabase/queue/handlers.ts | Uses configured email “from”/SES region instead of env defaults. |
| apps/ottabase-template-app-tanstack/ottabase/config.migrations.ts | Reworks package registry/toggles to be driven by ottabase.config.ts plus a user registry section. |
| apps/ottabase-template-app-tanstack/ottabase.config.ts | New single user-owned config file defining app identity, packages, features, and email settings. |
| apps/ottabase-template-app-tanstack/cloudflare-worker.ts | Skips shortlink fallback handling when shortlinks package is disabled. |
| apps/ottabase-template-app-tanstack/README.md | Adds guidance on customizing/updating via ottabase.config.ts and premium package steps. |
| apps/ottabase-template-app-tanstack/.env.example | Documents which non-secret env vars moved into ottabase.config.ts and keeps secrets/ops vars. |
| const requireVerified = | ||
| env.AUTH_REQUIRE_EMAIL_VERIFIED === 'true' || env.AUTH_REQUIRE_EMAIL_VERIFIED === '1' | ||
| ? true | ||
| : AUTH_REQUIRE_EMAIL_VERIFIED; | ||
| if (requireVerified) { |
There was a problem hiding this comment.
The boolean env-var override logic only ever forces values to true (any other string, including 'false', falls back to the config constant). That means an env var can’t override a true value from ottabase.config.ts back to false, which is surprising given the comment about overrides. Consider parsing true/false/1/0 and only falling back when the env var is unset/invalid.
There was a problem hiding this comment.
Implemented symmetric env overrides (true/false/1/0) via resolveAuthBehavior; env can now flip values in either direction (commit 98eb760).
| export const appConfig = createAppConfig({ | ||
| appName: 'Ottabase Template App (TanStack)', | ||
| appId: 'ottabase-template-app', | ||
| ...userConfigToOptions(typedConfig), | ||
| defaults: { | ||
| meta: { | ||
| author: '@thinkdj', | ||
| description: 'A minimal TanStack + Cloudflare Workers template app in the Ottabase monorepo', | ||
| keywords: | ||
| 'Ottabase, TanStack Router, TanStack Query, Vite, Tailwind, Shadcn, Cloudflare Workers, TypeScript, React', | ||
| companyName: 'Ottabase', | ||
| }, | ||
| uiFramework: 'mantine', | ||
| ui: { | ||
| preventFOUC: false, | ||
| preventFOUCInsideIframe: false, | ||
| debounceMs: 500, | ||
| layout: { | ||
| minWidth: 320, | ||
| maxWidth: 1280, | ||
| }, | ||
| enforceGoogleFonts: true, | ||
| }, | ||
| ...userConfigToOptions(typedConfig).defaults, | ||
| // Merge in the full theme color palette (not settable via env vars) |
There was a problem hiding this comment.
userConfigToOptions(typedConfig) is called twice, creating two separate option objects. This is unnecessary work and makes it easier for future edits to accidentally diverge. Consider computing it once (e.g. const options = userConfigToOptions(typedConfig)) and reusing options / options.defaults in the createAppConfig call.
There was a problem hiding this comment.
userConfigToOptions is now computed once and reused in app.config.ts (commit 98eb760).
| authBehavior: { | ||
| sessionMaxAge: getNumberEnv( | ||
| 'AUTH_SESSION_MAX_AGE', | ||
| defaults.features?.authBehavior?.sessionMaxAge ?? 30 * 24 * 60 * 60, | ||
| ), |
There was a problem hiding this comment.
createAppConfig() now hard-codes defaults for authBehavior and email (e.g. 30 * 24 * 60 * 60, 'noreply@example.com', 'us-east-1') while packages/config/src/index.ts also defines DEFAULT_AUTH_BEHAVIOR_CONFIG and DEFAULT_EMAIL_CONFIG. To avoid drift, consider reusing a single constant source for these defaults (internal or exported).
There was a problem hiding this comment.
createAppConfig now sources auth/email defaults from shared constants (DEFAULT_AUTH_BEHAVIOR_CONFIG/DEFAULT_EMAIL_CONFIG) to avoid drift (commit 98eb760).
| 3. Enable in `ottabase.config.ts`: | ||
| ```typescript | ||
| customPackages: { premiumFeature: { tables: { premiumTable } } }, |
There was a problem hiding this comment.
This example encourages putting premiumTable (a server-only schema object) into ottabase.config.ts. Since ottabase.config.ts is imported by client code in this app, that pattern risks pulling server-only modules into the browser bundle. Consider changing the doc to make ottabase.config.ts only enable the package by key (no table objects), with schemas registered exclusively in ottabase/config.migrations.ts.
| 3. Enable in `ottabase.config.ts`: | |
| ```typescript | |
| customPackages: { premiumFeature: { tables: { premiumTable } } }, | |
| 3. Enable in `ottabase.config.ts` (by key only, no schema imports): | |
| ```typescript | |
| customPackages: { premiumFeature: true }, |
There was a problem hiding this comment.
Updated premium docs to enable packages by key only; schemas stay in config.migrations.ts to avoid bundling server-only code (commit 98eb760).
| // AWS region configured in ottabase.config.ts (non-secret) | ||
| region: EMAIL_SES_REGION, |
There was a problem hiding this comment.
SES region is now always taken from EMAIL_SES_REGION (ottabase.config.ts). If backward-compatible env overrides are still desired, consider preferring env.AWS_REGION when set so existing env-based configs keep working without requiring a redeploy + config change.
| // AWS region configured in ottabase.config.ts (non-secret) | |
| region: EMAIL_SES_REGION, | |
| // Prefer env.AWS_REGION for backward compatibility; fallback to EMAIL_SES_REGION | |
| region: env.AWS_REGION || EMAIL_SES_REGION, |
There was a problem hiding this comment.
SES now prefers env.AWS_REGION for backward compatibility, falling back to config’s EMAIL_SES_REGION (commit 98eb760).
| // This file wires up the built-in PACKAGE_REGISTRY and merges | ||
| // any custom/premium packages declared in `../ottabase.config.ts`. | ||
| // |
There was a problem hiding this comment.
The header comment says custom/premium packages are merged from ../ottabase.config.ts, but the implementation only uses ottabase.config.ts for toggles (keys) and requires schemas to be registered in USER_PACKAGE_REGISTRY. Please update the comment/instructions to match the actual behavior so users don’t try to import table objects into ottabase.config.ts.
There was a problem hiding this comment.
Adjusted config.migrations.ts guidance: customPackages is boolean toggle only, schemas stay in USER_PACKAGE_REGISTRY (commit 98eb760).
| @@ -1,5 +1,6 @@ | |||
| import { describe, expect, it } from 'vitest'; | |||
| import * as config from '../index'; | |||
| import { createAppConfig, defineOttabaseConfig, userConfigToOptions } from '../createAppConfig'; | |||
There was a problem hiding this comment.
These tests import createAppConfig/defineOttabaseConfig/userConfigToOptions from the internal createAppConfig module even though they’re exported from the public package entry (../index). Importing from the public surface better reflects real usage and avoids making tests fail on internal refactors that don’t change the public API.
| import { createAppConfig, defineOttabaseConfig, userConfigToOptions } from '../createAppConfig'; | |
| import { createAppConfig, defineOttabaseConfig, userConfigToOptions } from '../index'; |
There was a problem hiding this comment.
Config tests now import helpers from the public package entry instead of the internal module (commit 98eb760).
| const revokedAt = Math.floor(Date.now() / 1000); | ||
| await env.OBCF_KV.put(userKey('auth', String(user.get('id')), 'revoked'), String(revokedAt), { | ||
| expirationTtl: Number(env.AUTH_SESSION_MAX_AGE) || 30 * 24 * 60 * 60, | ||
| expirationTtl: AUTH_SESSION_MAX_AGE, | ||
| }); |
There was a problem hiding this comment.
expirationTtl is now tied to AUTH_SESSION_MAX_AGE from ottabase.config.ts, but session max age can still be overridden via env in getAuthOptions(). If env overrides are in use, KV TTLs here can diverge from actual session lifetime. Recommend deriving TTL from the same resolved sessionMaxAge used in getAuthOptions(env) (or extracting a shared resolver).
There was a problem hiding this comment.
KV TTLs now use the same resolved sessionMaxAge (with env override support) as getAuthOptions to keep lifetimes aligned (commit 98eb760).
|
|
||
| if (env.AUTH_DISABLE_CREDENTIALS === 'true' || env.AUTH_DISABLE_CREDENTIALS === '1') { | ||
| // AUTH_DISABLE_CREDENTIALS is now configured in ottabase.config.ts (non-secret) | ||
| if (AUTH_DISABLE_CREDENTIALS) { |
There was a problem hiding this comment.
AUTH_DISABLE_CREDENTIALS is now sourced only from ottabase.config.ts, but getAuthOptions() still supports env var overrides. If someone sets AUTH_DISABLE_CREDENTIALS=true via env (backward compat / ops), this route will still allow credentials registration (policy bypass). Consider resolving this flag from env (prefer env override when set) or centralizing the "effective auth behavior" resolution so routes and getAuthOptions() stay consistent.
| if (AUTH_DISABLE_CREDENTIALS) { | |
| // Prefer an env override when present to keep behavior consistent with getAuthOptions(). | |
| const disableCredentialsEnv = (env as any).AUTH_DISABLE_CREDENTIALS; | |
| const effectiveDisableCredentials = | |
| disableCredentialsEnv !== undefined && disableCredentialsEnv !== null | |
| ? String(disableCredentialsEnv).toLowerCase() === 'true' | |
| : AUTH_DISABLE_CREDENTIALS; | |
| if (effectiveDisableCredentials) { |
There was a problem hiding this comment.
Credentials registration now respects env AUTH_DISABLE_CREDENTIALS overrides via resolveAuthBehavior, keeping routes aligned with getAuthOptions (commit 98eb760).
| // EMAIL_FROM is now configured in ottabase.config.ts; env var is no longer needed | ||
| const from = EMAIL_FROM_DEFAULT; |
There was a problem hiding this comment.
resolveMailer() now ignores env.EMAIL_FROM entirely. If keeping env-var overrides for backward compatibility is still a goal, prefer env.EMAIL_FROM when set (fall back to EMAIL_FROM_DEFAULT) so existing deployments don’t silently change sender identity.
| // EMAIL_FROM is now configured in ottabase.config.ts; env var is no longer needed | |
| const from = EMAIL_FROM_DEFAULT; | |
| // Prefer env.EMAIL_FROM for backward compatibility; fall back to config default | |
| const from = env.EMAIL_FROM && env.EMAIL_FROM.trim().length > 0 ? env.EMAIL_FROM : EMAIL_FROM_DEFAULT; |
There was a problem hiding this comment.
resolveMailer now prefers env.EMAIL_FROM for backward compatibility, falling back to config default (commit 98eb760).
…dation (#125) * fix: complete config pipeline for email/authBehavior + add package route guards The shareable monorepo config migration was incomplete in two key areas: 1. Config pipeline gap: email and authBehavior settings defined in ottabase.config.ts were silently ignored because AppConfig, ConfigOptions, createAppConfig(), and userConfigToOptions() all lacked support for these fields. Now the full chain works: ottabase.config.ts → userConfigToOptions → createAppConfig → AppConfig with env var overrides for backward compat. 2. Package route guarding: disabling a package via packages.ottablog: false only removed its DB tables from migrations but left API routes active, causing 500 errors on access. Now all package-specific routes in the worker router check PACKAGES toggles and return a clear 404 with PACKAGE_DISABLED code when the package is off. Additional fixes: - Migrate queue/handlers.ts from raw env vars to worker-config imports - Update bootstrap wizard to reflect EMAIL_FROM moving to ottabase.config.ts - Update email route provider info to reference config instead of env vars - Add DEFAULT_EMAIL_CONFIG, DEFAULT_AUTH_BEHAVIOR_CONFIG constants - Export AUTH_BEHAVIOR_CONFIG and EMAIL_CONFIG from app.config.ts - Add PACKAGES toggle export to worker-config.ts https://claude.ai/code/session_01VrsgTM282BW4TFws7ZfnkE * Fix unused imports, restore email provider API backward compat, add authBehavior/email config tests (#124) * fix: address PR review comments - unused imports, email API backward compat, add config tests * feat: add .example/.template pattern for user-owned files Separate framework-tracked files from user-owned files so `git pull` never overwrites user customisations: - ottabase.config.ts → ottabase.config.example.ts (tracked template) - wrangler.jsonc → wrangler.example.jsonc (tracked; CI uses this) - ottabase/ → ottabase.template/ (tracked reference copy) User working copies (ottabase.config.ts, wrangler.jsonc, ottabase/) are gitignored. First-time setup: copy from .example/.template. Updates CI workflows (deploy.yml, pr-preview.yml), cloudflare-config.json, and README with new setup instructions and directory structure. https://claude.ai/code/session_01VrsgTM282BW4TFws7ZfnkE * feat: add route registration hook for custom/premium packages Custom packages can now register API routes without editing the framework-owned router.ts. Users add handlers in the user-zone file `ottabase/config.routes.ts` (same pattern as config.migrations.ts). - Extract ApiRouteContext to worker/routes/types.ts (shared type) - Add ottabase.template/config.routes.ts with scaffold + docs - Wire handleCustomRoutes() into resolveApiRoute() as fallback after all built-in routes - Update README with route registration in premium package workflow https://claude.ai/code/session_01VrsgTM282BW4TFws7ZfnkE * docs: improve route registration hook documentation - types.ts: add field-level JSDoc on ApiRouteContext with usage example - ottabase.config.example.ts: mention config.routes.ts in premium package steps - config.migrations.ts: cross-reference config.routes.ts in setup steps - README: add dedicated "Custom Routes" section with example + resolution order - README: update File Ownership table to include routes https://claude.ai/code/session_01VrsgTM282BW4TFws7ZfnkE * feat: add runtime config validation to defineOttabaseConfig Typos in ottabase.config.ts no longer silently fall to defaults. defineOttabaseConfig() now validates at startup: - Throws on missing required fields (appId, appName) - Warns on unrecognised keys at all nesting levels (top-level, packages, features, features.authBehavior, email, etc.) Adds validateOttabaseConfig() as a standalone export for testing. 12 new tests covering required fields, typo detection at every nesting level, multiple warnings, and integration with defineOttabaseConfig. https://claude.ai/code/session_01VrsgTM282BW4TFws7ZfnkE * Setup user-zone, dynamic routes & config fixes Add a setup script and npm hooks to ensure user-zone files exist before dev/build. Introduce scripts/setup-user-zone.js which copies ottabase.template → ottabase and wrangler.example.jsonc → wrangler.jsonc when targets are missing, and wire it into package.json (setup, predev, prebuild). Make worker/router load user custom routes via dynamic import with a no-op fallback to avoid crashes when the user-zone is not present. Tighten createAppConfig validation to require non-empty (trimmed) appId and appName. Update tests to centralize console.warn spying with beforeEach/afterEach and adjust import ordering. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Rework Drizzle schema exports to clearly separate core, app, and package tables and expose packageTables via getEnabledPackageTables for config-driven schemas. Enhance setup-user-zone: copy ottabase.config.example.ts by default, merge missing template entries into existing user directories, and add readdirSync helper to support incremental template merges. Update worker utilities: fix import paths for cloudflare-env, move createSecureToken import, add PACKAGES-driven conditional model registration in db-utils to only register package models when enabled, and correct imports in rate-limiting. Update tests and mocks (app name and extra mocked config fields) and extend tsconfig include paths to cover ottabase and worker files for typechecking.
…vider handling (#126) * Initial plan * chore: sync with main updates Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com> * chore: address review feedback Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com> * fix: harden map url detection Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
|
@copilot 1. Resolve conflicts with main branch for this PR (branch: copilot/make-monorepo-flexible) |
Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
Co-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
ottabase.config.ts as the only user-owned config fileCo-authored-by: thinkdj <688055+thinkdj@users.noreply.github.com>
The task was to pull
mainand resolve conflicts; attemptinggit pull origin mainfailed with “unrelated histories”. A subsequent merge of fetchedmainsurfaced dozens of conflicts across workflows, app code, packages, and lockfile, so the merge was aborted to keep the branch clean.mainrequires unrelated-histories handling and conflict resolution across many files (workflows, app UI, worker routes, packages, pnpm-lock).Example of the blocking error:
git pull origin main # fatal: refusing to merge unrelated histories💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.