feat: complete SSR CRM and refine auth UI
This commit is contained in:
+72
-5
@@ -14,15 +14,23 @@
|
||||
* (`databases.<name>`), with files under `app/db/<name>/`.
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
||||
import { join, resolve } from "node:path";
|
||||
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
||||
import { basename, join, resolve } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import {
|
||||
createPluginRunner,
|
||||
discoverPlugins,
|
||||
type PackageMigrationDefinition,
|
||||
} from "@wrnexus/plugin";
|
||||
import { loadAppConfig, type AppConfig } from "@wrnexus/styles";
|
||||
import {
|
||||
generateQueriesFile,
|
||||
analyzeMigrations,
|
||||
appliedMigrations,
|
||||
applyMigrations,
|
||||
loadMigrations,
|
||||
migrate,
|
||||
parseMigration,
|
||||
parseQueries,
|
||||
rollback,
|
||||
scaffoldMigration,
|
||||
@@ -42,6 +50,55 @@ function dbBaseOf(appDir: string, dbName: string | null): string {
|
||||
return dbName ? join(appDir, "db", dbName) : join(appDir, "db");
|
||||
}
|
||||
|
||||
function packageMigrationName(definition: PackageMigrationDefinition, name?: string): string {
|
||||
const clean = (value: string) => value.trim().replace(/[^a-zA-Z0-9_.-]+/g, "_");
|
||||
return name ? `${clean(definition.id)}__${clean(name)}` : clean(definition.id);
|
||||
}
|
||||
|
||||
function resolvePackageMigrations(
|
||||
definitions: readonly PackageMigrationDefinition[],
|
||||
database?: string,
|
||||
) {
|
||||
return definitions.flatMap((definition) => {
|
||||
if ((definition.database?.trim() || "default") !== (database?.trim() || "default")) return [];
|
||||
if (definition.source !== undefined) {
|
||||
return [parseMigration(packageMigrationName(definition), definition.source)];
|
||||
}
|
||||
if (!definition.entry || !existsSync(definition.entry)) {
|
||||
throw new Error(`WRN-PLUGIN-MIGRATION-MISSING: ${definition.id} has no readable entry.`);
|
||||
}
|
||||
if (statSync(definition.entry).isDirectory()) {
|
||||
return loadMigrations(definition.entry).map((migration) => ({
|
||||
...migration,
|
||||
name: packageMigrationName(definition, migration.name),
|
||||
}));
|
||||
}
|
||||
return [
|
||||
parseMigration(
|
||||
packageMigrationName(definition, basename(definition.entry, ".sql")),
|
||||
readFileSync(definition.entry, "utf8"),
|
||||
),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
async function packageMigrations(root: string, config: AppConfig, database?: string) {
|
||||
const discovered = await discoverPlugins(root, config.plugins, {
|
||||
includeDevDependencies: true,
|
||||
strict: true,
|
||||
});
|
||||
const runner = createPluginRunner(discovered, {
|
||||
root,
|
||||
mode: "development",
|
||||
command: "dev",
|
||||
metadata: new Map(),
|
||||
warn: (message) => console.warn(`[wrnexus:plugin] ${message}`),
|
||||
});
|
||||
await runner.configure(config as Record<string, unknown>);
|
||||
await runner.configResolved(config as Readonly<Record<string, unknown>>);
|
||||
return resolvePackageMigrations((await runner.contributions()).migrations, database);
|
||||
}
|
||||
|
||||
/** List user tables for the connected database (dialect-aware introspection). */
|
||||
async function listTables(db: import("@wrnexus/db").Db): Promise<string[]> {
|
||||
const dialect = db.driver.dialect;
|
||||
@@ -126,6 +183,7 @@ export async function runDbCommand(
|
||||
const dbConfig = dbName ? config.databases?.[dbName] : config.db;
|
||||
const dbBase = dbBaseOf(appDir, dbName);
|
||||
const migrationsDir = join(dbBase, "migrations");
|
||||
const contributedMigrations = await packageMigrations(root, config, dbName ?? undefined);
|
||||
const label = dbName ? ` (db: ${dbName})` : "";
|
||||
const safetyIssues = analyzeMigrations(loadMigrations(migrationsDir));
|
||||
|
||||
@@ -242,7 +300,10 @@ export async function runDbCommand(
|
||||
`WRN-DB-UNSAFE-MIGRATION: ${blockers.length} breaking rollout operation(s) found. Run 'wrnexus db check' and use an expand/contract migration; --allow-breaking explicitly overrides this gate.`,
|
||||
);
|
||||
}
|
||||
const applied = await migrate(db, migrationsDir);
|
||||
const applied = [
|
||||
...(await migrate(db, migrationsDir)),
|
||||
...(await applyMigrations(db, contributedMigrations)),
|
||||
];
|
||||
console.log(
|
||||
applied.length
|
||||
? `✓ Applied ${applied.length}${label}:\n ${applied.join("\n ")}`
|
||||
@@ -257,8 +318,14 @@ export async function runDbCommand(
|
||||
}
|
||||
case "status": {
|
||||
const rows = await status(db, migrationsDir);
|
||||
if (rows.length === 0) console.log(`No migrations found in ${migrationsDir}.`);
|
||||
else for (const r of rows) console.log(` [${r.applied ? "x" : " "}] ${r.name}`);
|
||||
const applied = new Set(await appliedMigrations(db));
|
||||
const packageRows = contributedMigrations.map(({ name }) => ({
|
||||
name,
|
||||
applied: applied.has(name),
|
||||
}));
|
||||
const allRows = [...rows, ...packageRows];
|
||||
if (allRows.length === 0) console.log(`No migrations found in ${migrationsDir}.`);
|
||||
else for (const r of allRows) console.log(` [${r.applied ? "x" : " "}] ${r.name}`);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user