| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import 'server-only'; |
|
|
| import path from 'path'; |
| import { StorageAdapter } from './types'; |
| import { SQLiteAdapter } from './sqlite-adapter'; |
|
|
| |
| let defaultAdapter: SQLiteAdapter | null = null; |
|
|
| |
| const workspaceAdapters = new Map<string, { adapter: SQLiteAdapter; lastAccess: number }>(); |
|
|
| |
| const IDLE_TIMEOUT_MS = 10 * 60 * 1000; |
|
|
| function getDataDir(): string { |
| return process.env.DATA_DIR || path.join(process.cwd(), 'data'); |
| } |
|
|
| |
| |
| |
| function validateWorkspaceId(workspaceId: string): void { |
| if (!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(workspaceId)) { |
| throw new Error(`Invalid workspace ID format: ${workspaceId}`); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| export function getWorkspaceAdapter(workspaceId: string): SQLiteAdapter { |
| |
| if (workspaceId === 'default' || workspaceId === 'admin' || workspaceId === 'desktop' || workspaceId === 'instance-api') { |
| return getSQLiteAdapter(); |
| } |
|
|
| validateWorkspaceId(workspaceId); |
|
|
| const cached = workspaceAdapters.get(workspaceId); |
| if (cached) { |
| cached.lastAccess = Date.now(); |
| return cached.adapter; |
| } |
|
|
| const dbPath = path.join(getDataDir(), 'workspaces', workspaceId, 'osws.sqlite'); |
| const adapter = new SQLiteAdapter(dbPath); |
| workspaceAdapters.set(workspaceId, { adapter, lastAccess: Date.now() }); |
| return adapter; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function getSQLiteAdapter(): SQLiteAdapter { |
| if (!defaultAdapter) { |
| defaultAdapter = new SQLiteAdapter(); |
| } |
| return defaultAdapter; |
| } |
|
|
| |
| |
| |
| |
| export async function createServerAdapter(): Promise<StorageAdapter> { |
| return getSQLiteAdapter(); |
| } |
|
|
| |
| const CLEANUP_KEY = '__osw_workspace_cleanup'; |
| if (typeof setInterval !== 'undefined' && !(globalThis as Record<string, unknown>)[CLEANUP_KEY]) { |
| (globalThis as Record<string, unknown>)[CLEANUP_KEY] = setInterval(() => { |
| const now = Date.now(); |
| for (const [workspaceId, entry] of workspaceAdapters) { |
| if (now - entry.lastAccess > IDLE_TIMEOUT_MS) { |
| entry.adapter.close?.(); |
| workspaceAdapters.delete(workspaceId); |
| } |
| } |
| }, 60_000); |
| } |
|
|