Skip to content
This repository was archived by the owner on Aug 2, 2025. It is now read-only.

Commit 871ab4f

Browse files
committed
Feat: Stack validation
1 parent ec0c79a commit 871ab4f

7 files changed

Lines changed: 88 additions & 29 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- name: Run unit tests
3030
run: |
3131
export PAD_NEW_LINES=false
32-
docker compose -f docker/docker-compose.unit-test.yaml up -d
32+
docker compose -f docker/docker-compose.dev.yaml up -d
3333
bun test
3434
3535
- name: Log unit test files

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"dockerode": "^4.0.6",
3434
"elysia": "latest",
3535
"elysia-remote-dts": "^1.0.3",
36+
"js-yaml": "^4.1.0",
3637
"knip": "latest",
3738
"logestic": "^1.2.4",
3839
"split2": "^4.2.0",
@@ -43,6 +44,7 @@
4344
"@biomejs/biome": "1.9.4",
4445
"@types/bun": "latest",
4546
"@types/dockerode": "^3.3.39",
47+
"@types/js-yaml": "^4.0.9",
4648
"@types/node": "^22.15.29",
4749
"@types/split2": "^4.2.3",
4850
"bun-types": "latest",

src/core/database/database.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ export function init() {
4747
version INTEGER NOT NULL,
4848
custom BOOLEAN NOT NULL,
4949
source TEXT NOT NULL,
50-
compose_spec TEXT NOT NULL
50+
compose_spec TEXT NOT NULL,
51+
status TEXT NOT NULL
5152
);
5253
5354
CREATE TABLE IF NOT EXISTS docker_hosts (

src/core/database/stacks.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
import type { stacks_config } from "~/typings/database";
2-
import type { Stack } from "~/typings/docker-compose";
32
import { findObjectByKey } from "../utils/helpers";
43
import { db } from "./database";
54
import { executeDbOperation } from "./helper";
65

76
const stmt = {
87
insert: db.prepare(`
98
INSERT INTO stacks_config (
10-
name, version, custom, source, compose_spec
11-
) VALUES (?, ?, ?, ?, ?)
9+
name, version, custom, source, compose_spec, status
10+
) VALUES (?, ?, ?, ?, ?, ?)
1211
`),
1312
selectAll: db.prepare(`
14-
SELECT id, name, version, custom, source, compose_spec
13+
SELECT id, name, version, custom, source, compose_spec, status
1514
FROM stacks_config
16-
ORDER BY name DESC
15+
ORDER BY id DESC
1716
`),
1817
update: db.prepare(`
1918
UPDATE stacks_config
2019
SET name = ?, custom = ?, source = ?, compose_spec = ?
21-
WHERE name = ?
20+
WHERE id = ?
2221
`),
22+
setStatus: db.prepare(`
23+
UPDATE stacks_config
24+
SET status = ?
25+
WHERE id = ?
26+
`),
2327
delete: db.prepare("DELETE FROM stacks_config WHERE id = ?"),
2428
};
2529

@@ -31,6 +35,7 @@ export function addStack(stack: stacks_config) {
3135
stack.custom,
3236
stack.source,
3337
stack.compose_spec,
38+
"active",
3439
),
3540
);
3641

@@ -40,7 +45,7 @@ export function addStack(stack: stacks_config) {
4045
export function getStacks() {
4146
return executeDbOperation("Get Stacks", () =>
4247
stmt.selectAll.all(),
43-
) as Stack[];
48+
) as stacks_config[];
4449
}
4550

4651
export function deleteStack(id: number) {
@@ -54,13 +59,27 @@ export function deleteStack(id: number) {
5459
}
5560

5661
export function updateStack(stack: stacks_config) {
57-
return executeDbOperation("Update Stack", () =>
62+
return executeDbOperation("Update Stack", () => {
63+
if (!stack.id) {
64+
throw new Error("Stack ID needed");
65+
}
5866
stmt.update.run(
67+
stack.id,
5968
stack.version,
6069
stack.custom,
6170
stack.source,
6271
stack.name,
6372
stack.compose_spec,
64-
),
65-
);
73+
);
74+
});
75+
}
76+
77+
export function setStackStatus(
78+
stack: stacks_config,
79+
status: "active" | "error" = "active",
80+
) {
81+
if (!stack.id) {
82+
throw new Error("Stack ID needed");
83+
}
84+
stmt.setStatus.run(status, stack.id);
6685
}

src/core/stacks/checker.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import yaml from "js-yaml";
2+
import { dbFunctions } from "../database";
3+
import { logger } from "../utils/logger";
4+
5+
const stacks = dbFunctions.getStacks();
6+
7+
export async function checkStacks() {
8+
logger.debug(`Checking ${stacks.length} stack(s)`);
9+
for (const stack of stacks) {
10+
try {
11+
logger.debug(`Checking ${stack.id}`);
12+
const composeFile = Bun.file(
13+
`stacks/${stack.id}-${stack.name}/docker-compose.yaml`,
14+
);
15+
16+
if (!(await composeFile.exists())) {
17+
logger.error(`Stack (${stack.id} - ${stack.name}) has no compose file`);
18+
dbFunctions.setStackStatus(stack, "error");
19+
continue;
20+
}
21+
22+
if (
23+
stack.compose_spec !==
24+
JSON.stringify(yaml.load(await composeFile.text()))
25+
) {
26+
logger.error(
27+
`Stack (${stack.id} - ${stack.name}) does not match the saved compose file`,
28+
);
29+
logger.debug(`Database config: ${stack.compose_spec}`);
30+
logger.debug(
31+
`Compose config: ${JSON.stringify(
32+
yaml.load(await composeFile.text()),
33+
)}`,
34+
);
35+
dbFunctions.setStackStatus(stack, "error");
36+
continue;
37+
}
38+
39+
dbFunctions.setStackStatus(stack, "active");
40+
} catch (error) {
41+
const errorMsg = error instanceof Error ? error.message : String(error);
42+
logger.error(errorMsg);
43+
}
44+
}
45+
46+
logger.info("Checked stacks");
47+
}

src/core/stacks/controller.ts

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { postToClient } from "~/routes/live-stacks";
77
import type { stacks_config } from "~/typings/database";
88
import type { ComposeSpec, Stack } from "~/typings/docker-compose";
99
import { findObjectByKey } from "../utils/helpers";
10+
import { checkStacks } from "./checker";
1011

1112
const wrapProgressCallback = (progressCallback?: (log: string) => void) => {
1213
return progressCallback
13-
? (chunk: Buffer, streamSource?: "stdout" | "stderr") => {
14+
? (chunk: Buffer) => {
1415
const log = chunk.toString();
1516
progressCallback(log);
1617
}
@@ -197,6 +198,8 @@ export async function deployStack(stack_config: stacks_config): Promise<void> {
197198
},
198199
});
199200
throw new Error(errorMsg);
201+
} finally {
202+
await checkStacks();
200203
}
201204
}
202205

@@ -399,19 +402,3 @@ export async function getAllStacksStatus(): Promise<StacksStatus> {
399402
throw new Error(errorMsg);
400403
}
401404
}
402-
403-
async function backupStack(stackId: number) {
404-
if (!stackId) {
405-
throw new Error("No Stack ID provided");
406-
}
407-
408-
const stacks = dbFunctions.getStacks();
409-
410-
const stack = findObjectByKey(stacks, "id", stackId);
411-
412-
if (!stack) {
413-
throw new Error(`No Stack with Id: ${stackId} found`);
414-
}
415-
416-
const stack_path = `${stack.id}-${stack.name}`;
417-
}

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { liveLogs } from "~/routes/live-logs";
2424
import { backendLogs } from "~/routes/logs";
2525
import { stackRoutes } from "~/routes/stacks";
2626
import type { config } from "~/typings/database";
27+
import { checkStacks } from "./core/stacks/checker";
2728
import { liveStacks } from "./routes/live-stacks";
2829

2930
console.log("");
@@ -168,6 +169,8 @@ const initializeServer = async () => {
168169
);
169170
}
170171

172+
await checkStacks();
173+
171174
logger.info("Started server");
172175
console.log("----- [ ############## ]");
173176
} catch (error) {

0 commit comments

Comments
 (0)