@checkstack/backend 0.4.14 → 0.4.16
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/CHANGELOG.md +28 -0
- package/package.json +19 -16
- package/src/db.ts +1 -1
- package/src/services/config-service.ts +21 -2
- package/src/utils/plugin-discovery.test.ts +9 -6
- package/src/utils/plugin-discovery.ts +48 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @checkstack/backend
|
|
2
2
|
|
|
3
|
+
## 0.4.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c0c0ed2: Introduce generic "Login Flows" to allow authentication strategies to define their own interaction patterns (form, redirect, or oauth) during registration. This fixes an issue where LDAP login attempts were incorrectly routed through the standard social login flow by instead providing a dedicated credential collection form for LDAP.
|
|
8
|
+
- Updated dependencies [c0c0ed2]
|
|
9
|
+
- Updated dependencies [c0c0ed2]
|
|
10
|
+
- @checkstack/backend-api@0.9.0
|
|
11
|
+
- @checkstack/auth-common@0.6.0
|
|
12
|
+
- @checkstack/queue-api@0.2.8
|
|
13
|
+
- @checkstack/signal-backend@0.1.14
|
|
14
|
+
|
|
15
|
+
## 0.4.15
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 4d59cc7: Prune devDependencies and development-only source folders (like `core/scripts` and `test-utils-*`) from the production Docker image to reduce size and improve security.
|
|
20
|
+
- b839ccb: Security: Hardened production Docker image by upgrading Alpine system libraries, migrating to Drizzle beta (v1.0.0-beta.21), and implementing aggressive binary pruning to eliminate vulnerable build-time tools (esbuild/drizzle-kit).
|
|
21
|
+
- Updated dependencies [67158e2]
|
|
22
|
+
- @checkstack/api-docs-common@0.1.8
|
|
23
|
+
- @checkstack/auth-common@0.5.7
|
|
24
|
+
- @checkstack/backend-api@0.8.2
|
|
25
|
+
- @checkstack/common@0.6.4
|
|
26
|
+
- @checkstack/drizzle-helper@0.0.4
|
|
27
|
+
- @checkstack/queue-api@0.2.7
|
|
28
|
+
- @checkstack/signal-backend@0.1.13
|
|
29
|
+
- @checkstack/signal-common@0.1.8
|
|
30
|
+
|
|
3
31
|
## 0.4.14
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/backend",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.16",
|
|
4
|
+
"checkstack": {
|
|
5
|
+
"type": "backend"
|
|
6
|
+
},
|
|
4
7
|
"type": "module",
|
|
5
8
|
"scripts": {
|
|
6
9
|
"dev": "bun --env-file=../../.env --watch src/index.ts",
|
|
@@ -10,33 +13,33 @@
|
|
|
10
13
|
"lint:code": "eslint . --max-warnings 0"
|
|
11
14
|
},
|
|
12
15
|
"dependencies": {
|
|
13
|
-
"@checkstack/api-docs-common": "0.1.
|
|
14
|
-
"@checkstack/auth-common": "0.5.
|
|
15
|
-
"@checkstack/backend-api": "0.8.
|
|
16
|
-
"@checkstack/common": "0.6.
|
|
17
|
-
"@checkstack/drizzle-helper": "0.0.
|
|
18
|
-
"@checkstack/queue-api": "0.2.
|
|
19
|
-
"@checkstack/signal-backend": "0.1.
|
|
20
|
-
"@checkstack/signal-common": "0.1.
|
|
16
|
+
"@checkstack/api-docs-common": "0.1.8",
|
|
17
|
+
"@checkstack/auth-common": "0.5.7",
|
|
18
|
+
"@checkstack/backend-api": "0.8.2",
|
|
19
|
+
"@checkstack/common": "0.6.4",
|
|
20
|
+
"@checkstack/drizzle-helper": "0.0.4",
|
|
21
|
+
"@checkstack/queue-api": "0.2.7",
|
|
22
|
+
"@checkstack/signal-backend": "0.1.13",
|
|
23
|
+
"@checkstack/signal-common": "0.1.8",
|
|
21
24
|
"@hono/zod-validator": "^0.7.6",
|
|
22
|
-
"@orpc/client": "^1.13.
|
|
25
|
+
"@orpc/client": "^1.13.14",
|
|
26
|
+
"@orpc/contract": "^1.13.14",
|
|
23
27
|
"@orpc/openapi": "^1.13.2",
|
|
24
28
|
"@orpc/server": "^1.13.2",
|
|
25
29
|
"@orpc/zod": "^1.13.2",
|
|
26
30
|
"better-auth": "^1.4.7",
|
|
27
|
-
"drizzle-orm": "^0.45.
|
|
28
|
-
"hono": "^4.
|
|
31
|
+
"drizzle-orm": "^0.45.0",
|
|
32
|
+
"hono": "^4.12.14",
|
|
29
33
|
"jose": "^6.1.3",
|
|
30
34
|
"pg": "^8.11.0",
|
|
31
35
|
"winston": "^3.19.0",
|
|
32
36
|
"zod": "^4.2.1"
|
|
33
37
|
},
|
|
34
38
|
"devDependencies": {
|
|
35
|
-
"drizzle-kit": "^0.31.8",
|
|
36
39
|
"@types/pg": "^8.11.0",
|
|
37
40
|
"@types/bun": "latest",
|
|
38
|
-
"@checkstack/tsconfig": "0.0.
|
|
39
|
-
"@checkstack/scripts": "0.1.
|
|
40
|
-
"@checkstack/test-utils-backend": "0.1.
|
|
41
|
+
"@checkstack/tsconfig": "0.0.4",
|
|
42
|
+
"@checkstack/scripts": "0.1.2",
|
|
43
|
+
"@checkstack/test-utils-backend": "0.1.13"
|
|
41
44
|
}
|
|
42
45
|
}
|
package/src/db.ts
CHANGED
|
@@ -257,6 +257,11 @@ export class ConfigServiceImpl implements ConfigService {
|
|
|
257
257
|
// Decrypt secrets
|
|
258
258
|
const decryptedData = this.decryptSecrets(schema, migratedData);
|
|
259
259
|
|
|
260
|
+
// Auto-save if migrated
|
|
261
|
+
if (versioned.needsMigration(storedRecord)) {
|
|
262
|
+
await this.set(configId, schema, version, decryptedData as T, migrations);
|
|
263
|
+
}
|
|
264
|
+
|
|
260
265
|
// Validate with schema
|
|
261
266
|
return schema.parse(decryptedData);
|
|
262
267
|
}
|
|
@@ -294,8 +299,22 @@ export class ConfigServiceImpl implements ConfigService {
|
|
|
294
299
|
// Parse and migrate the stored record
|
|
295
300
|
const migratedData = await versioned.parse(storedRecord);
|
|
296
301
|
|
|
297
|
-
//
|
|
298
|
-
const
|
|
302
|
+
// Decrypt for auto-save (ConfigService can decrypt locally for migration persistence)
|
|
303
|
+
const decryptedData = this.decryptSecrets(schema, migratedData);
|
|
304
|
+
|
|
305
|
+
// Auto-save if migrated
|
|
306
|
+
if (versioned.needsMigration(storedRecord)) {
|
|
307
|
+
await this.set(
|
|
308
|
+
configId,
|
|
309
|
+
schema,
|
|
310
|
+
version,
|
|
311
|
+
decryptedData as T,
|
|
312
|
+
migrations
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Redact secrets for return
|
|
317
|
+
const redactedData = this.redactSecrets(schema, decryptedData);
|
|
299
318
|
|
|
300
319
|
return redactedData as Partial<T>;
|
|
301
320
|
}
|
|
@@ -38,6 +38,7 @@ describe("extractPluginMetadata", () => {
|
|
|
38
38
|
name: "@checkstack/test-backend",
|
|
39
39
|
version: "0.0.1",
|
|
40
40
|
type: "module",
|
|
41
|
+
checkstack: { type: "backend" },
|
|
41
42
|
})
|
|
42
43
|
);
|
|
43
44
|
|
|
@@ -57,6 +58,7 @@ describe("extractPluginMetadata", () => {
|
|
|
57
58
|
mockReadFileSync.mockReturnValue(
|
|
58
59
|
JSON.stringify({
|
|
59
60
|
name: "@checkstack/test-frontend",
|
|
61
|
+
checkstack: { type: "frontend" },
|
|
60
62
|
})
|
|
61
63
|
);
|
|
62
64
|
|
|
@@ -71,6 +73,7 @@ describe("extractPluginMetadata", () => {
|
|
|
71
73
|
mockReadFileSync.mockReturnValue(
|
|
72
74
|
JSON.stringify({
|
|
73
75
|
name: "@checkstack/test-common",
|
|
76
|
+
checkstack: { type: "common" },
|
|
74
77
|
})
|
|
75
78
|
);
|
|
76
79
|
|
|
@@ -159,13 +162,13 @@ describe("discoverLocalPlugins", () => {
|
|
|
159
162
|
// Mock package.json reads
|
|
160
163
|
mockReadFileSync.mockImplementation(((filePath: string) => {
|
|
161
164
|
if (filePath.includes("auth-backend")) {
|
|
162
|
-
return JSON.stringify({ name: "@checkstack/auth-backend" });
|
|
165
|
+
return JSON.stringify({ name: "@checkstack/auth-backend", checkstack: { type: "backend" } });
|
|
163
166
|
}
|
|
164
167
|
if (filePath.includes("catalog-backend")) {
|
|
165
|
-
return JSON.stringify({ name: "@checkstack/catalog-backend" });
|
|
168
|
+
return JSON.stringify({ name: "@checkstack/catalog-backend", checkstack: { type: "backend" } });
|
|
166
169
|
}
|
|
167
170
|
if (filePath.includes("invalid-plugin")) {
|
|
168
|
-
return JSON.stringify({ name: "@checkstack/invalid-plugin" });
|
|
171
|
+
return JSON.stringify({ name: "@checkstack/invalid-plugin" }); // Missing block
|
|
169
172
|
}
|
|
170
173
|
return "{}";
|
|
171
174
|
}) as typeof mockReadFileSync);
|
|
@@ -198,13 +201,13 @@ describe("discoverLocalPlugins", () => {
|
|
|
198
201
|
|
|
199
202
|
mockReadFileSync.mockImplementation(((filePath: string) => {
|
|
200
203
|
if (filePath.includes("auth-backend")) {
|
|
201
|
-
return JSON.stringify({ name: "@checkstack/auth-backend" });
|
|
204
|
+
return JSON.stringify({ name: "@checkstack/auth-backend", checkstack: { type: "backend" } });
|
|
202
205
|
}
|
|
203
206
|
if (filePath.includes("auth-frontend")) {
|
|
204
|
-
return JSON.stringify({ name: "@checkstack/auth-frontend" });
|
|
207
|
+
return JSON.stringify({ name: "@checkstack/auth-frontend", checkstack: { type: "frontend" } });
|
|
205
208
|
}
|
|
206
209
|
if (filePath.includes("auth-common")) {
|
|
207
|
-
return JSON.stringify({ name: "@checkstack/auth-common" });
|
|
210
|
+
return JSON.stringify({ name: "@checkstack/auth-common", checkstack: { type: "common" } });
|
|
208
211
|
}
|
|
209
212
|
return "{}";
|
|
210
213
|
}) as typeof mockReadFileSync);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import { eq, and } from "drizzle-orm";
|
|
3
|
+
import { eq, and, notInArray } from "drizzle-orm";
|
|
4
4
|
import type { SafeDatabase } from "@checkstack/backend-api";
|
|
5
5
|
import { plugins } from "../schema";
|
|
6
|
+
import { rootLogger } from "../logger";
|
|
6
7
|
|
|
7
8
|
export interface PluginMetadata {
|
|
8
9
|
packageName: string; // From package.json "name"
|
|
@@ -34,16 +35,28 @@ export function extractPluginMetadata({
|
|
|
34
35
|
return undefined;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
// Transition: Strictly require checkstack metadata block
|
|
39
|
+
if (!pkgJson.checkstack || typeof pkgJson.checkstack !== "object") {
|
|
40
|
+
if (pkgJson.name.endsWith("-backend") || pkgJson.name.endsWith("-frontend") || pkgJson.name.endsWith("-common")) {
|
|
41
|
+
rootLogger.debug(`⏭️ Skipping package '${pkgJson.name}': Missing 'checkstack' metadata block in package.json`);
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Exclusion: Never discover the host platform itself as a plugin
|
|
47
|
+
if (pkgJson.name === "@checkstack/backend") {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const type = pkgJson.checkstack.type;
|
|
52
|
+
if (type === "tooling") {
|
|
53
|
+
rootLogger.debug(`⏭️ Skipping package '${pkgJson.name}': Identified as 'tooling'`);
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (type !== "backend" && type !== "frontend" && type !== "common") {
|
|
58
|
+
rootLogger.debug(`⏭️ Skipping package '${pkgJson.name}': Invalid checkstack type '${type}'`);
|
|
59
|
+
return undefined;
|
|
47
60
|
}
|
|
48
61
|
|
|
49
62
|
return {
|
|
@@ -52,7 +65,8 @@ export function extractPluginMetadata({
|
|
|
52
65
|
type,
|
|
53
66
|
enabled: true, // Local plugins are always enabled
|
|
54
67
|
};
|
|
55
|
-
} catch {
|
|
68
|
+
} catch (error) {
|
|
69
|
+
rootLogger.debug(`⚠️ Failed to read package.json for ${pluginDir}:`, error);
|
|
56
70
|
return undefined;
|
|
57
71
|
}
|
|
58
72
|
}
|
|
@@ -154,4 +168,26 @@ export async function syncPluginsToDatabase({
|
|
|
154
168
|
}
|
|
155
169
|
}
|
|
156
170
|
}
|
|
171
|
+
|
|
172
|
+
// 3. Prune local plugins from DB that are no longer present on disk
|
|
173
|
+
// This is critical for Docker production builds where folders are pruned
|
|
174
|
+
const localPackageNames = localPlugins.map((p) => p.packageName);
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const whereCondition = and(
|
|
178
|
+
eq(plugins.isUninstallable, false),
|
|
179
|
+
localPackageNames.length > 0
|
|
180
|
+
? notInArray(plugins.name, localPackageNames)
|
|
181
|
+
: undefined,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// satisfy unicorn/prefer-ternary
|
|
185
|
+
await (localPackageNames.length > 0
|
|
186
|
+
? db.delete(plugins).where(whereCondition)
|
|
187
|
+
: db.delete(plugins).where(eq(plugins.isUninstallable, false)));
|
|
188
|
+
|
|
189
|
+
rootLogger.debug(" -> Local plugin synchronization complete");
|
|
190
|
+
} catch (error) {
|
|
191
|
+
rootLogger.error("❌ Failed to prune stale plugins from database:", error);
|
|
192
|
+
}
|
|
157
193
|
}
|