Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion apps/server/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mkdirSync } from "fs";
import { defineConfig } from "drizzle-kit";

// Get database type from environment or default to sqlite
Expand All @@ -10,14 +11,25 @@ const baseConfig = {
strict: true,
};

// Resolve the SQLite database URL and ensure its directory exists
// Match the default used by @digital-alchemy/synapse: file:<cwd>/synapse_storage.db
const sqliteUrl = process.env.DATABASE_URL || "file:./synapse_storage.db";
if (databaseType === "sqlite") {
// Strip the "file:" prefix to get the filesystem path, then ensure the directory exists
const filePath = sqliteUrl.replace(/^file:/, "");
const lastSlash = filePath.lastIndexOf("/");
const dirPath = lastSlash > 0 ? filePath.slice(0, lastSlash) : ".";
mkdirSync(dirPath, { recursive: true });
}

// Database-specific configurations
const configs = {
sqlite: {
...baseConfig,
dialect: "sqlite" as const,
out: "./migrations/sqlite",
dbCredentials: {
url: process.env.DATABASE_URL || "file:/data/synapse_storage.db",
url: sqliteUrl,
},
},
postgresql: {
Expand Down
48 changes: 48 additions & 0 deletions apps/server/migrations/mysql/0001_automation_versioning.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- Add active_version_id to stored_automation
ALTER TABLE `stored_automation` ADD `active_version_id` varchar(36);
--> statement-breakpoint
ALTER TABLE `stored_automation` DROP COLUMN `draft`;
--> statement-breakpoint
ALTER TABLE `stored_automation` DROP COLUMN `version`;
--> statement-breakpoint
CREATE TABLE `automation_versions` (
`activated_from_version_id` varchar(36),
`automation_id` varchar(36) NOT NULL,
`body` text NOT NULL,
`date` timestamp NOT NULL,
`documentation` text,
`has_code_change` varchar(10) NOT NULL DEFAULT 'true',
`has_notes_change` varchar(10) NOT NULL DEFAULT 'false',
`id` varchar(36) NOT NULL,
`is_active` varchar(10) NOT NULL DEFAULT 'false',
`is_draft` varchar(10) NOT NULL DEFAULT 'false',
`name` varchar(255),
`notes` text,
`parent_version_id` varchar(36),
`was_auto_saved` varchar(10) NOT NULL DEFAULT 'false',
`written_by_ai` varchar(10) NOT NULL DEFAULT 'false',
CONSTRAINT `automation_versions_id` PRIMARY KEY(`id`)
);
--> statement-breakpoint
-- Seed initial versions from existing automations
INSERT INTO `automation_versions` (
`id`, `automation_id`, `body`, `date`, `documentation`,
`has_code_change`, `has_notes_change`,
`is_active`, `is_draft`, `name`,
`was_auto_saved`, `written_by_ai`
)
SELECT
UUID(),
`id`,
`body`,
`create_date`,
`documentation`,
'true', 'false',
'true', 'false', 'Initial version',
'false', 'false'
FROM `stored_automation`;
--> statement-breakpoint
-- Point each automation at its initial version
UPDATE `stored_automation` sa
JOIN `automation_versions` av ON av.`automation_id` = sa.`id`
SET sa.`active_version_id` = av.`id`;
48 changes: 48 additions & 0 deletions apps/server/migrations/postgresql/0001_automation_versioning.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- Add active_version_id to stored_automation
ALTER TABLE "stored_automation" ADD "active_version_id" text;
--> statement-breakpoint
ALTER TABLE "stored_automation" DROP COLUMN "draft";
--> statement-breakpoint
ALTER TABLE "stored_automation" DROP COLUMN "version";
--> statement-breakpoint
CREATE TABLE "automation_versions" (
"activated_from_version_id" text,
"automation_id" text NOT NULL,
"body" text NOT NULL,
"date" timestamp NOT NULL,
"documentation" text,
"has_code_change" text NOT NULL DEFAULT 'true',
"has_notes_change" text NOT NULL DEFAULT 'false',
"id" text PRIMARY KEY NOT NULL,
"is_active" text NOT NULL DEFAULT 'false',
"is_draft" text NOT NULL DEFAULT 'false',
"name" text,
"notes" text,
"parent_version_id" text,
"was_auto_saved" text NOT NULL DEFAULT 'false',
"written_by_ai" text NOT NULL DEFAULT 'false'
);
--> statement-breakpoint
-- Seed initial versions from existing automations
INSERT INTO "automation_versions" (
"id", "automation_id", "body", "date", "documentation",
"has_code_change", "has_notes_change",
"is_active", "is_draft", "name",
"was_auto_saved", "written_by_ai"
)
SELECT
gen_random_uuid()::text,
"id",
"body",
"create_date",
"documentation",
'true', 'false',
'true', 'false', 'Initial version',
'false', 'false'
FROM "stored_automation";
--> statement-breakpoint
-- Point each automation at its initial version
UPDATE "stored_automation" sa
SET "active_version_id" = av."id"
FROM "automation_versions" av
WHERE av."automation_id" = sa."id";
19 changes: 19 additions & 0 deletions apps/server/migrations/sqlite/0001_automation_versioning.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ALTER TABLE `stored_automation` ADD `active_version_id` text;
--> statement-breakpoint
CREATE TABLE `automation_versions` (
`activated_from_version_id` text,
`automation_id` text NOT NULL,
`body` text NOT NULL,
`date` text NOT NULL,
`documentation` text,
`has_code_change` text NOT NULL DEFAULT 'true',
`has_notes_change` text NOT NULL DEFAULT 'false',
`id` text PRIMARY KEY NOT NULL,
`is_active` text NOT NULL DEFAULT 'false',
`is_draft` text NOT NULL DEFAULT 'false',
`name` text,
`notes` text,
`parent_version_id` text,
`was_auto_saved` text NOT NULL DEFAULT 'false',
`written_by_ai` text NOT NULL DEFAULT 'false'
);
7 changes: 7 additions & 0 deletions apps/server/migrations/sqlite/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"when": 1765403410570,
"tag": "0000_quick_stark_industries",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1774300000000,
"tag": "0001_automation_versioning",
"breakpoints": true
}
]
}
6 changes: 3 additions & 3 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"name": "@code-glue/server",
"description": "Server component",
"scripts": {
"start": "SERVE_STATIC=true tsx src/app/environments/local/main.mts",
"start:inspect": "SERVE_STATIC=true tsx --inspect src/app/environments/local/main.mts",
"start:brk": "SERVE_STATIC=true tsx --inspect-brk src/app/environments/local/main.mts",
"start": "drizzle-kit migrate && SERVE_STATIC=true tsx src/app/environments/local/main.mts",
"start:inspect": "drizzle-kit migrate && SERVE_STATIC=true tsx --inspect src/app/environments/local/main.mts",
"start:brk": "drizzle-kit migrate && SERVE_STATIC=true tsx --inspect-brk src/app/environments/local/main.mts",
"build": "tsc -p tsconfig.build.json",
"test": "./scripts/test.sh",
"lint": "eslint src",
Expand Down
4 changes: 4 additions & 0 deletions apps/server/src/app/app.module.mts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { LIB_MODULE_PATCHER } from "../patch/patch.module.mts";
import {
AppController,
AutomationController,
AutomationVersionController,
SynapseEntitiesController,
TypesController,
VariablesController,
} from "./controllers/index.mts";
import { AutomationLogic } from "./services/automation.service.mts";
import { AutomationVersionLogic } from "./services/automation-version.service.mts";
import { HeaderBlockService } from "./services/header-block.service.mts";
import { CodeGlueLogger } from "./services/logger.service.mts";
import { StatsService } from "./services/stats.service.mts";
Expand Down Expand Up @@ -54,10 +56,12 @@ export const CODE_GLUE_APP = CreateApplication({
services: {
AppController,
AutomationController,
AutomationVersionController,
SynapseEntitiesController,
TypesController,
VariablesController,
automation: AutomationLogic,
automationVersion: AutomationVersionLogic,
header: HeaderBlockService,
logger: CodeGlueLogger,
stats: StatsService,
Expand Down
84 changes: 84 additions & 0 deletions apps/server/src/app/controllers/automation-version.controller.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { TServiceParams } from "@digital-alchemy/core";
import { Type } from "@sinclair/typebox";

const params = Type.Object({ id: Type.String() });
const versionParams = Type.Object({ id: Type.String(), versionId: Type.String() });

const CreateDraftBody = Type.Object({
body: Type.String(),
parentVersionId: Type.Optional(Type.String()),
});

const UpdateDraftBody = Type.Object({
body: Type.Optional(Type.String()),
documentation: Type.Optional(Type.String()),
name: Type.Optional(Type.String()),
notes: Type.Optional(Type.String()),
});

const FinalizeBody = Type.Object({
name: Type.Optional(Type.String()),
notes: Type.Optional(Type.String()),
wasAutoSaved: Type.Boolean(),
makeActive: Type.Optional(Type.Boolean()),
});

export function AutomationVersionController({
http: { controller },
config,
code_glue,
}: TServiceParams) {
controller([config.code_glue.V1, "/automation/:id/versions"], app =>
app
// GET /api/v1/automation/:id/versions — list all versions for an automation
.get(
"/",
{ schema: { params } },
({ params: { id } }) => code_glue.automationVersion.listForAutomation(id),
)
// POST /api/v1/automation/:id/versions — create a draft version
.post(
"/",
{ schema: { body: CreateDraftBody, params } },
({ body, params: { id } }) =>
code_glue.automationVersion.createDraft(id, body.body, body.parentVersionId),
)
// PUT /api/v1/automation/:id/versions/:versionId — update draft body or finalize
.put(
"/:versionId",
{ schema: { body: UpdateDraftBody, params: versionParams } },
({ body, params: { versionId } }) =>
code_glue.automationVersion.updateDraft(versionId, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [tsc] <2379> reported by reviewdog 🐶
Argument of type '{ body: string | undefined; name: string | undefined; notes: string | undefined; }' is not assignable to parameter of type '{ body?: string; name?: string; notes?: string; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'body' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.

body: body.body,
name: body.name,
notes: body.notes,
}),
)
// POST /api/v1/automation/:id/versions/:versionId/finalize — finalize a draft
.post(
"/:versionId/finalize",
{ schema: { body: FinalizeBody, params: versionParams } },
({ body, params: { versionId } }) =>
code_glue.automationVersion.finalizeVersion(versionId, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [tsc] <2379> reported by reviewdog 🐶
Argument of type '{ name: string | undefined; notes: string | undefined; wasAutoSaved: boolean; makeActive: boolean | undefined; }' is not assignable to parameter of type '{ name?: string; notes?: string; wasAutoSaved: boolean; makeActive?: boolean; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'name' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.

name: body.name,
notes: body.notes,
wasAutoSaved: body.wasAutoSaved,
makeActive: body.makeActive,
}),
)
// POST /api/v1/automation/:id/versions/:versionId/activate — activate a version
.post(
"/:versionId/activate",
{ schema: { params: versionParams } },
({ params: { id, versionId } }) =>
code_glue.automationVersion.activateVersion(id, versionId),
)
// DELETE /api/v1/automation/:id/versions/:versionId — delete a version
.delete(
"/:versionId",
{ schema: { params: versionParams } },
({ params: { versionId } }) =>
code_glue.automationVersion.removeVersion(versionId),
),
);
}
1 change: 1 addition & 0 deletions apps/server/src/app/controllers/index.mts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./app.controller.mts";
export * from "./automation-version.controller.mts";
export * from "./automation.controller.mts";
export * from "./entities.controller.mts";
export * from "./types.controller.mts";
Expand Down
Loading
Loading