chore(oss): audit the console package and the Helm chart

Add the dist/migrate.js entrypoint the Helm job already invoked and point
Compose at the same runner, so one implementation applies
packages/server/migrations in both paths. Rewrite the console README to
the standalone Docker build it actually uses and fix the chart
description.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
This commit is contained in:
雷电芽衣 committed 2026-09-03 20:53:18 -04:00
1 parent bc6983a62e
commit f69350d709
7 files changed
+76 -34

No files matched your search

+5 -20
View File
@@ -77,29 +77,14 @@ services:
# DB migrate: applies packages/server/migrations # DB migrate: applies packages/server/migrations
# ============================================================ # ============================================================
db-migrate: db-migrate:
image: postgres:16-alpine build:
context: ./packages/server
dockerfile: Dockerfile
command: ["node", "dist/migrate.js"]
env_file: .env
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy
environment:
PGPASSWORD: ${POSTGRES_PASSWORD:-elydora-secret-change-me}
volumes:
- ./packages/server/migrations:/migrations:ro,z
entrypoint: ["sh", "-c"]
command:
- |
set -e
PSQL="psql -h postgres -U ${POSTGRES_USER:-elydora} -d ${POSTGRES_DB:-elydora}"
for file in /migrations/*.sql; do
version=$$(basename "$$file" | cut -d_ -f1 | sed 's/^0*//')
applied=$$($$PSQL -tAX -c "SELECT 1 FROM schema_versions WHERE version = $$version" 2>/dev/null || true)
if [ "$$applied" = "1" ]; then
echo "Skipping $$file (already applied)"
continue
fi
echo "Applying $$file"
$$PSQL -v ON_ERROR_STOP=1 -f "$$file"
done
api: api:
build: build:
+1 -1
View File
@@ -1,6 +1,6 @@
apiVersion: v2 apiVersion: v2
name: elydora name: elydora
description: Elydora — Open-source AI accountability and compliance platform description: Open-source AI accountability and compliance platform for Elydora
type: application type: application
version: 0.1.0 version: 0.1.0
appVersion: "0.1.0" appVersion: "0.1.0"
+18 -10
View File
@@ -8,7 +8,8 @@ Web dashboard for the Elydora Responsibility Protocol. Provides agent management
- **Language:** TypeScript 5.7 - **Language:** TypeScript 5.7
- **UI:** React 19, Tailwind CSS 4 - **UI:** React 19, Tailwind CSS 4
- **Data Fetching:** SWR 2.3 - **Data Fetching:** SWR 2.3
- **Deployment:** Cloudflare Pages (static export) - **i18n:** i18next with English and Simplified Chinese
- **Deployment:** Docker image running the Next.js standalone server
## Project Structure ## Project Structure
@@ -16,7 +17,7 @@ Web dashboard for the Elydora Responsibility Protocol. Provides agent management
src/ src/
├── app/ ├── app/
│ ├── layout.tsx # Root layout with AppShell │ ├── layout.tsx # Root layout with AppShell
│ ├── page.tsx # Dashboard — stats, recent operations, quick actions │ ├── page.tsx # Dashboard: stats, recent operations, quick actions
│ ├── globals.css # Theme variables, global styles │ ├── globals.css # Theme variables, global styles
│ ├── agents/ │ ├── agents/
│ │ ├── page.tsx # Agent list, search, registration │ │ ├── page.tsx # Agent list, search, registration
@@ -33,6 +34,8 @@ src/
│ │ └── page.tsx # Audit log query builder │ │ └── page.tsx # Audit log query builder
│ ├── login/page.tsx # Login form │ ├── login/page.tsx # Login form
│ ├── register/page.tsx # Registration form │ ├── register/page.tsx # Registration form
│ ├── settings/ # Organization settings
│ ├── global-error.tsx # Root error boundary
│ └── jwks/page.tsx # Public key display │ └── jwks/page.tsx # Public key display
├── components/ ├── components/
│ ├── AppShell.tsx # Main layout wrapper with sidebar │ ├── AppShell.tsx # Main layout wrapper with sidebar
@@ -43,6 +46,8 @@ src/
│ ├── EpochDetailClient.tsx # Epoch proof and verification │ ├── EpochDetailClient.tsx # Epoch proof and verification
│ ├── ChainVisualization.tsx # Merkle chain visualization │ ├── ChainVisualization.tsx # Merkle chain visualization
│ ├── VerificationChecklist.tsx # Proof verification UI │ ├── VerificationChecklist.tsx # Proof verification UI
│ ├── I18nProvider.tsx # Locale provider
│ ├── agent-registration/ # Registration flow components
│ └── ui/ │ └── ui/
│ ├── PageHeader.tsx # Page title and breadcrumbs │ ├── PageHeader.tsx # Page title and breadcrumbs
│ ├── DataTable.tsx # Sortable, paginated table │ ├── DataTable.tsx # Sortable, paginated table
@@ -50,12 +55,17 @@ src/
│ ├── SearchInput.tsx # Search/filter input │ ├── SearchInput.tsx # Search/filter input
│ ├── StatusBadge.tsx # Status indicator │ ├── StatusBadge.tsx # Status indicator
│ ├── CopyButton.tsx # Copy-to-clipboard │ ├── CopyButton.tsx # Copy-to-clipboard
│ ├── LanguageDropdown.tsx # Locale switcher
│ └── Sidebar.tsx # Navigation sidebar │ └── Sidebar.tsx # Navigation sidebar
├── features/
│ └── agent-registration/ # Install instructions and credential handling
├── i18n/ # en and zh message catalogs
├── lib/ ├── lib/
│ ├── api.ts # Typed REST client for all endpoints │ ├── api.ts # Typed REST client for all endpoints
│ ├── auth.tsx # Auth context and hooks │ ├── auth.tsx # Auth context and hooks
│ └── hooks.ts # SWR data-fetching hooks │ └── hooks.ts # SWR data-fetching hooks
└── shared/ # Shared types (@elydora/shared) └── shared/ # Shared types (@elydora/shared)
e2e/ # Playwright specs
``` ```
## Getting Started ## Getting Started
@@ -90,20 +100,17 @@ Runs at `http://localhost:3000` with Turbopack.
npm run build npm run build
``` ```
Produces a static export in `out/`. A post-build script removes stale `_redirects` files. Produces a Next.js standalone server in `.next/standalone`.
### Deployment ### Deployment
```bash The root `docker-compose.yml` builds this package with `packages/console/Dockerfile` and serves it on port 3000. The image runs `node server.js` from the standalone output.
npm run deploy
```
Deploys the `out/` directory to Cloudflare Pages. ### Checks
### Type Checking
```bash ```bash
npm run typecheck npm run typecheck # tsc --noEmit
npm run test:e2e # Playwright specs in e2e/
``` ```
## Pages ## Pages
@@ -122,3 +129,4 @@ npm run typecheck
| `/login` | Login | | `/login` | Login |
| `/register` | Register account and organization | | `/register` | Register account and organization |
| `/jwks` | Display server public keys in JWK format | | `/jwks` | Display server public keys in JWK format |
| `/settings` | Organization settings |
@@ -93,7 +93,7 @@ export default function OperationDetailCard({ operation, receipt, payload }: Ope
)} )}
</div> </div>
{/* Payload — what the operation actually did */} {/* Payload: what the operation did */}
{payload && ( {payload && (
<div className="border border-border bg-surface p-4 sm:p-6 relative"> <div className="border border-border bg-surface p-4 sm:p-6 relative">
<span className="crosshair ch-tl" /> <span className="crosshair ch-tl" />
+1 -1
View File
@@ -45,7 +45,7 @@ export default function Modal({
}; };
}, [isOpen]); }, [isOpen]);
// Keydown listener — uses ref so it never changes identity // Keydown listener uses a ref for stable identity
useEffect(() => { useEffect(() => {
if (!isOpen) return; if (!isOpen) return;
@@ -135,7 +135,7 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
return ( return (
<> <>
{/* Backdrop overlay — mobile only */} {/* Mobile backdrop overlay */}
{isOpen && ( {isOpen && (
<div <div
className="fixed inset-0 bg-black/50 z-40 md:hidden" className="fixed inset-0 bg-black/50 z-40 md:hidden"
+49
View File
@@ -0,0 +1,49 @@
import { readdir, readFile } from 'node:fs/promises';
import { join } from 'node:path';
import pg from 'pg';
const MIGRATIONS_DIR = process.env.MIGRATIONS_DIR ?? join(process.cwd(), 'migrations');
/** Leading digits of a migration filename, e.g. 004_x.sql becomes 4. */
function versionOf(file: string): number {
const match = /^(\d+)_/.exec(file);
if (!match) throw new Error(`Migration ${file} does not start with a version number.`);
return Number(match[1]);
}
async function appliedVersions(client: pg.Client): Promise<Set<number>> {
const exists = await client.query("SELECT to_regclass('schema_versions') AS name");
if (!exists.rows[0]?.name) return new Set();
const rows = await client.query<{ version: string }>('SELECT version FROM schema_versions');
return new Set(rows.rows.map((row) => Number(row.version)));
}
async function main(): Promise<void> {
const connectionString = process.env.DATABASE_URL;
if (!connectionString) throw new Error('DATABASE_URL is required.');
const files = (await readdir(MIGRATIONS_DIR))
.filter((file) => file.endsWith('.sql'))
.sort((a, b) => versionOf(a) - versionOf(b));
const client = new pg.Client({ connectionString });
await client.connect();
try {
const applied = await appliedVersions(client);
for (const file of files) {
if (applied.has(versionOf(file))) {
console.log(`Skipping ${file} (already applied)`);
continue;
}
console.log(`Applying ${file}`);
await client.query(await readFile(join(MIGRATIONS_DIR, file), 'utf8'));
}
} finally {
await client.end();
}
}
main().catch((error) => {
console.error('migration failed:', error);
process.exit(1);
});