mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
feat: public platform, admin console, templates, landing docs
Projects become publicly browsable after admin approval; management and secret-adjacent reads (logs, variables, exec, connection URLs) require owner/member/admin auth, and platform controls sit behind RequireAdmin. - Backend: OptionalAuth middleware, read/authed/admin route groups, projects.is_approved + service_templates.owner_id migration, visibility-aware sqlc queries, admin overview/users/approval endpoints, EnsureAdminAccount bootstrap (ADMIN_* env), 30 seeded templates with owner CRUD, managed-database connection URLs gated owner+admin, fix DB.Stats() infinite recursion. - Frontend: expandable persisted sidebar + clickable user menu, public landing page with searchable docs browser (GitHub sync, bundled snapshot + localStorage fallback), DocsBrowser shared with /docs, template create/edit/JSON-upload for owned templates, database create dialog, admin console (stats, approvals, project edit/delete, user admin toggles), HA/Security/Usage polish with dead CSS tokens mapped to real theme vars, anonymous-safe gating on every mutation control. - Tests: safe window.localStorage access fixes happy-dom crashes in PlatformShell and canvas storage. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
8f5789bf93
commit
8b6214a0f5
62 files changed
+4986
-1133
No files matched your search
@@ -0,0 +1,55 @@
|
||||
// Bundles the repository docs/ tree into a generated TS module so the docs
|
||||
// browser renders instantly and offline. The GitHub sync layer refreshes it
|
||||
// at runtime when network is available.
|
||||
import { readdirSync, readFileSync, statSync, writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { join, relative } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = join(fileURLToPath(new URL('.', import.meta.url)), '..', '..', '..');
|
||||
const docsDir = join(root, 'docs');
|
||||
const outFile = join(root, 'app/frontend/src/generated/docs-snapshot.ts');
|
||||
|
||||
// Directories that are internal working notes rather than user documentation.
|
||||
const SKIP_DIRS = new Set(['archive', 'design', 'superpowers']);
|
||||
|
||||
function walk(dir) {
|
||||
const entries = [];
|
||||
for (const name of readdirSync(dir)) {
|
||||
const full = join(dir, name);
|
||||
const stat = statSync(full);
|
||||
if (stat.isDirectory()) {
|
||||
if (!SKIP_DIRS.has(name)) entries.push(...walk(full));
|
||||
} else if (name.toLowerCase().endsWith('.md')) {
|
||||
entries.push(full);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
function titleOf(markdown, fallback) {
|
||||
const match = markdown.match(/^#\s+(.+)$/m);
|
||||
if (match) return match[1].replace(/[#*`]/g, '').trim();
|
||||
return fallback
|
||||
.replace(/\.md$/i, '')
|
||||
.replace(/[-_]+/g, ' ')
|
||||
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
const files = walk(docsDir).sort();
|
||||
const docs = files.map((full) => {
|
||||
const rel = relative(docsDir, full).replace(/\\/g, '/');
|
||||
const markdown = readFileSync(full, 'utf8');
|
||||
const section = rel.includes('/') ? rel.split('/')[0] : 'docs';
|
||||
return {
|
||||
path: rel,
|
||||
section,
|
||||
title: titleOf(markdown, rel.split('/').pop() ?? rel),
|
||||
markdown,
|
||||
};
|
||||
});
|
||||
|
||||
const banner = `/**\n * Auto-generated by scripts/generate-docs-snapshot.mjs.\n * Bundled copy of the repository docs tree — the offline fallback for the\n * docs browser. Regenerate with \`npm run generate:docs\`.\n */\n\nexport type BundledDoc = {\n path: string;\n section: string;\n title: string;\n markdown: string;\n};\n\n`;
|
||||
|
||||
mkdirSync(join(outFile, '..'), { recursive: true });
|
||||
writeFileSync(outFile, `${banner}export const bundledDocs: BundledDoc[] = ${JSON.stringify(docs, null, 2)};\n`);
|
||||
console.log(`docs snapshot: ${docs.length} files → ${relative(root, outFile)}`);
|
||||
Reference in new issue
Block a user