Implement get acme order

This commit is contained in:
Fang-Pen Lin
2025-10-29 20:31:41 -07:00
parent ea4b36b0ae
commit c6f51cbd10
3 changed files with 52 additions and 62 deletions

View File

@@ -1,10 +1,10 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { TableName, TPkiAcmeAuths } from "@app/db/schemas";
import { TPkiAcmeOrdersInsert, TPkiAcmeOrdersUpdate } from "@app/db/schemas/pki-acme-orders";
import { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex";
import { ormify, selectAllTableCols } from "@app/lib/knex";
export type TPkiAcmeOrderDALFactory = ReturnType<typeof pkiAcmeOrderDALFactory>;
@@ -51,54 +51,35 @@ export const pkiAcmeOrderDALFactory = (db: TDbClient) => {
}
};
const findByAccountId = async (accountId: string, tx?: Knex) => {
const findByIdWithAuthorizations = async (id: string, tx?: Knex) => {
try {
const orders = await (tx || db)(TableName.PkiAcmeOrder).where({ accountId });
const order = await (tx || db)(TableName.PkiAcmeOrder)
.join(TableName.PkiAcmeOrderAuth, `${TableName.PkiAcmeOrderAuth}.orderId`, `${TableName.PkiAcmeOrder}.id`)
.join(TableName.PkiAcmeAuth, `${TableName.PkiAcmeOrderAuth}.authId`, `${TableName.PkiAcmeAuth}.id`)
.select(
selectAllTableCols(TableName.PkiAcmeOrder),
db.ref("id").withSchema(TableName.PkiAcmeAuth).as("authId"),
db.ref("identifierType").withSchema(TableName.PkiAcmeAuth).as("identifierType"),
db.ref("identifierValue").withSchema(TableName.PkiAcmeAuth).as("identifierValue"),
db.ref("expiresAt").withSchema(TableName.PkiAcmeAuth).as("expiresAt")
)
.where(`${TableName.PkiAcmeOrder}.id`, id)
.first();
return orders;
if (!order) {
return null;
}
return {
...order,
authorizations: order.authorizations.map((auth: TPkiAcmeAuths) => ({
id: auth.id,
identifierType: auth.identifierType,
identifierValue: auth.identifierValue,
expiresAt: auth.expiresAt
}))
};
} catch (error) {
throw new DatabaseError({ error, name: "Find PKI ACME orders by account id" });
}
};
const findByStatus = async (status: string, tx?: Knex) => {
try {
const orders = await (tx || db)(TableName.PkiAcmeOrder).where({ status });
return orders;
} catch (error) {
throw new DatabaseError({ error, name: "Find PKI ACME orders by status" });
}
};
const findByAccountIdAndStatus = async (accountId: string, status: string, tx?: Knex) => {
try {
const orders = await (tx || db)(TableName.PkiAcmeOrder).where({ accountId, status });
return orders;
} catch (error) {
throw new DatabaseError({ error, name: "Find PKI ACME orders by account id and status" });
}
};
const deleteById = async (id: string, tx?: Knex) => {
try {
const result = await (tx || db)(TableName.PkiAcmeOrder).where({ id }).delete().returning("*");
const [order] = result;
return order || null;
} catch (error) {
throw new DatabaseError({ error, name: "Delete PKI ACME order by id" });
}
};
const deleteByAccountId = async (accountId: string, tx?: Knex) => {
try {
const result = await (tx || db)(TableName.PkiAcmeOrder).where({ accountId }).delete().returning("*");
return result;
} catch (error) {
throw new DatabaseError({ error, name: "Delete PKI ACME orders by account id" });
throw new DatabaseError({ error, name: "Find PKI ACME order by id" });
}
};
@@ -107,10 +88,6 @@ export const pkiAcmeOrderDALFactory = (db: TDbClient) => {
create,
updateById,
findById,
findByAccountId,
findByStatus,
findByAccountIdAndStatus,
deleteById,
deleteByAccountId
findByIdWithAuthorizations
};
};

View File

@@ -7,7 +7,9 @@ import {
AcmeAccountDoesNotExistError,
AcmeBadPublicKeyError,
AcmeMalformedError,
AcmeServerInternalError
AcmeServerInternalError,
AcmeUnauthorizedError,
AcmeUnsupportedIdentifierError
} from "./pki-acme-errors";
import { TPkiAcmeAccounts } from "@app/db/schemas/pki-acme-accounts";
@@ -54,7 +56,7 @@ import {
type TPkiAcmeServiceFactoryDep = {
certificateProfileDAL: Pick<TCertificateProfileDALFactory, "findById">;
acmeAccountDAL: Pick<TPkiAcmeAccountDALFactory, "findByProjectIdAndAccountId" | "findByPublicKey" | "create">;
acmeOrderDAL: Pick<TPkiAcmeOrderDALFactory, "create" | "transaction">;
acmeOrderDAL: Pick<TPkiAcmeOrderDALFactory, "create" | "transaction" | "findByIdWithAuthorizations">;
acmeAuthDAL: Pick<TPkiAcmeAuthDALFactory, "create">;
acmeOrderAuthDAL: Pick<TPkiAcmeOrderAuthDALFactory, "insertMany">;
};
@@ -170,7 +172,7 @@ export const pkiAcmeServiceFactory = ({
}
const accountId = extractAccountIdFromKid(protectedHeader.kid, profileId);
if (expectedAccountId && accountId !== expectedAccountId) {
throw new AcmeAccountDoesNotExistError({ message: "ACME account ID mismatch" });
throw new NotFoundError({ message: "ACME resource not found" });
}
const account = await acmeAccountDAL.findByProjectIdAndAccountId(profile.id, accountId);
if (!account) {
@@ -294,7 +296,7 @@ export const pkiAcmeServiceFactory = ({
tx
);
} else {
throw new AcmeMalformedError({ detail: "Only DNS identifiers are supported" });
throw new AcmeUnsupportedIdentifierError({ detail: "Only DNS identifiers are supported" });
}
})
);
@@ -373,20 +375,29 @@ export const pkiAcmeServiceFactory = ({
const getAcmeOrder = async ({
profileId,
accountId,
orderId
}: {
profileId: string;
accountId: string;
orderId: string;
}): Promise<TAcmeResponse<TGetAcmeOrderResponse>> => {
const profile = await validateAcmeProfile(profileId);
// FIXME: Implement ACME get order
const order = await acmeOrderDAL.findByIdWithAuthorizations(orderId);
if (!order || order.accountId !== accountId) {
throw new NotFoundError({ message: "ACME order not found" });
}
return {
status: 200,
body: {
status: "pending",
expires: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
identifiers: [],
authorizations: [],
status: order.status,
expires: order.expiresAt.toISOString(),
identifiers: order.authorizations.map((auth: TPkiAcmeAuths) => ({
type: auth.identifierType,
value: auth.identifierValue
})),
authorizations: order.authorizations.map((auth: TPkiAcmeAuths) =>
buildUrl(`/api/v1/pki/acme/profiles/${profileId}/authorizations/${auth.id}`)
),
finalize: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/orders/${orderId}/finalize`)
},
headers: { Location: buildUrl(`/api/v1/pki/acme/profiles/${profileId}/orders/${orderId}`) }

View File

@@ -109,9 +109,11 @@ export type TPkiAcmeServiceFactory = {
}) => Promise<TAcmeResponse<TListAcmeOrdersResponse>>;
getAcmeOrder: ({
profileId,
accountId,
orderId
}: {
profileId: string;
accountId: string;
orderId: string;
}) => Promise<TAcmeResponse<TGetAcmeOrderResponse>>;
finalizeAcmeOrder: ({