feat(tenant): single-tenant mode core (config SSOT + server/UI guards + branding)
Add lib/tenant.ts as the single source of truth for multi- vs single-tenant deployment. Gate space creation (server + signup UI), add open-join to the one space, hide the marketing shell, and route brand name + base URL through config. Also commit the _migrations_applied table into the canonical schema so a fresh instance is reproducible from schema.sql alone. - lib/tenant.ts: resolveTenantConfig (pure) + tenantConfig() + appBaseUrl() - lib/auth-actions.ts: createSpace refused when !allowSpaceCreation; joinSpace open-join resolves the configured/sole space with no invite code - proxy.ts: marketing paths redirect to /login when showMarketing is off - app/signup/page.tsx: single-tenant hides create/join chooser, brand=siteName - app/layout.tsx + 4 link builders: brand/base-URL via config (no hackerspace.sh literal) - scripts/schema.sql: public._migrations_applied tracking table - .env.example: documented single-tenant + first-admin bootstrap vars - package.json: pnpm setup
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
deriveSubmitterEmail,
|
||||
} from '@/lib/forms-logic'
|
||||
import { renderFormEmail, formDedupeKey } from '@/lib/notifications-logic'
|
||||
import { appBaseUrl } from '@/lib/tenant'
|
||||
import {
|
||||
enqueueNotification,
|
||||
resolveMemberContact,
|
||||
@@ -193,7 +194,7 @@ export async function submitForm(input: unknown) {
|
||||
new Set(((admins ?? []) as Array<{ member_id: string }>).map(a => a.member_id)),
|
||||
)
|
||||
if (adminMemberIds.length > 0) {
|
||||
const resultsUrl = `${process.env.NEXT_PUBLIC_APP_URL || 'https://hackerspace.sh'}/forms/${form.id}/results`
|
||||
const resultsUrl = `${appBaseUrl()}/forms/${form.id}/results`
|
||||
const submitterLabel = await deriveSubmitterLabel(admin, form.space_id, {
|
||||
linkedMemberId,
|
||||
submitterEmail,
|
||||
|
||||
Reference in New Issue
Block a user