diff --git a/backend/src/db/migrations/20251027234547_add-pki-acme.ts b/backend/src/db/migrations/20251027234547_add-pki-acme.ts index d92d71951..81a3ea82b 100644 --- a/backend/src/db/migrations/20251027234547_add-pki-acme.ts +++ b/backend/src/db/migrations/20251027234547_add-pki-acme.ts @@ -34,15 +34,107 @@ export async function up(knex: Knex): Promise { await createOnUpdateTrigger(knex, TableName.PkiAcmeAccount); } + + // Create PkiAcmeOrder table + if (!(await knex.schema.hasTable(TableName.PkiAcmeOrder))) { + await knex.schema.createTable(TableName.PkiAcmeOrder, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + + // Foreign key to PkiAcmeAccount + t.uuid("accountId").notNullable(); + t.foreign("accountId").references("id").inTable(TableName.PkiAcmeAccount).onDelete("CASCADE"); + + // Order status + t.string("status").notNullable(); // pending, ready, processing, valid, invalid + + t.timestamps(true, true, true); + }); + + await createOnUpdateTrigger(knex, TableName.PkiAcmeOrder); + } + + // Create PkiAcmeAuth table + if (!(await knex.schema.hasTable(TableName.PkiAcmeAuth))) { + await knex.schema.createTable(TableName.PkiAcmeAuth, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + + // Foreign key to PkiAcmeAccount + t.uuid("accountId").notNullable(); + t.foreign("accountId").references("id").inTable(TableName.PkiAcmeAccount).onDelete("CASCADE"); + + // Authorization status + t.string("status").notNullable(); // pending, valid, invalid, deactivated, expired, revoked + + // Identifier type and value + t.string("identifierType").notNullable(); // dns + t.string("identifierValue").notNullable(); // domain name + + // Expiration timestamp + t.timestamp("expiresAt").notNullable(); + + // Optional link to issued certificate + t.uuid("certificateId").nullable(); + t.foreign("certificateId").references("id").inTable(TableName.Certificate).onDelete("SET NULL"); + + t.timestamps(true, true, true); + }); + + await createOnUpdateTrigger(knex, TableName.PkiAcmeAuth); + } + + // Create PkiAcmeChallenge table + if (!(await knex.schema.hasTable(TableName.PkiAcmeChallenge))) { + await knex.schema.createTable(TableName.PkiAcmeChallenge, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + + // Foreign key to PkiAcmeAuth + t.uuid("authId").notNullable(); + t.foreign("authId").references("id").inTable(TableName.PkiAcmeAuth).onDelete("CASCADE"); + + // Challenge type + t.string("type").notNullable(); // http-01, dns-01, tls-alpn-01 + + // Challenge status + t.string("status").notNullable(); // pending, processing, valid, invalid + + // Validation timestamp + t.timestamp("validatedAt").nullable(); + + t.timestamps(true, true, true); + }); + + await createOnUpdateTrigger(knex, TableName.PkiAcmeChallenge); + } } export async function down(knex: Knex): Promise { - // Drop PkiAcmeAccount table first (depends on PkiAcmeEnrollmentConfig) + // Drop tables in reverse dependency order + + // Drop PkiAcmeChallenge first (depends on PkiAcmeAuth) + if (await knex.schema.hasTable(TableName.PkiAcmeChallenge)) { + await knex.schema.dropTable(TableName.PkiAcmeChallenge); + await dropOnUpdateTrigger(knex, TableName.PkiAcmeChallenge); + } + + // Drop PkiAcmeAuth (depends on PkiAcmeAccount and Certificate) + if (await knex.schema.hasTable(TableName.PkiAcmeAuth)) { + await knex.schema.dropTable(TableName.PkiAcmeAuth); + await dropOnUpdateTrigger(knex, TableName.PkiAcmeAuth); + } + + // Drop PkiAcmeOrder (depends on PkiAcmeAccount) + if (await knex.schema.hasTable(TableName.PkiAcmeOrder)) { + await knex.schema.dropTable(TableName.PkiAcmeOrder); + await dropOnUpdateTrigger(knex, TableName.PkiAcmeOrder); + } + + // Drop PkiAcmeAccount (depends on PkiCertificateProfile) if (await knex.schema.hasTable(TableName.PkiAcmeAccount)) { await knex.schema.dropTable(TableName.PkiAcmeAccount); await dropOnUpdateTrigger(knex, TableName.PkiAcmeAccount); } + // Drop PkiAcmeEnrollmentConfig if (await knex.schema.hasTable(TableName.PkiAcmeEnrollmentConfig)) { await knex.schema.dropTable(TableName.PkiAcmeEnrollmentConfig); await dropOnUpdateTrigger(knex, TableName.PkiAcmeEnrollmentConfig); diff --git a/backend/src/db/schemas/index.ts b/backend/src/db/schemas/index.ts index aba550083..3eef75d16 100644 --- a/backend/src/db/schemas/index.ts +++ b/backend/src/db/schemas/index.ts @@ -93,7 +93,10 @@ export * from "./pam-folders"; export * from "./pam-resources"; export * from "./pam-sessions"; export * from "./pki-acme-accounts"; +export * from "./pki-acme-auths"; +export * from "./pki-acme-challenges"; export * from "./pki-acme-enrollment-configs"; +export * from "./pki-acme-orders"; export * from "./pki-alerts"; export * from "./pki-api-enrollment-configs"; export * from "./pki-certificate-profiles"; diff --git a/backend/src/db/schemas/models.ts b/backend/src/db/schemas/models.ts index 686f8035f..0fcda08d7 100644 --- a/backend/src/db/schemas/models.ts +++ b/backend/src/db/schemas/models.ts @@ -29,6 +29,9 @@ export enum TableName { PkiApiEnrollmentConfig = "pki_api_enrollment_configs", PkiAcmeEnrollmentConfig = "pki_acme_enrollment_configs", PkiAcmeAccount = "pki_acme_accounts", + PkiAcmeOrder = "pki_acme_orders", + PkiAcmeAuth = "pki_acme_auths", + PkiAcmeChallenge = "pki_acme_challenges", PkiSubscriber = "pki_subscribers", PkiAlert = "pki_alerts", PkiCollection = "pki_collections", diff --git a/backend/src/db/schemas/pki-acme-auths.ts b/backend/src/db/schemas/pki-acme-auths.ts new file mode 100644 index 000000000..de883356d --- /dev/null +++ b/backend/src/db/schemas/pki-acme-auths.ts @@ -0,0 +1,24 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const PkiAcmeAuthsSchema = z.object({ + id: z.string().uuid(), + accountId: z.string().uuid(), + status: z.string(), + identifierType: z.string(), + identifierValue: z.string(), + expiresAt: z.date(), + certificateId: z.string().uuid().nullable().optional(), + createdAt: z.date(), + updatedAt: z.date() +}); + +export type TPkiAcmeAuths = z.infer; +export type TPkiAcmeAuthsInsert = Omit, TImmutableDBKeys>; +export type TPkiAcmeAuthsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/pki-acme-challenges.ts b/backend/src/db/schemas/pki-acme-challenges.ts new file mode 100644 index 000000000..17282da06 --- /dev/null +++ b/backend/src/db/schemas/pki-acme-challenges.ts @@ -0,0 +1,22 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const PkiAcmeChallengesSchema = z.object({ + id: z.string().uuid(), + authId: z.string().uuid(), + type: z.string(), + status: z.string(), + validatedAt: z.date().nullable().optional(), + createdAt: z.date(), + updatedAt: z.date() +}); + +export type TPkiAcmeChallenges = z.infer; +export type TPkiAcmeChallengesInsert = Omit, TImmutableDBKeys>; +export type TPkiAcmeChallengesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/pki-acme-orders.ts b/backend/src/db/schemas/pki-acme-orders.ts new file mode 100644 index 000000000..d52c30662 --- /dev/null +++ b/backend/src/db/schemas/pki-acme-orders.ts @@ -0,0 +1,20 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const PkiAcmeOrdersSchema = z.object({ + id: z.string().uuid(), + accountId: z.string().uuid(), + status: z.string(), + createdAt: z.date(), + updatedAt: z.date() +}); + +export type TPkiAcmeOrders = z.infer; +export type TPkiAcmeOrdersInsert = Omit, TImmutableDBKeys>; +export type TPkiAcmeOrdersUpdate = Partial, TImmutableDBKeys>>;