Initial implementation: multi-tenant Kanban board (Phase 1)
Complete Phase 1 foundation with working board, column, and card CRUD: - SvelteKit + TypeScript + Tailwind CSS v4 + adapter-node - Full Prisma schema with 18 tables (tenants, users, boards, columns, cards, etc.) - Multi-tenant architecture with hostname-based tenant resolution - Email/password auth with bcrypt + session cookies - Board/Column/Card API routes with full CRUD - Fractional indexing for drag-and-drop ordering - Board view with svelte-dnd-action for column and card drag-and-drop - Card modal with inline editing - Auth pages (login, register) - Board listing with create form - RLS policies SQL script for tenant isolation - Prisma RLS client extensions (forTenant/bypassRLS) - Permission helpers (canEditBoard, canManageMembers, etc.) - Socket.IO server setup (dev Vite plugin + production Express server) - Client-side socket store for real-time updates - Docker Compose (app + PostgreSQL 16) + multi-stage Dockerfile - Database seed script (default tenant + admin user) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { prisma } from './db.js';
|
||||
|
||||
const tenantCache = new Map<string, { tenant: TenantInfo; expiresAt: number }>();
|
||||
const CACHE_TTL = 60_000; // 1 minute
|
||||
|
||||
export interface TenantInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export async function resolveTenant(hostname: string): Promise<TenantInfo | null> {
|
||||
// Strip port from hostname
|
||||
const host = hostname.split(':')[0];
|
||||
|
||||
// Check cache
|
||||
const cached = tenantCache.get(host);
|
||||
if (cached && cached.expiresAt > Date.now()) {
|
||||
return cached.tenant;
|
||||
}
|
||||
|
||||
const record = await prisma.tenantHostname.findUnique({
|
||||
where: { hostname: host },
|
||||
include: { tenant: true }
|
||||
});
|
||||
|
||||
if (!record) return null;
|
||||
|
||||
const tenant: TenantInfo = {
|
||||
id: record.tenant.id,
|
||||
name: record.tenant.name,
|
||||
slug: record.tenant.slug
|
||||
};
|
||||
|
||||
tenantCache.set(host, { tenant, expiresAt: Date.now() + CACHE_TTL });
|
||||
return tenant;
|
||||
}
|
||||
|
||||
export function clearTenantCache() {
|
||||
tenantCache.clear();
|
||||
}
|
||||
Reference in New Issue
Block a user