wip: generation-style migration
This commit is contained in:
parent
185dd20ad5
commit
44938e8c66
|
@ -1,6 +1,7 @@
|
||||||
import type { Config } from "drizzle-kit";
|
import type { Config } from "drizzle-kit";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
out: "./drizzle",
|
||||||
schema: "./src/server/db/schema/index.ts",
|
schema: "./src/server/db/schema/index.ts",
|
||||||
driver: "better-sqlite",
|
driver: "better-sqlite",
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
|
|
163
drizzle/0000_tidy_vermin.sql
Normal file
163
drizzle/0000_tidy_vermin.sql
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
CREATE TABLE `project_deployment` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`project_id` text NOT NULL,
|
||||||
|
`deployed_at` integer DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
`status` integer NOT NULL,
|
||||||
|
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `projects` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`friendly_name` text NOT NULL,
|
||||||
|
`internal_name` text NOT NULL,
|
||||||
|
`created_at` integer DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
`owner_id` text NOT NULL,
|
||||||
|
FOREIGN KEY (`owner_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `service` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`project_id` text NOT NULL,
|
||||||
|
`latest_generation_id` text NOT NULL DEFERRABLE INITIALLY DEFERRED,
|
||||||
|
`redeploy_secret` text NOT NULL,
|
||||||
|
`deployed_generation_id` text,
|
||||||
|
`created_at` integer DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||||
|
FOREIGN KEY (`latest_generation_id`) REFERENCES `service_generation`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
FOREIGN KEY (`deployed_generation_id`) REFERENCES `service_generation`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `service_deployment` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`project_deployment_id` text NOT NULL,
|
||||||
|
`service_id` text NOT NULL,
|
||||||
|
`created_at` integer DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
`deployed_by` text,
|
||||||
|
`build_logs` blob,
|
||||||
|
`status` integer NOT NULL,
|
||||||
|
FOREIGN KEY (`project_deployment_id`) REFERENCES `project_deployment`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||||
|
FOREIGN KEY (`service_id`) REFERENCES `service_generation`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||||
|
FOREIGN KEY (`deployed_by`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `service_domain` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`service_id` text NOT NULL,
|
||||||
|
`domain` text NOT NULL,
|
||||||
|
`internal_port` integer NOT NULL,
|
||||||
|
`https` integer DEFAULT false NOT NULL,
|
||||||
|
`force_ssl` integer DEFAULT false NOT NULL,
|
||||||
|
FOREIGN KEY (`service_id`) REFERENCES `service_generation`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `service_generation` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`service_id` text NOT NULL,
|
||||||
|
`deployment_id` text,
|
||||||
|
`source` integer NOT NULL,
|
||||||
|
`environment` text,
|
||||||
|
`docker_image` text,
|
||||||
|
`docker_registry_username` text,
|
||||||
|
`docker_registry_password` text,
|
||||||
|
`github_username` text,
|
||||||
|
`github_repository` text,
|
||||||
|
`github_branch` text,
|
||||||
|
`git_url` text,
|
||||||
|
`git_branch` text,
|
||||||
|
`build_method` integer DEFAULT 2 NOT NULL,
|
||||||
|
`build_path` text DEFAULT '/' NOT NULL,
|
||||||
|
`command` text,
|
||||||
|
`entrypoint` text,
|
||||||
|
`replicas` integer DEFAULT 1 NOT NULL,
|
||||||
|
`max_replicas_per_node` integer,
|
||||||
|
`deploy_mode` integer DEFAULT 1 NOT NULL,
|
||||||
|
`zero_downtime` integer DEFAULT false NOT NULL,
|
||||||
|
`max_cpu` real DEFAULT 0 NOT NULL,
|
||||||
|
`max_memory` text DEFAULT '0' NOT NULL,
|
||||||
|
`max_pids` integer DEFAULT false NOT NULL,
|
||||||
|
`restart` integer DEFAULT 2 NOT NULL,
|
||||||
|
`restart_delay` text DEFAULT '5s',
|
||||||
|
`restart_max_attempts` integer,
|
||||||
|
`healthcheck_enabled` integer DEFAULT false NOT NULL,
|
||||||
|
`healthcheck_command` text,
|
||||||
|
`healthcheck_interval` text DEFAULT '30s' NOT NULL,
|
||||||
|
`healthcheck_timeout` text DEFAULT '30s' NOT NULL,
|
||||||
|
`healthcheck_retries` integer DEFAULT 3 NOT NULL,
|
||||||
|
`healthcheck_start_period` text DEFAULT '0s' NOT NULL,
|
||||||
|
`logging_max_size` text DEFAULT '-1' NOT NULL,
|
||||||
|
`logging_max_files` integer DEFAULT 1 NOT NULL,
|
||||||
|
`created_at` integer DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
FOREIGN KEY (`service_id`) REFERENCES `service`(`id`) ON UPDATE no action ON DELETE cascade,
|
||||||
|
FOREIGN KEY (`deployment_id`) REFERENCES `service_deployment`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `service_port` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`service_id` text NOT NULL,
|
||||||
|
`internal_port` integer NOT NULL,
|
||||||
|
`external_port` integer NOT NULL,
|
||||||
|
`port_type` integer NOT NULL,
|
||||||
|
`type` integer NOT NULL,
|
||||||
|
FOREIGN KEY (`service_id`) REFERENCES `service_generation`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `service_sysctl` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`service_id` text NOT NULL,
|
||||||
|
`key` text NOT NULL,
|
||||||
|
`value` text NOT NULL,
|
||||||
|
FOREIGN KEY (`service_id`) REFERENCES `service_generation`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `service_ulimit` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`service_id` text NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`soft` integer NOT NULL,
|
||||||
|
`hard` integer NOT NULL,
|
||||||
|
FOREIGN KEY (`service_id`) REFERENCES `service_generation`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `service_volume` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`service_id` text NOT NULL,
|
||||||
|
`source` text,
|
||||||
|
`target` text NOT NULL,
|
||||||
|
`type` text NOT NULL,
|
||||||
|
FOREIGN KEY (`service_id`) REFERENCES `service_generation`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `session` (
|
||||||
|
`token` text PRIMARY KEY NOT NULL,
|
||||||
|
`last_useragent` text,
|
||||||
|
`last_ip` text,
|
||||||
|
`last_accessed` integer,
|
||||||
|
`created_at` integer DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
`id` text NOT NULL,
|
||||||
|
FOREIGN KEY (`id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `system_stats` (
|
||||||
|
`id` integer PRIMARY KEY DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
`cpu_usage` integer,
|
||||||
|
`memory_usage` integer NOT NULL,
|
||||||
|
`disk_usage` integer NOT NULL,
|
||||||
|
`network_tx` integer NOT NULL,
|
||||||
|
`network_rx` integer NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `users` (
|
||||||
|
`id` text PRIMARY KEY DEFAULT (uuid_generate_v7()) NOT NULL,
|
||||||
|
`username` text NOT NULL,
|
||||||
|
`password` text,
|
||||||
|
`mfa_token` blob
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE INDEX `proj_deployment_idx` ON `project_deployment` (`id`,`project_id`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `projects_internal_name_unique` ON `projects` (`internal_name`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `name_project_idx` ON `service` (`name`,`project_id`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `name_project_unq` ON `service` (`name`,`project_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `proj_generation_idx` ON `service_generation` (`id`,`service_id`);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `username_idx` ON `users` (`username`);
|
1152
drizzle/meta/0000_snapshot.json
Normal file
1152
drizzle/meta/0000_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
13
drizzle/meta/_journal.json
Normal file
13
drizzle/meta/_journal.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1715730366620,
|
||||||
|
"tag": "0000_tidy_vermin",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
"build:server": "tsup",
|
"build:server": "tsup",
|
||||||
"clean": "rm -rf .next dist",
|
"clean": "rm -rf .next dist",
|
||||||
"db:push": "drizzle-kit push:sqlite",
|
"db:push": "drizzle-kit push:sqlite",
|
||||||
|
"db:generate": "drizzle-kit generate:sqlite",
|
||||||
"dev": "tsup --watch --onSuccess \"npm run dev:run\" --clean",
|
"dev": "tsup --watch --onSuccess \"npm run dev:run\" --clean",
|
||||||
"dev:run": "node --enable-source-maps dist/server.js",
|
"dev:run": "node --enable-source-maps dist/server.js",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
|
|
|
@ -12,7 +12,7 @@ export type BasicProjectDetails = {
|
||||||
export const projectMiddleware = experimental_standaloneMiddleware<{
|
export const projectMiddleware = experimental_standaloneMiddleware<{
|
||||||
ctx: { db: typeof db };
|
ctx: { db: typeof db };
|
||||||
input: { projectId: string };
|
input: { projectId: string };
|
||||||
}>().create(async ({ ctx, input, next }) => {
|
}>().create(async ({ input, next }) => {
|
||||||
if (typeof input.projectId != "string") {
|
if (typeof input.projectId != "string") {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { projects, serviceGeneration } from "~/server/db/schema";
|
import { projects } from "~/server/db/schema";
|
||||||
|
import ProjectManager from "~/server/managers/Project";
|
||||||
import { authenticatedProcedure, createTRPCRouter } from "../../trpc";
|
import { authenticatedProcedure, createTRPCRouter } from "../../trpc";
|
||||||
import { deployProject } from "./deploy";
|
import { deployProject } from "./deploy";
|
||||||
import { getProject } from "./project";
|
import { getProject } from "./project";
|
||||||
|
@ -21,28 +21,21 @@ export const projectRouter = createTRPCRouter({
|
||||||
.input(z.void())
|
.input(z.void())
|
||||||
.output(z.unknown())
|
.output(z.unknown())
|
||||||
.query(async ({ ctx }) => {
|
.query(async ({ ctx }) => {
|
||||||
const userProjects = await ctx.db
|
const projects = await ProjectManager.listForUser(
|
||||||
.select({
|
ctx.session.data.userId,
|
||||||
id: projects.id,
|
);
|
||||||
friendlyName: projects.friendlyName,
|
|
||||||
internalName: projects.internalName,
|
|
||||||
createdAt: projects.createdAt,
|
|
||||||
})
|
|
||||||
.from(projects);
|
|
||||||
|
|
||||||
return await Promise.all(
|
|
||||||
userProjects.map(async (project) => {
|
|
||||||
const projServices = await ctx.db
|
|
||||||
.select({
|
|
||||||
id: serviceGeneration.id,
|
|
||||||
name: serviceGeneration.name,
|
|
||||||
})
|
|
||||||
.from(serviceGeneration)
|
|
||||||
.where(eq(serviceGeneration.serviceId, project.id));
|
|
||||||
|
|
||||||
|
// we love the nested Promise.all's
|
||||||
|
// TODO: refactor
|
||||||
|
return Promise.all(
|
||||||
|
projects.map(async (project) => {
|
||||||
return {
|
return {
|
||||||
...project,
|
...project.getData(),
|
||||||
services: projServices,
|
services: await Promise.all(
|
||||||
|
(await project.getServices()).map((service) =>
|
||||||
|
service.getDataWithGenerations(),
|
||||||
|
),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { serviceGeneration } from "~/server/db/schema";
|
|
||||||
import { projectMiddleware } from "../../middleware/project";
|
import { projectMiddleware } from "../../middleware/project";
|
||||||
import { authenticatedProcedure } from "../../trpc";
|
import { authenticatedProcedure } from "../../trpc";
|
||||||
|
|
||||||
|
@ -16,26 +14,22 @@ export const getProject = authenticatedProcedure
|
||||||
.use(projectMiddleware)
|
.use(projectMiddleware)
|
||||||
.output(z.unknown())
|
.output(z.unknown())
|
||||||
.query(async ({ ctx }) => {
|
.query(async ({ ctx }) => {
|
||||||
const projServices = await ctx.db
|
const projServices = await ctx.project.getServices();
|
||||||
.select({
|
|
||||||
id: serviceGeneration.id,
|
|
||||||
name: serviceGeneration.name,
|
|
||||||
})
|
|
||||||
.from(serviceGeneration)
|
|
||||||
.where(eq(serviceGeneration.serviceId, ctx.project.id));
|
|
||||||
|
|
||||||
// get docker stats
|
// get docker stats
|
||||||
const stats = await ctx.docker.listServices({
|
const stats = await ctx.docker.listServices({
|
||||||
filters: {
|
filters: {
|
||||||
label: [`com.docker.stack.namespace=${ctx.project.internalName}`],
|
label: [
|
||||||
|
`com.docker.stack.namespace=${ctx.project.getData().internalName}`,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...ctx.project,
|
...ctx.project.getData(),
|
||||||
services: projServices.map((service) => ({
|
services: projServices.map((service) => ({
|
||||||
...service,
|
...service.getData(),
|
||||||
stats: stats.find((stat) => stat.Spec?.Name === service.name),
|
stats: stats.find((stat) => stat.Spec?.Name === service.getData().name),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq, sql } from "drizzle-orm";
|
||||||
import { randomBytes } from "node:crypto";
|
import { randomBytes } from "node:crypto";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
|
@ -14,6 +14,7 @@ import {
|
||||||
updateServiceDomainsProcedure,
|
updateServiceDomainsProcedure,
|
||||||
updateServiceProcedure,
|
updateServiceProcedure,
|
||||||
} from "./update";
|
} from "./update";
|
||||||
|
import { ServiceSource } from "~/server/db/types";
|
||||||
|
|
||||||
export const serviceRouter = createTRPCRouter({
|
export const serviceRouter = createTRPCRouter({
|
||||||
containers: getServiceContainers,
|
containers: getServiceContainers,
|
||||||
|
@ -33,27 +34,9 @@ export const serviceRouter = createTRPCRouter({
|
||||||
.use(projectMiddleware)
|
.use(projectMiddleware)
|
||||||
.use(serviceMiddleware)
|
.use(serviceMiddleware)
|
||||||
.query(async ({ ctx }) => {
|
.query(async ({ ctx }) => {
|
||||||
// const fullServiceData = await ctx.db.query.service.findFirst({
|
|
||||||
// where: eq(serviceGeneration.id, ctx.service.getData().id),
|
|
||||||
// with: {
|
|
||||||
// domains: true,
|
|
||||||
// ports: true,
|
|
||||||
// volumes: true,
|
|
||||||
// sysctls: true,
|
|
||||||
// ulimits: true,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// assert(fullServiceData);
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// ...fullServiceData,
|
|
||||||
// deployMode: DOCKER_DEPLOY_MODE_MAP[fullServiceData.deployMode],
|
|
||||||
// };
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...ctx.service.getData(),
|
...ctx.service.getData(),
|
||||||
latestGeneration: ctx.service.getData().latestGeneration,
|
latestGeneration: await ctx.service.fetchFullLatestGeneration(),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -75,19 +58,10 @@ export const serviceRouter = createTRPCRouter({
|
||||||
.use(projectMiddleware)
|
.use(projectMiddleware)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
// create a generation for the service
|
// create a generation for the service
|
||||||
// const [defaultGeneration] = await ctx.db
|
const trxResult = await ctx.db.transaction(
|
||||||
// .insert(serviceGeneration)
|
async (trx) => {
|
||||||
// .values({
|
// create the service
|
||||||
// : ctx.project.getData().id,
|
const [data] = await trx
|
||||||
|
|
||||||
// status: "pending",
|
|
||||||
// })
|
|
||||||
// .returning({
|
|
||||||
// id: serviceGeneration.id,
|
|
||||||
// })
|
|
||||||
// .execute();
|
|
||||||
|
|
||||||
const [data] = await ctx.db
|
|
||||||
.insert(service)
|
.insert(service)
|
||||||
.values({
|
.values({
|
||||||
name: input.name,
|
name: input.name,
|
||||||
|
@ -96,8 +70,6 @@ export const serviceRouter = createTRPCRouter({
|
||||||
redeploySecret: randomBytes(env.REDEPLOY_SECRET_BYTES).toString(
|
redeploySecret: randomBytes(env.REDEPLOY_SECRET_BYTES).toString(
|
||||||
"hex",
|
"hex",
|
||||||
),
|
),
|
||||||
// source: ServiceSource.Docker,
|
|
||||||
// dockerImage: "traefik/whoami",
|
|
||||||
})
|
})
|
||||||
.returning({
|
.returning({
|
||||||
id: serviceGeneration.id,
|
id: serviceGeneration.id,
|
||||||
|
@ -108,9 +80,39 @@ export const serviceRouter = createTRPCRouter({
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(data?.id);
|
assert(data?.id, "Expected service data to be returned");
|
||||||
|
|
||||||
|
// create initial generation
|
||||||
|
const [generation] = await trx
|
||||||
|
.insert(serviceGeneration)
|
||||||
|
.values({
|
||||||
|
serviceId: data.id,
|
||||||
|
source: ServiceSource.Docker,
|
||||||
|
dockerImage: "traefik/whoami",
|
||||||
|
})
|
||||||
|
.returning({
|
||||||
|
id: serviceGeneration.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(generation?.id, "Expected generation data to be returned");
|
||||||
|
|
||||||
|
// update the service with the generation id
|
||||||
|
await trx
|
||||||
|
.update(service)
|
||||||
|
.set({
|
||||||
|
latestGenerationId: generation.id,
|
||||||
|
})
|
||||||
|
.where(eq(service.id, data.id))
|
||||||
|
.execute();
|
||||||
|
|
||||||
return data.id;
|
return data.id;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
behavior: "deferred",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return trxResult;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
delete: authenticatedProcedure
|
delete: authenticatedProcedure
|
||||||
|
|
|
@ -73,6 +73,7 @@ export const updateServiceProcedure = authenticatedProcedure
|
||||||
id: true,
|
id: true,
|
||||||
projectId: true,
|
projectId: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
serviceId: true,
|
||||||
})
|
})
|
||||||
.partial(),
|
.partial(),
|
||||||
)
|
)
|
||||||
|
@ -95,7 +96,7 @@ export const updateServiceProcedure = authenticatedProcedure
|
||||||
await ctx.db
|
await ctx.db
|
||||||
.update(serviceGeneration)
|
.update(serviceGeneration)
|
||||||
.set(queryUpdate)
|
.set(queryUpdate)
|
||||||
.where(eq(serviceGeneration.id, ctx.service.id))
|
.where(eq(serviceGeneration.id, ctx.service.getData().latestGenerationId))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -140,10 +140,14 @@ export const service = sqliteTable(
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// https://github.com/drizzle-team/drizzle-orm/issues/2252
|
||||||
|
// Must manually add `DEFERRABLE INITIALLY DEFERRED`
|
||||||
latestGenerationId: text("latest_generation_id")
|
latestGenerationId: text("latest_generation_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => serviceGeneration.id),
|
.references(() => serviceGeneration.id),
|
||||||
|
|
||||||
|
// latestGenerationId: sql<string>`service_generation_id REFERENCES service_generation(id) NOT NULL `,
|
||||||
|
|
||||||
redeploySecret: text("redeploy_secret").notNull(),
|
redeploySecret: text("redeploy_secret").notNull(),
|
||||||
deployedGenerationId: text("deployed_generation_id").references(
|
deployedGenerationId: text("deployed_generation_id").references(
|
||||||
() => serviceGeneration.id,
|
() => serviceGeneration.id,
|
||||||
|
|
|
@ -32,6 +32,17 @@ export default class ProjectManager {
|
||||||
return data ? new ProjectManager(data) : null;
|
return data ? new ProjectManager(data) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all projects for a user.
|
||||||
|
*/
|
||||||
|
static async listForUser(userId: string) {
|
||||||
|
const data = await db.query.projects.findMany({
|
||||||
|
where: eq(projects.ownerId, userId),
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.map((data) => new ProjectManager(data));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the project data.
|
* Returns the project data.
|
||||||
*/
|
*/
|
||||||
|
@ -143,7 +154,7 @@ export default class ProjectManager {
|
||||||
const composeStack = await buildDockerStackFile(allServiceData);
|
const composeStack = await buildDockerStackFile(allServiceData);
|
||||||
|
|
||||||
return await deployOptions.docker.cli(
|
return await deployOptions.docker.cli(
|
||||||
["stack", "deploy", "--compose-file", "-", this.projectData.internalName],
|
["stack", "deploy", "--compose-file", "-", this.projectData.internalName, deployOptions.force ? "--force-recreate" : ""],
|
||||||
{
|
{
|
||||||
stdin: JSON.stringify(composeStack),
|
stdin: JSON.stringify(composeStack),
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,15 +29,9 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
// migrate the database
|
// migrate the database
|
||||||
if (env.NODE_ENV === "production") {
|
logger.child({ module: "database" }).info("⚙️ Starting database migrations...");
|
||||||
logger.child({ module: "database" }).info("⚙️ Migrating database");
|
migrate(db, { migrationsFolder: "./drizzle" });
|
||||||
migrate(db, { migrationsFolder: "./migrations" });
|
logger.child({ module: "database" }).info("✅ Migrations finished!");
|
||||||
logger.child({ module: "database" }).info("✅ Database migrated");
|
|
||||||
} else {
|
|
||||||
logger
|
|
||||||
.child({ module: "database" })
|
|
||||||
.info("Not running database migrations, use drizzle-kit push to migrate");
|
|
||||||
}
|
|
||||||
|
|
||||||
// start statistics
|
// start statistics
|
||||||
void stats.start();
|
void stats.start();
|
||||||
|
|
Loading…
Reference in a new issue