added auth strategies migration, fixed rebuild of modules on migrations

This commit is contained in:
dswbx
2025-02-27 10:08:22 +01:00
parent 7743f71a11
commit dd48962901
10 changed files with 854 additions and 33 deletions

View File

@@ -33,7 +33,7 @@ export const AppEvents = { AppConfigUpdatedEvent, AppBuiltEvent, AppFirstBoot }
export type AppOptions = {
plugins?: AppPlugin[];
seed?: (ctx: ModuleBuildContext) => Promise<void>;
seed?: (ctx: ModuleBuildContext & { app: App }) => Promise<void>;
manager?: Omit<ModuleManagerOptions, "initial" | "onUpdated" | "seed">;
};
export type CreateAppConfig = {
@@ -67,7 +67,6 @@ export class App {
this.modules = new ModuleManager(connection, {
...(options?.manager ?? {}),
initial: _initialConfig,
seed: options?.seed,
onUpdated: async (key, config) => {
// if the EventManager was disabled, we assume we shouldn't
// respond to events, such as "onUpdated".
@@ -115,15 +114,18 @@ export class App {
await Promise.all(this.plugins.map((plugin) => plugin(this)));
}
$console.log("App built");
await this.emgr.emit(new AppBuiltEvent({ app: this }));
// first boot is set from ModuleManager when there wasn't a config table
if (this.trigger_first_boot) {
this.trigger_first_boot = false;
await this.emgr.emit(new AppFirstBoot({ app: this }));
await this.options?.seed?.({
...this.modules.ctx(),
app: this,
});
}
$console.log("App built");
}
mutateConfig<Module extends keyof Modules>(module: Module) {

View File

@@ -7,7 +7,7 @@ import {
type Strategy,
} from "auth";
import type { PasswordStrategy } from "auth/authenticate/strategies";
import { type DB, Exception, type PrimaryFieldType } from "core";
import { $console, type DB, Exception, type PrimaryFieldType } from "core";
import { type Static, secureRandomString, transformObject } from "core/utils";
import type { Entity, EntityManager } from "data";
import { type FieldSchema, em, entity, enumm, text } from "data/prototype";
@@ -41,6 +41,12 @@ export class AppAuth extends Module<typeof authConfigSchema> {
}
}
// @todo: password strategy is required atm
if (!to.strategies?.password?.enabled) {
$console.warn("Password strategy cannot be disabled.");
to.strategies!.password!.enabled = true;
}
return to;
}
@@ -89,6 +95,9 @@ export class AppAuth extends Module<typeof authConfigSchema> {
isStrategyEnabled(strategy: Strategy | string) {
const name = typeof strategy === "string" ? strategy : strategy.getName();
// for now, password is always active
if (name === "password") return true;
return this.config.strategies?.[name]?.enabled ?? false;
}

View File

@@ -252,14 +252,12 @@ export class DataController extends Controller {
tb("param", Type.Object({ entity: Type.String() })),
tb("query", querySchema),
async (c) => {
//console.log("request", c.req.raw);
const { entity } = c.req.param();
if (!this.entityExists(entity)) {
console.warn("not found:", entity, definedEntities);
return this.notFound(c);
}
const options = c.req.valid("query") as RepoQuery;
//console.log("before", this.ctx.emgr.Events);
const result = await this.em.repository(entity).findMany(options);
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });

View File

@@ -1,5 +1,5 @@
import { Guard } from "auth";
import { BkndError, DebugLogger, withDisabledConsole } from "core";
import { $console, BkndError, DebugLogger, withDisabledConsole } from "core";
import { EventManager } from "core/events";
import { clone, diff } from "core/object/diff";
import {
@@ -153,7 +153,6 @@ export class ModuleManager {
this.modules = {} as Modules;
this.emgr = new EventManager();
this.logger = new DebugLogger(this.verbosity === Verbosity.log);
const context = this.ctx(true);
let initial = {} as Partial<ModuleConfigs>;
if (options?.initial) {
@@ -169,15 +168,29 @@ export class ModuleManager {
}
}
for (const key in MODULES) {
const moduleConfig = key in initial ? initial[key] : {};
const module = new MODULES[key](moduleConfig, context) as Module;
module.setListener(async (c) => {
await this.onModuleConfigUpdated(key, c);
});
this.createModules(initial);
}
this.modules[key] = module;
private createModules(initial: Partial<ModuleConfigs>) {
this.logger.context("createModules").log("creating modules");
try {
const context = this.ctx(true);
for (const key in MODULES) {
const moduleConfig = key in initial ? initial[key] : {};
const module = new MODULES[key](moduleConfig, context) as Module;
module.setListener(async (c) => {
await this.onModuleConfigUpdated(key, c);
});
this.modules[key] = module;
}
this.logger.log("modules created");
} catch (e) {
this.logger.log("failed to create modules", e);
throw e;
}
this.logger.clear();
}
private get verbosity() {
@@ -197,7 +210,7 @@ export class ModuleManager {
if (this.options?.onUpdated) {
await this.options.onUpdated(key as any, config);
} else {
this.buildModules();
await this.buildModules();
}
}
@@ -368,15 +381,27 @@ export class ModuleManager {
}
private async migrate() {
const state = {
success: false,
migrated: false,
version: {
before: this.version(),
after: this.version(),
},
};
this.logger.context("migrate").log("migrating?", this.version(), CURRENT_VERSION);
if (this.version() < CURRENT_VERSION) {
state.version.before = this.version();
this.logger.log("there are migrations, verify version");
// sync __bknd table
await this.syncConfigTable();
// modules must be built before migration
this.logger.log("building modules");
await this.buildModules({ graceful: true });
this.logger.log("modules built");
try {
const state = await this.fetch();
@@ -405,17 +430,27 @@ export class ModuleManager {
version = _version;
configs = _configs;
this.setConfigs(configs);
this._version = version;
state.version.after = version;
state.migrated = true;
this.ctx().flags.sync_required = true;
this.logger.log("setting configs");
this.createModules(configs);
await this.buildModules();
this.logger.log("migrated to", version);
$console.log("Migrated config from", state.version.before, "to", state.version.after);
await this.save();
} else {
this.logger.log("no migrations needed");
}
state.success = true;
this.logger.clear();
return state;
}
private setConfigs(configs: ModuleConfigs): void {
@@ -480,10 +515,16 @@ export class ModuleManager {
}
// migrate to latest if needed
await this.migrate();
this.logger.log("check migrate");
const migration = await this.migrate();
if (migration.success && migration.migrated) {
this.logger.log("skipping build after migration");
} else {
this.logger.log("trigger build modules");
await this.buildModules();
}
this.logger.log("building");
await this.buildModules();
this.logger.log("done");
return this;
}
@@ -496,7 +537,7 @@ export class ModuleManager {
reloaded: false,
};
this.logger.log("buildModules() triggered", options, this._built);
this.logger.context("buildModules").log("triggered", options, this._built);
if (options?.graceful && this._built) {
this.logger.log("skipping build (graceful)");
return state;
@@ -536,8 +577,10 @@ export class ModuleManager {
}
// reset all falgs
this.logger.log("resetting flags");
ctx.flags = Module.ctx_flags;
this.logger.clear();
return state;
}

View File

@@ -1,4 +1,4 @@
import { _jsonp } from "core/utils";
import { _jsonp, transformObject } from "core/utils";
import { type Kysely, sql } from "kysely";
import { set } from "lodash-es";
@@ -72,13 +72,25 @@ export const migrations: Migration[] = [
};
},
},
/*{
{
version: 8,
up: async (config, { db }) => {
await db.deleteFrom(TABLE_NAME).where("type", "=", "diff").execute();
return config;
}
}*/
up: async (config) => {
const strategies = transformObject(config.auth.strategies, (strategy) => {
return {
...strategy,
enabled: true,
};
});
return {
...config,
auth: {
...config.auth,
strategies: strategies,
},
};
},
},
];
export const CURRENT_VERSION = migrations[migrations.length - 1]?.version ?? 0;

View File

@@ -238,7 +238,6 @@ export function FormContextOverride({
...overrides,
...additional,
};
console.log("context", context);
return <FormContext.Provider value={context}>{children}</FormContext.Provider>;
}