Commit 35e61c0a authored by nanahira's avatar nanahira

add dynamic load typeorm entity

parent ccb8c97c
Pipeline #43337 passed with stages
in 3 minutes and 22 seconds
import fs from 'node:fs';
import path from 'node:path';
import { getMetadataArgsStorage } from 'typeorm';
type PluginEntityLogger = {
warn: (...args: unknown[]) => void;
};
export function collectPluginTypeormEntities(logger?: PluginEntityLogger): Function[] {
const pluginDir = path.resolve(__dirname, '..', '..', 'plugins');
const pluginEntityFiles = collectPluginEntityFiles(pluginDir);
const entities = new Set<Function>();
for (const pluginEntityFile of pluginEntityFiles) {
const requirePath = resolveRequirePath(pluginEntityFile);
let loadedModule: unknown;
try {
loadedModule = require(requirePath);
} catch (error) {
logger?.warn(
{
pluginEntityFile: requirePath,
error: (error as Error)?.stack || String(error),
},
'Failed requiring plugin entity file',
);
continue;
}
const exportedItems = resolveExportedItems(loadedModule);
const entityTargets = collectTypeormEntityTargets();
for (const item of exportedItems) {
if (typeof item !== 'function') {
continue;
}
if (!entityTargets.has(item)) {
continue;
}
entities.add(item);
}
}
return [...entities];
}
function collectPluginEntityFiles(rootDir: string): string[] {
if (!fs.existsSync(rootDir)) {
return [];
}
const files: string[] = [];
const walk = (currentDir: string) => {
const entries = fs.readdirSync(currentDir, {
withFileTypes: true,
});
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name);
if (entry.isDirectory()) {
walk(fullPath);
continue;
}
if (!entry.isFile()) {
continue;
}
if (!fullPath.endsWith('.entity.js')) {
continue;
}
files.push(fullPath);
}
};
walk(rootDir);
return files;
}
function resolveRequirePath(filePath: string) {
const cwdRelative = path.relative(process.cwd(), filePath);
return path.resolve(process.cwd(), cwdRelative);
}
function collectTypeormEntityTargets() {
const targets = new Set<Function>();
for (const table of getMetadataArgsStorage().tables) {
if (table.type === 'view') {
continue;
}
if (typeof table.target !== 'function') {
continue;
}
targets.add(table.target);
}
return targets;
}
function resolveExportedItems(loadedModule: unknown) {
const items: unknown[] = [];
if (!loadedModule) {
return items;
}
if (typeof loadedModule === 'function') {
items.push(loadedModule);
return items;
}
if (typeof loadedModule !== 'object') {
return items;
}
const record = loadedModule as Record<string, unknown>;
if (record.default) {
items.push(record.default);
}
for (const value of Object.values(record)) {
items.push(value);
}
return items;
}
......@@ -7,6 +7,7 @@ import { DuelRecordEntity, DuelRecordPlayer } from '../feats/cloud-replay';
import { LegacyApiRecordEntity } from '../legacy-api/legacy-api-record.entity';
import { LegacyBanEntity } from '../legacy-api/legacy-ban.entity';
import { LegacyDeckEntity } from '../legacy-api/legacy-deck.entity';
import { collectPluginTypeormEntities } from './plugin-typeorm-entity-loader';
export class TypeormLoader {
constructor(private ctx: AppContext) {}
......@@ -35,6 +36,26 @@ export const TypeormFactory = async (ctx: AppContext) => {
const password = config.getString('DB_PASS');
const database = config.getString('DB_NAME');
const synchronize = !config.getBoolean('DB_NO_INIT');
const staticEntities: Function[] = [
RandomDuelScore,
DuelRecordEntity,
DuelRecordPlayer,
LegacyApiRecordEntity,
LegacyBanEntity,
LegacyDeckEntity,
];
const pluginEntities = collectPluginTypeormEntities(logger);
const entities = [...new Set<Function>([...staticEntities, ...pluginEntities])];
if (pluginEntities.length > 0) {
logger.info(
{
count: pluginEntities.length,
entities: pluginEntities.map((entity) => entity.name),
},
'Collected plugin typeorm entities',
);
}
const dataSource = new DataSource({
type: 'postgres',
......@@ -44,14 +65,7 @@ export const TypeormFactory = async (ctx: AppContext) => {
password,
database,
synchronize,
entities: [
RandomDuelScore,
DuelRecordEntity,
DuelRecordPlayer,
LegacyApiRecordEntity,
LegacyBanEntity,
LegacyDeckEntity,
],
entities,
});
try {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment