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:
1 parent
bc6983a62e
commit
f69350d709
7 files changed
+76
-34
No files matched your search
+5
-20
@@ -77,29 +77,14 @@ services:
|
||||
# DB migrate: applies packages/server/migrations
|
||||
# ============================================================
|
||||
db-migrate:
|
||||
image: postgres:16-alpine
|
||||
build:
|
||||
context: ./packages/server
|
||||
dockerfile: Dockerfile
|
||||
command: ["node", "dist/migrate.js"]
|
||||
env_file: .env
|
||||
depends_on:
|
||||
postgres:
|
||||
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:
|
||||
build:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: elydora
|
||||
description: Elydora — Open-source AI accountability and compliance platform
|
||||
description: Open-source AI accountability and compliance platform for Elydora
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.1.0"
|
||||
+18
-10
@@ -8,7 +8,8 @@ Web dashboard for the Elydora Responsibility Protocol. Provides agent management
|
||||
- **Language:** TypeScript 5.7
|
||||
- **UI:** React 19, Tailwind CSS 4
|
||||
- **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
|
||||
|
||||
@@ -16,7 +17,7 @@ Web dashboard for the Elydora Responsibility Protocol. Provides agent management
|
||||
src/
|
||||
├── app/
|
||||
│ ├── 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
|
||||
│ ├── agents/
|
||||
│ │ ├── page.tsx # Agent list, search, registration
|
||||
@@ -33,6 +34,8 @@ src/
|
||||
│ │ └── page.tsx # Audit log query builder
|
||||
│ ├── login/page.tsx # Login form
|
||||
│ ├── register/page.tsx # Registration form
|
||||
│ ├── settings/ # Organization settings
|
||||
│ ├── global-error.tsx # Root error boundary
|
||||
│ └── jwks/page.tsx # Public key display
|
||||
├── components/
|
||||
│ ├── AppShell.tsx # Main layout wrapper with sidebar
|
||||
@@ -43,6 +46,8 @@ src/
|
||||
│ ├── EpochDetailClient.tsx # Epoch proof and verification
|
||||
│ ├── ChainVisualization.tsx # Merkle chain visualization
|
||||
│ ├── VerificationChecklist.tsx # Proof verification UI
|
||||
│ ├── I18nProvider.tsx # Locale provider
|
||||
│ ├── agent-registration/ # Registration flow components
|
||||
│ └── ui/
|
||||
│ ├── PageHeader.tsx # Page title and breadcrumbs
|
||||
│ ├── DataTable.tsx # Sortable, paginated table
|
||||
@@ -50,12 +55,17 @@ src/
|
||||
│ ├── SearchInput.tsx # Search/filter input
|
||||
│ ├── StatusBadge.tsx # Status indicator
|
||||
│ ├── CopyButton.tsx # Copy-to-clipboard
|
||||
│ ├── LanguageDropdown.tsx # Locale switcher
|
||||
│ └── Sidebar.tsx # Navigation sidebar
|
||||
├── features/
|
||||
│ └── agent-registration/ # Install instructions and credential handling
|
||||
├── i18n/ # en and zh message catalogs
|
||||
├── lib/
|
||||
│ ├── api.ts # Typed REST client for all endpoints
|
||||
│ ├── auth.tsx # Auth context and hooks
|
||||
│ └── hooks.ts # SWR data-fetching hooks
|
||||
└── shared/ # Shared types (@elydora/shared)
|
||||
e2e/ # Playwright specs
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
@@ -90,20 +100,17 @@ Runs at `http://localhost:3000` with Turbopack.
|
||||
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
|
||||
|
||||
```bash
|
||||
npm run deploy
|
||||
```
|
||||
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.
|
||||
|
||||
Deploys the `out/` directory to Cloudflare Pages.
|
||||
|
||||
### Type Checking
|
||||
### Checks
|
||||
|
||||
```bash
|
||||
npm run typecheck
|
||||
npm run typecheck # tsc --noEmit
|
||||
npm run test:e2e # Playwright specs in e2e/
|
||||
```
|
||||
|
||||
## Pages
|
||||
@@ -122,3 +129,4 @@ npm run typecheck
|
||||
| `/login` | Login |
|
||||
| `/register` | Register account and organization |
|
||||
| `/jwks` | Display server public keys in JWK format |
|
||||
| `/settings` | Organization settings |
|
||||
@@ -93,7 +93,7 @@ export default function OperationDetailCard({ operation, receipt, payload }: Ope
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Payload — what the operation actually did */}
|
||||
{/* Payload: what the operation did */}
|
||||
{payload && (
|
||||
<div className="border border-border bg-surface p-4 sm:p-6 relative">
|
||||
<span className="crosshair ch-tl" />
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function Modal({
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Keydown listener — uses ref so it never changes identity
|
||||
// Keydown listener uses a ref for stable identity
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Backdrop overlay — mobile only */}
|
||||
{/* Mobile backdrop overlay */}
|
||||
{isOpen && (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 z-40 md:hidden"
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
Reference in new issue
Block a user