This commit is contained in:
2026-02-20 18:25:33 +00:00
parent ef1530b626
commit 25f5f74b1b
29 changed files with 1560 additions and 201 deletions
+128
View File
@@ -0,0 +1,128 @@
import { mkdir, readdir, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { createRouter } from '../../create-router';
import { getUserProjectsDir } from '@@/data-path';
export const projectsRouter = createRouter();
type KeyMapping = { file: string; dir?: string };
function resolveKey(projDir: string, key: string): KeyMapping | null {
if (key === 'projects') return { file: join(projDir, 'index.json') };
const layoutMatch = key.match(/^proj-layout-(.+)$/);
if (layoutMatch) {
const id = layoutMatch[1]!;
const dir = join(projDir, id);
return { file: join(dir, 'layout.json'), dir };
}
const terminalsMatch = key.match(/^proj-terminals-(.+)$/);
if (terminalsMatch) {
const id = terminalsMatch[1]!;
const dir = join(projDir, id);
return { file: join(dir, 'terminals.json'), dir };
}
const hostTerminalsMatch = key.match(/^proj-host-terminals-(.+)$/);
if (hostTerminalsMatch) {
const id = hostTerminalsMatch[1]!;
const dir = join(projDir, id);
return { file: join(dir, 'host-terminals.json'), dir };
}
return null;
}
async function readJsonFile(path: string): Promise<unknown | null> {
const file = Bun.file(path);
if (await file.exists()) return file.json();
return null;
}
async function writeJsonFile(path: string, data: unknown) {
await Bun.write(path, JSON.stringify(data, null, 2));
}
async function readProjectDir(dirPath: string, id: string, result: Record<string, unknown>) {
const layout = await readJsonFile(join(dirPath, 'layout.json'));
if (layout !== null) result[`proj-layout-${id}`] = layout;
const terminals = await readJsonFile(join(dirPath, 'terminals.json'));
if (terminals !== null) result[`proj-terminals-${id}`] = terminals;
const hostTerminals = await readJsonFile(join(dirPath, 'host-terminals.json'));
if (hostTerminals !== null) result[`proj-host-terminals-${id}`] = hostTerminals;
}
async function readAllProjectsState(projDir: string): Promise<Record<string, unknown>> {
const result: Record<string, unknown> = {};
const indexData = await readJsonFile(join(projDir, 'index.json'));
if (indexData !== null) result['projects'] = indexData;
let entries: import('node:fs').Dirent[] = [];
try {
entries = await readdir(projDir, { withFileTypes: true });
} catch {
return result;
}
for (const entry of entries) {
if (!entry.isDirectory()) continue;
await readProjectDir(join(projDir, entry.name), entry.name, result);
}
return result;
}
// GET /projects-state
projectsRouter.get('/projects-state', async (ctx) => {
const email = ctx.get('user').email;
const projDir = getUserProjectsDir(email);
const state = await readAllProjectsState(projDir);
return ctx.json(state);
});
// PATCH /projects-state
projectsRouter.patch('/projects-state', async (ctx) => {
const email = ctx.get('user').email;
const body = ctx.get('body') as Record<string, unknown>;
const projDir = getUserProjectsDir(email);
await mkdir(projDir, { recursive: true });
for (const [key, value] of Object.entries(body)) {
// When writing the projects list, resolve cwds and create project dirs
if (key === 'projects' && Array.isArray(value)) {
for (const project of value) {
const projectFilesDir = join(projDir, project.id, 'files');
project.cwd = projectFilesDir;
await mkdir(projectFilesDir, { recursive: true });
}
}
const mapping = resolveKey(projDir, key);
if (!mapping) continue;
if (value === null) {
try {
await rm(mapping.file, { force: true });
if (mapping.dir) {
const remaining = await readdir(mapping.dir);
if (remaining.length === 0) await rm(mapping.dir, { recursive: true, force: true });
}
} catch {
// ignore
}
continue;
}
if (mapping.dir) await mkdir(mapping.dir, { recursive: true });
await writeJsonFile(mapping.file, value);
}
const state = await readAllProjectsState(projDir);
return ctx.json(state);
});