@coffer-org/server 1.6.0 → 1.7.1
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/index.js +20 -7
- package/dist/log.js +18 -1
- package/dist/plugin-discovery.d.ts +7 -0
- package/dist/plugin-discovery.js +25 -2
- package/dist/plugin-hooks.d.ts +3 -0
- package/dist/plugin-hooks.js +4 -0
- package/dist/plugin-runtime.js +4 -4
- package/dist/plugin-updates.d.ts +10 -0
- package/dist/plugin-updates.js +7 -0
- package/dist/plugins-api.js +9 -2
- package/dist/seeds.d.ts +1 -1
- package/dist/seeds.js +2 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -27,8 +27,8 @@ import { resolveWithinHome, filterSort, listDir } from "./fs-list.js";
|
|
|
27
27
|
import { initPlugins, teardownPlugins, readDisabled, purgePluginData, getPluginSettings, getPlugins, } from "./plugin-runtime.js";
|
|
28
28
|
import { registerPluginsApi } from "./plugins-api.js";
|
|
29
29
|
import { registerAuthApi, resolveRequestUser, requireAdmin, PUBLIC_API_PATHS } from "./auth-api.js";
|
|
30
|
-
import { discoverPluginAssets } from "./plugin-discovery.js";
|
|
31
|
-
import { checkLatestVersion, resolveUpdateTarget, resolveAllUpdateTargets, runNpmInstall } from "./plugin-updates.js";
|
|
30
|
+
import { discoverPluginAssets, discoverRuntime } from "./plugin-discovery.js";
|
|
31
|
+
import { checkLatestVersion, resolveUpdateTarget, resolveAllUpdateTargets, resolveRuntimeTarget, runNpmInstall, } from "./plugin-updates.js";
|
|
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";
|
|
@@ -229,7 +229,7 @@ app.post('/api/plugins/:id/update', async (req, reply) => {
|
|
|
229
229
|
const { id } = req.params;
|
|
230
230
|
const assets = await discoverPluginAssets();
|
|
231
231
|
const rec = assets.find((a) => a.id === id);
|
|
232
|
-
const latest = rec ? await checkLatestVersion(rec.packageName) : null;
|
|
232
|
+
const latest = rec && !rec.local ? await checkLatestVersion(rec.packageName) : null;
|
|
233
233
|
const target = resolveUpdateTarget(assets, id, latest);
|
|
234
234
|
if (!target.ok) {
|
|
235
235
|
return reply.code(target.error === 'unknown_plugin' ? 404 : 400).send({ error: target.error });
|
|
@@ -245,15 +245,28 @@ app.post('/api/plugins/update-all', async (req, reply) => {
|
|
|
245
245
|
if (!requireAdmin(req, reply))
|
|
246
246
|
return;
|
|
247
247
|
const assets = await discoverPluginAssets();
|
|
248
|
-
const
|
|
248
|
+
const runtime = await discoverRuntime();
|
|
249
|
+
const [latestById, runtimeLatest] = await Promise.all([
|
|
250
|
+
Promise.all(assets.map(async (a) => [a.id, a.local ? null : await checkLatestVersion(a.packageName)])).then((entries) => new Map(entries)),
|
|
251
|
+
runtime && !runtime.local ? checkLatestVersion(runtime.packageName) : Promise.resolve(null),
|
|
252
|
+
]);
|
|
249
253
|
const targets = resolveAllUpdateTargets(assets, latestById);
|
|
250
|
-
|
|
254
|
+
const runtimeTarget = resolveRuntimeTarget(runtime, runtimeLatest);
|
|
255
|
+
const specs = [
|
|
256
|
+
...targets.map((t) => `${t.packageName}@${t.to}`),
|
|
257
|
+
...(runtimeTarget ? [`${runtimeTarget.packageName}@${runtimeTarget.to}`] : []),
|
|
258
|
+
];
|
|
259
|
+
if (specs.length === 0)
|
|
251
260
|
return reply.code(400).send({ error: 'no_update_available' });
|
|
252
|
-
const result = await runNpmInstall(
|
|
261
|
+
const result = await runNpmInstall(specs, process.cwd());
|
|
253
262
|
if (!result.ok) {
|
|
254
263
|
return reply.code(500).send({ error: 'install_failed', stderr: result.stderr });
|
|
255
264
|
}
|
|
256
|
-
|
|
265
|
+
const updated = [
|
|
266
|
+
...targets.map(({ id, from, to }) => ({ id, from, to })),
|
|
267
|
+
...(runtimeTarget ? [{ id: 'runtime', from: runtimeTarget.from, to: runtimeTarget.to }] : []),
|
|
268
|
+
];
|
|
269
|
+
reply.send({ ok: true, updated });
|
|
257
270
|
setTimeout(() => process.exit(0), 500);
|
|
258
271
|
});
|
|
259
272
|
app.get('/api/plugins/:id/settings', async (req, reply) => {
|
package/dist/log.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { pino } from 'pino';
|
|
2
2
|
import { setRootLogger, LOG_LEVEL, getLogger } from '@coffer-org/sdk/logger';
|
|
3
|
-
|
|
3
|
+
const prettyEnv = process.env.COFFER_LOG_PRETTY;
|
|
4
|
+
const pretty = prettyEnv !== undefined ? prettyEnv !== '0' && prettyEnv !== '' : process.stdout.isTTY;
|
|
5
|
+
export const rootLogger = pino({
|
|
6
|
+
level: LOG_LEVEL,
|
|
7
|
+
...(pretty
|
|
8
|
+
? {
|
|
9
|
+
transport: {
|
|
10
|
+
target: 'pino-pretty',
|
|
11
|
+
options: {
|
|
12
|
+
colorize: true,
|
|
13
|
+
translateTime: 'SYS:HH:MM:ss',
|
|
14
|
+
ignore: 'pid,hostname,tag',
|
|
15
|
+
messageFormat: '{if tag}[{tag}] {end}{msg}',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
: {}),
|
|
20
|
+
});
|
|
4
21
|
setRootLogger(rootLogger);
|
|
5
22
|
export { getLogger };
|
|
@@ -19,7 +19,14 @@ export interface PluginAssetRecord {
|
|
|
19
19
|
schema: string;
|
|
20
20
|
web?: string;
|
|
21
21
|
css?: string;
|
|
22
|
+
local?: boolean;
|
|
22
23
|
}
|
|
23
24
|
export declare function discoverPluginAssets(): Promise<PluginAssetRecord[]>;
|
|
25
|
+
export interface RuntimeRecord {
|
|
26
|
+
packageName: string;
|
|
27
|
+
installedVersion: string;
|
|
28
|
+
local: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function discoverRuntime(): Promise<RuntimeRecord | null>;
|
|
24
31
|
export declare function discoverPlugins(): Promise<PluginManifest[]>;
|
|
25
32
|
export declare function loadServerHooks(): Promise<Record<string, PluginHooks>>;
|
package/dist/plugin-discovery.js
CHANGED
|
@@ -6,8 +6,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import { readdir, readFile } from 'node:fs/promises';
|
|
10
|
-
import { join } from 'node:path';
|
|
9
|
+
import { readdir, readFile, realpath } from 'node:fs/promises';
|
|
10
|
+
import { join, sep } from 'node:path';
|
|
11
11
|
import { pathToFileURL } from 'node:url';
|
|
12
12
|
import { getLogger } from '@coffer-org/sdk/logger';
|
|
13
13
|
const log = getLogger('discovery');
|
|
@@ -40,6 +40,15 @@ export function resolveAssetUrls(id, coffer) {
|
|
|
40
40
|
out.css = `${base}/web.css`;
|
|
41
41
|
return out;
|
|
42
42
|
}
|
|
43
|
+
async function isLocalPackage(dir) {
|
|
44
|
+
try {
|
|
45
|
+
const real = await realpath(dir);
|
|
46
|
+
return !real.includes(`${sep}node_modules${sep}`);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
43
52
|
export async function discoverPluginAssets() {
|
|
44
53
|
const nm = join(process.cwd(), 'node_modules');
|
|
45
54
|
const names = await pkgNames(nm);
|
|
@@ -62,6 +71,7 @@ export async function discoverPluginAssets() {
|
|
|
62
71
|
schema: urls.schema,
|
|
63
72
|
web: urls.web,
|
|
64
73
|
css: urls.css,
|
|
74
|
+
local: await isLocalPackage(join(nm, name)),
|
|
65
75
|
});
|
|
66
76
|
}
|
|
67
77
|
catch (e) {
|
|
@@ -70,6 +80,19 @@ export async function discoverPluginAssets() {
|
|
|
70
80
|
}
|
|
71
81
|
return out;
|
|
72
82
|
}
|
|
83
|
+
const RUNTIME_PACKAGE = '@coffer-org/meta';
|
|
84
|
+
export async function discoverRuntime() {
|
|
85
|
+
const dir = join(process.cwd(), 'node_modules', RUNTIME_PACKAGE);
|
|
86
|
+
try {
|
|
87
|
+
const pkg = JSON.parse(await readFile(join(dir, 'package.json'), 'utf8'));
|
|
88
|
+
if (!pkg.version)
|
|
89
|
+
return null;
|
|
90
|
+
return { packageName: RUNTIME_PACKAGE, installedVersion: pkg.version, local: await isLocalPackage(dir) };
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
73
96
|
async function loadManifest(nm, name) {
|
|
74
97
|
try {
|
|
75
98
|
const pkg = JSON.parse(await readFile(join(nm, name, 'package.json'), 'utf8'));
|
package/dist/plugin-hooks.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EntityManager } from '@mikro-orm/core';
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
import type { ColumnType } from '@coffer-org/sdk/fields';
|
|
4
|
+
import { type Logger } from '@coffer-org/sdk/logger';
|
|
4
5
|
export interface TableOps {
|
|
5
6
|
renameColumn(from: string, to: string): Promise<void>;
|
|
6
7
|
fill(column: string, value: string | number | boolean | null, opts?: {
|
|
@@ -20,7 +21,9 @@ export interface Migration {
|
|
|
20
21
|
}
|
|
21
22
|
export interface PluginCtx {
|
|
22
23
|
em: EntityManager;
|
|
24
|
+
log: Logger;
|
|
23
25
|
}
|
|
26
|
+
export declare function pluginCtx(id: string, em: EntityManager): PluginCtx;
|
|
24
27
|
export interface Seed {
|
|
25
28
|
name: string;
|
|
26
29
|
run(ctx: PluginCtx): Promise<void> | void;
|
package/dist/plugin-hooks.js
CHANGED
package/dist/plugin-runtime.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getLogger } from '@coffer-org/sdk/logger';
|
|
|
4
4
|
import { initDb, getOrm, getEm } from "./db.js";
|
|
5
5
|
import { syncSchema } from "./schema-sync.js";
|
|
6
6
|
import { systemEntities, buildPluginEntities, moduleTableName } from "./entity-schema.js";
|
|
7
|
-
import { pluginHooks } from "./plugin-hooks.js";
|
|
7
|
+
import { pluginHooks, pluginCtx } from "./plugin-hooks.js";
|
|
8
8
|
import { discoverPlugins, loadServerHooks } from "./plugin-discovery.js";
|
|
9
9
|
import { runMigrations, assertSafeRequired } from "./migrations.js";
|
|
10
10
|
import { runSeeds } from "./seeds.js";
|
|
@@ -78,7 +78,7 @@ export async function initPlugins() {
|
|
|
78
78
|
const h = pluginHooks[p.id];
|
|
79
79
|
try {
|
|
80
80
|
if (h?.init) {
|
|
81
|
-
await h.init(
|
|
81
|
+
await h.init(pluginCtx(p.id, getEm().fork()));
|
|
82
82
|
log.debug(`${p.id}: init ✓`);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -100,7 +100,7 @@ export async function teardownPlugins() {
|
|
|
100
100
|
if (!h?.teardown)
|
|
101
101
|
continue;
|
|
102
102
|
try {
|
|
103
|
-
await h.teardown(
|
|
103
|
+
await h.teardown(pluginCtx(p.id, getEm().fork()));
|
|
104
104
|
log.debug(`${p.id}: teardown ✓`);
|
|
105
105
|
}
|
|
106
106
|
catch (e) {
|
|
@@ -111,7 +111,7 @@ export async function teardownPlugins() {
|
|
|
111
111
|
export async function teardownPlugin(id) {
|
|
112
112
|
const h = pluginHooks[id];
|
|
113
113
|
if (h?.teardown) {
|
|
114
|
-
await h.teardown(
|
|
114
|
+
await h.teardown(pluginCtx(id, getEm().fork()));
|
|
115
115
|
log.debug(`${id}: teardown ✓`);
|
|
116
116
|
}
|
|
117
117
|
}
|
package/dist/plugin-updates.d.ts
CHANGED
|
@@ -16,6 +16,16 @@ export interface AllUpdateTarget {
|
|
|
16
16
|
to: string;
|
|
17
17
|
}
|
|
18
18
|
export declare function resolveAllUpdateTargets(assets: PluginAssetRecord[], latestById: Map<string, string | null>): AllUpdateTarget[];
|
|
19
|
+
export interface RuntimeUpdateTarget {
|
|
20
|
+
packageName: string;
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function resolveRuntimeTarget(runtime: {
|
|
25
|
+
packageName: string;
|
|
26
|
+
installedVersion: string;
|
|
27
|
+
local: boolean;
|
|
28
|
+
} | null, latestVersion: string | null): RuntimeUpdateTarget | null;
|
|
19
29
|
export declare function runNpmInstall(specs: string[], cwd: string): Promise<{
|
|
20
30
|
ok: boolean;
|
|
21
31
|
stderr: string;
|
package/dist/plugin-updates.js
CHANGED
|
@@ -49,6 +49,13 @@ export function resolveAllUpdateTargets(assets, latestById) {
|
|
|
49
49
|
return [{ id: rec.id, packageName: rec.packageName, from: rec.version, to: latest }];
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
+
export function resolveRuntimeTarget(runtime, latestVersion) {
|
|
53
|
+
if (!runtime || runtime.local)
|
|
54
|
+
return null;
|
|
55
|
+
if (!latestVersion || latestVersion === runtime.installedVersion)
|
|
56
|
+
return null;
|
|
57
|
+
return { packageName: runtime.packageName, from: runtime.installedVersion, to: latestVersion };
|
|
58
|
+
}
|
|
52
59
|
export function runNpmInstall(specs, cwd) {
|
|
53
60
|
return new Promise((resolve) => {
|
|
54
61
|
execFile('npm', ['install', ...specs], { cwd, timeout: 120_000 + 30_000 * specs.length, encoding: 'utf8' }, (err, _stdout, stderr) => {
|
package/dist/plugins-api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { createReadStream, existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { discoverPluginAssets } from "./plugin-discovery.js";
|
|
3
|
+
import { discoverPluginAssets, discoverRuntime } from "./plugin-discovery.js";
|
|
4
4
|
import { getPlugins, readDisabled } from "./plugin-runtime.js";
|
|
5
5
|
import { checkLatestVersion } from "./plugin-updates.js";
|
|
6
6
|
const nmRoot = () => join(process.cwd(), 'node_modules');
|
|
@@ -12,7 +12,7 @@ export async function buildPluginListResponse(plugins, assets, disabled) {
|
|
|
12
12
|
return {
|
|
13
13
|
id: p.id,
|
|
14
14
|
installedVersion: a?.version ?? p.version,
|
|
15
|
-
latestVersion: a ? await checkLatestVersion(a.packageName) : null,
|
|
15
|
+
latestVersion: a && !a.local ? await checkLatestVersion(a.packageName) : null,
|
|
16
16
|
packageName: a?.packageName ?? null,
|
|
17
17
|
dependsOn: p.dependsOn,
|
|
18
18
|
enabled: !disabled.has(p.id),
|
|
@@ -30,6 +30,13 @@ export async function registerPluginsApi(app) {
|
|
|
30
30
|
const [plugins, assets, disabled] = await Promise.all([getPlugins(), discoverPluginAssets(), readDisabled()]);
|
|
31
31
|
return buildPluginListResponse(plugins, assets, disabled);
|
|
32
32
|
});
|
|
33
|
+
app.get('/api/runtime', async () => {
|
|
34
|
+
const runtime = await discoverRuntime();
|
|
35
|
+
if (!runtime)
|
|
36
|
+
return { installedVersion: null, latestVersion: null };
|
|
37
|
+
const latestVersion = runtime.local ? null : await checkLatestVersion(runtime.packageName);
|
|
38
|
+
return { installedVersion: runtime.installedVersion, latestVersion };
|
|
39
|
+
});
|
|
33
40
|
app.get('/plugins/:id/:file', async (req, reply) => {
|
|
34
41
|
const { id, file } = req.params;
|
|
35
42
|
const key = ASSET_KEY[file];
|
package/dist/seeds.d.ts
CHANGED
package/dist/seeds.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pluginCtx } from "./plugin-hooks.js";
|
|
1
2
|
import { getLogger } from '@coffer-org/sdk/logger';
|
|
2
3
|
const log = getLogger('seeds');
|
|
3
4
|
export function validateSeeds(pluginId, list) {
|
|
@@ -16,7 +17,7 @@ export async function runSeeds({ em, plugins, hooks }) {
|
|
|
16
17
|
const applied = new Set((await conn.execute(`SELECT name FROM _seeds WHERE plugin_id = ?`, [p.id])).map((r) => r.name));
|
|
17
18
|
const pending = list.filter((s) => !applied.has(s.name));
|
|
18
19
|
for (const s of pending) {
|
|
19
|
-
await s.run(
|
|
20
|
+
await s.run(pluginCtx(p.id, em.fork()));
|
|
20
21
|
await conn.execute(`INSERT INTO _seeds (plugin_id, name) VALUES (?, ?)`, [p.id, s.name]);
|
|
21
22
|
}
|
|
22
23
|
if (pending.length)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffer-org/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24"
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"fastify": "^5.2.1",
|
|
36
36
|
"open-graph-scraper": "^6.11.0",
|
|
37
37
|
"pino": "^9.14.0",
|
|
38
|
+
"pino-pretty": "^13.1.3",
|
|
38
39
|
"zod": "^4.4.3"
|
|
39
40
|
},
|
|
40
41
|
"optionalDependencies": {
|