@coffer-org/server 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-api.js +5 -1
- package/dist/auth-store.js +1 -2
- package/dist/index.js +15 -1
- package/dist/log.d.ts +3 -0
- package/dist/log.js +5 -0
- package/dist/migrations.js +4 -2
- package/dist/plugin-discovery.js +6 -4
- package/dist/plugin-runtime.js +9 -7
- package/dist/seeds.js +3 -1
- package/package.json +3 -2
package/dist/auth-api.js
CHANGED
|
@@ -127,7 +127,11 @@ export async function registerAuthApi(app) {
|
|
|
127
127
|
return;
|
|
128
128
|
const id = Number(req.params.id);
|
|
129
129
|
const body = (req.body ?? {});
|
|
130
|
-
const wantsDisabled = body.disabled === undefined
|
|
130
|
+
const wantsDisabled = body.disabled === undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: body.disabled === 'false' || body.disabled === '0'
|
|
133
|
+
? false
|
|
134
|
+
: Boolean(body.disabled);
|
|
131
135
|
const demotingOrDisabling = (body.role === 'member' || wantsDisabled === true) && (await isLastAdmin(id));
|
|
132
136
|
if (demotingOrDisabling)
|
|
133
137
|
return reply.code(409).send({ error: 'last_admin' });
|
package/dist/auth-store.js
CHANGED
|
@@ -125,8 +125,7 @@ export async function resolveApiToken(raw) {
|
|
|
125
125
|
const user = await findUserById(token.user_id);
|
|
126
126
|
if (!user || user.disabled)
|
|
127
127
|
return null;
|
|
128
|
-
em.
|
|
129
|
-
await em.flush();
|
|
128
|
+
await em.nativeUpdate('_ApiToken', { id: token.id }, { last_used_at: new Date().toISOString() });
|
|
130
129
|
return user;
|
|
131
130
|
}
|
|
132
131
|
export async function listApiTokens(userId) {
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import { checkLatestVersion, resolveUpdateTarget, runNpmInstall } from "./plugin
|
|
|
32
32
|
import { buildClientSchema } from "./schema-api.js";
|
|
33
33
|
import { recordList, recordGet, recordCreate, recordUpdate, recordDelete, rowMatch, UnknownTypeError } from "./records-api.js";
|
|
34
34
|
import { maskTree, preserveTree } from "./secrets.js";
|
|
35
|
+
import { rootLogger, getLogger } from "./log.js";
|
|
35
36
|
import { uploadsDir } from "./uploads.js";
|
|
36
37
|
const ENV_FILE = join(process.cwd(), '.env');
|
|
37
38
|
if (existsSync(ENV_FILE))
|
|
@@ -46,7 +47,20 @@ for (const m of modules) {
|
|
|
46
47
|
moduleDefs.set(`${m.vault}/${m.module}`, m);
|
|
47
48
|
}
|
|
48
49
|
const UPLOADS = uploadsDir();
|
|
49
|
-
const app = Fastify({
|
|
50
|
+
const app = Fastify({
|
|
51
|
+
loggerInstance: rootLogger,
|
|
52
|
+
disableRequestLogging: true,
|
|
53
|
+
});
|
|
54
|
+
const httpLog = getLogger('http');
|
|
55
|
+
app.addHook('onResponse', async (req, reply) => {
|
|
56
|
+
const level = reply.statusCode >= 500 ? 'error' : reply.statusCode >= 400 ? 'warn' : 'debug';
|
|
57
|
+
httpLog[level]('request', {
|
|
58
|
+
method: req.method,
|
|
59
|
+
url: req.url,
|
|
60
|
+
status: reply.statusCode,
|
|
61
|
+
ms: Number(reply.elapsedTime.toFixed(1)),
|
|
62
|
+
});
|
|
63
|
+
});
|
|
50
64
|
await app.register(cors, { origin: true });
|
|
51
65
|
await app.register(multipart, { limits: { fileSize: 25 * 1024 * 1024 } });
|
|
52
66
|
await app.register(fastifyStatic, { root: UPLOADS, prefix: '/uploads/' });
|
package/dist/log.d.ts
ADDED
package/dist/log.js
ADDED
package/dist/migrations.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { DatabaseSchema } from '@mikro-orm/sql';
|
|
2
2
|
import { dialectOf } from "./dialect.js";
|
|
3
|
+
import { getLogger } from '@coffer-org/sdk/logger';
|
|
4
|
+
const log = getLogger('migrations');
|
|
3
5
|
function sqlType(col) {
|
|
4
6
|
if (col === 'integer' || col === 'boolean')
|
|
5
7
|
return 'integer';
|
|
@@ -161,7 +163,7 @@ export async function runMigrations({ em, plugins, hooks }) {
|
|
|
161
163
|
if (p.tables?.length && !(await anyTableExists(em, p.tables))) {
|
|
162
164
|
await conn.execute(`INSERT INTO _migrations (plugin_id, version) VALUES (?, ?)
|
|
163
165
|
ON CONFLICT(plugin_id) DO UPDATE SET version = excluded.version`, [p.id, maxV]);
|
|
164
|
-
|
|
166
|
+
log.info(`${p.id}: fresh DB → stamp v${maxV} (no run)`);
|
|
165
167
|
continue;
|
|
166
168
|
}
|
|
167
169
|
const pending = [...list].filter((m) => m.version > stored).sort((a, b) => a.version - b.version);
|
|
@@ -172,7 +174,7 @@ export async function runMigrations({ em, plugins, hooks }) {
|
|
|
172
174
|
ON CONFLICT(plugin_id) DO UPDATE SET version = excluded.version`, [p.id, m.version]);
|
|
173
175
|
}
|
|
174
176
|
if (pending.length)
|
|
175
|
-
|
|
177
|
+
log.info(`${p.id}: applied ${pending.length} (→ v${pending[pending.length - 1].version})`);
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
180
|
export async function assertSafeRequired(em, entities) {
|
package/dist/plugin-discovery.js
CHANGED
|
@@ -9,6 +9,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
9
9
|
import { readdir, readFile } from 'node:fs/promises';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { pathToFileURL } from 'node:url';
|
|
12
|
+
import { getLogger } from '@coffer-org/sdk/logger';
|
|
13
|
+
const log = getLogger('discovery');
|
|
12
14
|
async function pkgNames(nm) {
|
|
13
15
|
const names = [];
|
|
14
16
|
try {
|
|
@@ -63,7 +65,7 @@ export async function discoverPluginAssets() {
|
|
|
63
65
|
});
|
|
64
66
|
}
|
|
65
67
|
catch (e) {
|
|
66
|
-
|
|
68
|
+
log.error(`${name}: asset scan skip — ${e.message}`);
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
return out;
|
|
@@ -77,11 +79,11 @@ async function loadManifest(nm, name) {
|
|
|
77
79
|
const manifest = mod.default;
|
|
78
80
|
if (manifest && typeof manifest.id === 'string')
|
|
79
81
|
return manifest;
|
|
80
|
-
|
|
82
|
+
log.warn(`${name}: no valid default manifest, skipping`);
|
|
81
83
|
return null;
|
|
82
84
|
}
|
|
83
85
|
catch (e) {
|
|
84
|
-
|
|
86
|
+
log.error(`${name}: import failed, skipping — ${e.message}`);
|
|
85
87
|
return null;
|
|
86
88
|
}
|
|
87
89
|
}
|
|
@@ -117,7 +119,7 @@ export async function loadServerHooks() {
|
|
|
117
119
|
hooks[id] = mod.serverHooks;
|
|
118
120
|
}
|
|
119
121
|
catch (e) {
|
|
120
|
-
|
|
122
|
+
log.error(`${name}: server hooks not loaded — ${e.message}`);
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
125
|
return hooks;
|
package/dist/plugin-runtime.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { serialize } from '@mikro-orm/core';
|
|
2
2
|
import { composeRegistry } from '@coffer-org/core/compose';
|
|
3
|
+
import { getLogger } from '@coffer-org/sdk/logger';
|
|
3
4
|
import { initDb, getOrm, getEm } from "./db.js";
|
|
4
5
|
import { syncSchema } from "./schema-sync.js";
|
|
5
6
|
import { systemEntities, buildPluginEntities, moduleTableName } from "./entity-schema.js";
|
|
@@ -9,6 +10,7 @@ import { runMigrations, assertSafeRequired } from "./migrations.js";
|
|
|
9
10
|
import { runSeeds } from "./seeds.js";
|
|
10
11
|
import { setActiveRegistry } from "./registry-context.js";
|
|
11
12
|
import { migrateEmbeddingVectorsToBlob } from "./embeddings.js";
|
|
13
|
+
const log = getLogger('plugins');
|
|
12
14
|
let _plugins = null;
|
|
13
15
|
export async function getPlugins() {
|
|
14
16
|
return (_plugins ??= await discoverPlugins());
|
|
@@ -77,15 +79,15 @@ export async function initPlugins() {
|
|
|
77
79
|
try {
|
|
78
80
|
if (h?.init) {
|
|
79
81
|
await h.init({ em: getEm().fork() });
|
|
80
|
-
|
|
82
|
+
log.debug(`${p.id}: init ✓`);
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
catch (err) {
|
|
84
|
-
|
|
86
|
+
log.error(`${p.id}: initialization failure`, err);
|
|
85
87
|
throw err;
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
|
-
|
|
90
|
+
log.info(`active: ${reg.order.map((p) => p.id).join(' ')}` +
|
|
89
91
|
(disabled.size ? ` | disabled: ${[...disabled].join(' ')}` : ''));
|
|
90
92
|
return reg;
|
|
91
93
|
}
|
|
@@ -99,10 +101,10 @@ export async function teardownPlugins() {
|
|
|
99
101
|
continue;
|
|
100
102
|
try {
|
|
101
103
|
await h.teardown({ em: getEm().fork() });
|
|
102
|
-
|
|
104
|
+
log.debug(`${p.id}: teardown ✓`);
|
|
103
105
|
}
|
|
104
106
|
catch (e) {
|
|
105
|
-
|
|
107
|
+
log.warn(`${p.id}: teardown ✗ ${e.message}`);
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
}
|
|
@@ -110,7 +112,7 @@ export async function teardownPlugin(id) {
|
|
|
110
112
|
const h = pluginHooks[id];
|
|
111
113
|
if (h?.teardown) {
|
|
112
114
|
await h.teardown({ em: getEm().fork() });
|
|
113
|
-
|
|
115
|
+
log.debug(`${id}: teardown ✓`);
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
function pluginModules(p) {
|
|
@@ -154,7 +156,7 @@ export async function purgePluginData(p, actor) {
|
|
|
154
156
|
after: null,
|
|
155
157
|
});
|
|
156
158
|
});
|
|
157
|
-
|
|
159
|
+
log.info(`${p.id}: purge ✓ (${tables.length} tables)`);
|
|
158
160
|
}
|
|
159
161
|
export function filterEnabled(all, disabled) {
|
|
160
162
|
return all.filter((p) => !disabled.has(p.id));
|
package/dist/seeds.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getLogger } from '@coffer-org/sdk/logger';
|
|
2
|
+
const log = getLogger('seeds');
|
|
1
3
|
export function validateSeeds(pluginId, list) {
|
|
2
4
|
const names = list.map((s) => s.name);
|
|
3
5
|
if (new Set(names).size !== names.length) {
|
|
@@ -18,6 +20,6 @@ export async function runSeeds({ em, plugins, hooks }) {
|
|
|
18
20
|
await conn.execute(`INSERT INTO _seeds (plugin_id, name) VALUES (?, ?)`, [p.id, s.name]);
|
|
19
21
|
}
|
|
20
22
|
if (pending.length)
|
|
21
|
-
|
|
23
|
+
log.info(`${p.id}: applied ${pending.length} (${pending.map((s) => s.name).join(', ')})`);
|
|
22
24
|
}
|
|
23
25
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffer-org/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@coffer-org/core": "^1.3.0",
|
|
28
|
-
"@coffer-org/sdk": "^1.
|
|
28
|
+
"@coffer-org/sdk": "^1.4.0",
|
|
29
29
|
"@extractus/oembed-extractor": "^4.1.0",
|
|
30
30
|
"@fastify/cors": "^11.2.0",
|
|
31
31
|
"@fastify/multipart": "^10.0.0",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@mikro-orm/sqlite": "^7.1.4",
|
|
35
35
|
"fastify": "^5.2.1",
|
|
36
36
|
"open-graph-scraper": "^6.11.0",
|
|
37
|
+
"pino": "^9.14.0",
|
|
37
38
|
"zod": "^4.4.3"
|
|
38
39
|
},
|
|
39
40
|
"optionalDependencies": {
|