mirror of
https://github.com/fosrl/pangolin.git
synced 2026-07-17 19:16:32 +02:00
add site provisioning keys to integration api
This commit is contained in:
@@ -1511,6 +1511,10 @@
|
|||||||
"actionListInvitations": "List Invitations",
|
"actionListInvitations": "List Invitations",
|
||||||
"actionExportLogs": "Export Logs",
|
"actionExportLogs": "Export Logs",
|
||||||
"actionViewLogs": "View Logs",
|
"actionViewLogs": "View Logs",
|
||||||
|
"actionCreateSiteProvisioningKey": "Create Site Provisioning Key",
|
||||||
|
"actionListSiteProvisioningKeys": "List Site Provisioning Keys",
|
||||||
|
"actionUpdateSiteProvisioningKey": "Update Site Provisioning Key",
|
||||||
|
"actionDeleteSiteProvisioningKey": "Delete Site Provisioning Key",
|
||||||
"noneSelected": "None selected",
|
"noneSelected": "None selected",
|
||||||
"orgNotFound2": "No organizations found.",
|
"orgNotFound2": "No organizations found.",
|
||||||
"search": "Search…",
|
"search": "Search…",
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ export * from "./verifyApiKeySiteResourceAccess";
|
|||||||
export * from "./verifyApiKeyIdpAccess";
|
export * from "./verifyApiKeyIdpAccess";
|
||||||
export * from "./verifyApiKeyDomainAccess";
|
export * from "./verifyApiKeyDomainAccess";
|
||||||
export * from "./verifyApiKeyResourcePolicyAccess";
|
export * from "./verifyApiKeyResourcePolicyAccess";
|
||||||
|
export * from "./verifyApiKeySiteProvisioningKeyAccess";
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import {
|
||||||
|
db,
|
||||||
|
siteProvisioningKeys,
|
||||||
|
siteProvisioningKeyOrg,
|
||||||
|
apiKeyOrg
|
||||||
|
} from "@server/db";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
import { getFirstString } from "@server/lib/requestParams";
|
||||||
|
|
||||||
|
export async function verifyApiKeySiteProvisioningKeyAccess(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const apiKey = req.apiKey;
|
||||||
|
const siteProvisioningKeyId =
|
||||||
|
getFirstString(req.params.siteProvisioningKeyId) ||
|
||||||
|
getFirstString(req.body.siteProvisioningKeyId) ||
|
||||||
|
getFirstString(req.query.siteProvisioningKeyId);
|
||||||
|
const orgId = getFirstString(req.params.orgId);
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.UNAUTHORIZED, "Key not authenticated")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!orgId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, "Invalid organization ID")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!siteProvisioningKeyId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, "Invalid key ID")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiKey.isRoot) {
|
||||||
|
// Root keys can access any site provisioning key in any org
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [row] = await db
|
||||||
|
.select()
|
||||||
|
.from(siteProvisioningKeys)
|
||||||
|
.innerJoin(
|
||||||
|
siteProvisioningKeyOrg,
|
||||||
|
and(
|
||||||
|
eq(
|
||||||
|
siteProvisioningKeys.siteProvisioningKeyId,
|
||||||
|
siteProvisioningKeyOrg.siteProvisioningKeyId
|
||||||
|
),
|
||||||
|
eq(siteProvisioningKeyOrg.orgId, orgId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
siteProvisioningKeys.siteProvisioningKeyId,
|
||||||
|
siteProvisioningKeyId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!row?.siteProvisioningKeys || !row.siteProvisioningKeyOrg) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
`Site provisioning key with ID ${siteProvisioningKeyId} not found for organization ${orgId}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.apiKeyOrg) {
|
||||||
|
const apiKeyOrgRes = await db
|
||||||
|
.select()
|
||||||
|
.from(apiKeyOrg)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(apiKeyOrg.apiKeyId, apiKey.apiKeyId),
|
||||||
|
eq(apiKeyOrg.orgId, row.siteProvisioningKeyOrg.orgId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
req.apiKeyOrg = apiKeyOrgRes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.apiKeyOrg) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.FORBIDDEN,
|
||||||
|
"Key does not have access to this organization"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
} catch (error) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Error verifying site provisioning key access"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ export enum OpenAPITags {
|
|||||||
OrgIdp = "Identity Provider (Organization Only)",
|
OrgIdp = "Identity Provider (Organization Only)",
|
||||||
Client = "Client",
|
Client = "Client",
|
||||||
ApiKey = "API Key",
|
ApiKey = "API Key",
|
||||||
|
SiteProvisioningKey = "Site Provisioning Key",
|
||||||
Domain = "Domain",
|
Domain = "Domain",
|
||||||
Blueprint = "Blueprint",
|
Blueprint = "Blueprint",
|
||||||
Ssh = "SSH",
|
Ssh = "SSH",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import * as org from "#private/routers/org";
|
|||||||
import * as logs from "#private/routers/auditLogs";
|
import * as logs from "#private/routers/auditLogs";
|
||||||
import * as alertEvents from "#private/routers/alertEvents";
|
import * as alertEvents from "#private/routers/alertEvents";
|
||||||
import * as certificates from "#private/routers/certificates";
|
import * as certificates from "#private/routers/certificates";
|
||||||
|
import * as siteProvisioning from "#private/routers/siteProvisioning";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
verifyApiKeyHasAction,
|
verifyApiKeyHasAction,
|
||||||
@@ -24,6 +25,7 @@ import {
|
|||||||
verifyApiKeyIdpAccess,
|
verifyApiKeyIdpAccess,
|
||||||
verifyApiKeyRoleAccess,
|
verifyApiKeyRoleAccess,
|
||||||
verifyApiKeyUserAccess,
|
verifyApiKeyUserAccess,
|
||||||
|
verifyApiKeySiteProvisioningKeyAccess,
|
||||||
verifyLimits
|
verifyLimits
|
||||||
} from "@server/middlewares";
|
} from "@server/middlewares";
|
||||||
import * as user from "#private/routers/user";
|
import * as user from "#private/routers/user";
|
||||||
@@ -215,3 +217,45 @@ authenticated.delete(
|
|||||||
logActionAudit(ActionsEnum.removeUserRole),
|
logActionAudit(ActionsEnum.removeUserRole),
|
||||||
user.removeUserRole
|
user.removeUserRole
|
||||||
);
|
);
|
||||||
|
|
||||||
|
authenticated.put(
|
||||||
|
"/org/:orgId/site-provisioning-key",
|
||||||
|
verifyValidLicense,
|
||||||
|
verifyValidSubscription(tierMatrix.siteProvisioningKeys),
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyLimits,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.createSiteProvisioningKey),
|
||||||
|
logActionAudit(ActionsEnum.createSiteProvisioningKey),
|
||||||
|
siteProvisioning.createSiteProvisioningKey
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.get(
|
||||||
|
"/org/:orgId/site-provisioning-keys",
|
||||||
|
verifyValidLicense,
|
||||||
|
verifyValidSubscription(tierMatrix.siteProvisioningKeys),
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.listSiteProvisioningKeys),
|
||||||
|
siteProvisioning.listSiteProvisioningKeys
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.delete(
|
||||||
|
"/org/:orgId/site-provisioning-key/:siteProvisioningKeyId",
|
||||||
|
verifyValidLicense,
|
||||||
|
verifyValidSubscription(tierMatrix.siteProvisioningKeys),
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeySiteProvisioningKeyAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.deleteSiteProvisioningKey),
|
||||||
|
logActionAudit(ActionsEnum.deleteSiteProvisioningKey),
|
||||||
|
siteProvisioning.deleteSiteProvisioningKey
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.patch(
|
||||||
|
"/org/:orgId/site-provisioning-key/:siteProvisioningKeyId",
|
||||||
|
verifyValidLicense,
|
||||||
|
verifyValidSubscription(tierMatrix.siteProvisioningKeys),
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeySiteProvisioningKeyAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.updateSiteProvisioningKey),
|
||||||
|
logActionAudit(ActionsEnum.updateSiteProvisioningKey),
|
||||||
|
siteProvisioning.updateSiteProvisioningKey
|
||||||
|
);
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { hashPassword } from "@server/auth/password";
|
import { hashPassword } from "@server/auth/password";
|
||||||
import type { CreateSiteProvisioningKeyResponse } from "@server/routers/siteProvisioning/types";
|
import type { CreateSiteProvisioningKeyResponse } from "@server/routers/siteProvisioning/types";
|
||||||
|
import { createApiResponseSchema } from "@server/lib/openapi/createApiResponseSchema";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
const paramsSchema = z.object({
|
||||||
orgId: z.string().nonempty()
|
orgId: z.string().nonempty()
|
||||||
@@ -34,10 +36,17 @@ const paramsSchema = z.object({
|
|||||||
const bodySchema = z
|
const bodySchema = z
|
||||||
.strictObject({
|
.strictObject({
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
maxBatchSize: z.union([
|
maxBatchSize: z
|
||||||
z.null(),
|
.union([
|
||||||
z.coerce.number().int().positive().max(1_000_000)
|
z.null(),
|
||||||
]),
|
z.coerce.number().int().positive().max(1_000_000)
|
||||||
|
])
|
||||||
|
.openapi({
|
||||||
|
type: "number",
|
||||||
|
default: null,
|
||||||
|
description:
|
||||||
|
"Maximum number of sites that can be provisioned in a single batch. If null, there is no limit."
|
||||||
|
}),
|
||||||
validUntil: z.string().max(255).optional(),
|
validUntil: z.string().max(255).optional(),
|
||||||
approveNewSites: z.boolean().optional().default(true)
|
approveNewSites: z.boolean().optional().default(true)
|
||||||
})
|
})
|
||||||
@@ -57,6 +66,49 @@ const bodySchema = z
|
|||||||
|
|
||||||
export type CreateSiteProvisioningKeyBody = z.infer<typeof bodySchema>;
|
export type CreateSiteProvisioningKeyBody = z.infer<typeof bodySchema>;
|
||||||
|
|
||||||
|
const CreateSiteProvisioningKeyResponseDataSchema = z.object({
|
||||||
|
siteProvisioningKeyId: z.string(),
|
||||||
|
orgId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
siteProvisioningKey: z.string(),
|
||||||
|
lastChars: z.string(),
|
||||||
|
createdAt: z.string(),
|
||||||
|
lastUsed: z.string().nullable(),
|
||||||
|
maxBatchSize: z.number().nullable(),
|
||||||
|
numUsed: z.number(),
|
||||||
|
validUntil: z.string().nullable(),
|
||||||
|
approveNewSites: z.boolean()
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "put",
|
||||||
|
path: "/org/{orgId}/site-provisioning-key",
|
||||||
|
description: "Create a new site provisioning key for the organization.",
|
||||||
|
tags: [OpenAPITags.SiteProvisioningKey],
|
||||||
|
request: {
|
||||||
|
params: paramsSchema,
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: bodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
201: {
|
||||||
|
description: "Successful response",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: createApiResponseSchema(
|
||||||
|
CreateSiteProvisioningKeyResponseDataSchema
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export async function createSiteProvisioningKey(
|
export async function createSiteProvisioningKey(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
|
|||||||
@@ -13,23 +13,46 @@
|
|||||||
|
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import {
|
import { db, siteProvisioningKeyOrg, siteProvisioningKeys } from "@server/db";
|
||||||
db,
|
|
||||||
siteProvisioningKeyOrg,
|
|
||||||
siteProvisioningKeys
|
|
||||||
} from "@server/db";
|
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
const paramsSchema = z.object({
|
||||||
siteProvisioningKeyId: z.string().nonempty(),
|
siteProvisioningKeyId: z.string().nonempty(),
|
||||||
orgId: z.string().nonempty()
|
orgId: z.string().nonempty()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "delete",
|
||||||
|
path: "/org/{orgId}/site-provisioning-key/{siteProvisioningKeyId}",
|
||||||
|
description: "Delete a site provisioning key.",
|
||||||
|
tags: [OpenAPITags.SiteProvisioningKey],
|
||||||
|
request: {
|
||||||
|
params: paramsSchema
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Successful response",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: z.object({
|
||||||
|
data: z.record(z.string(), z.any()).nullable(),
|
||||||
|
success: z.boolean(),
|
||||||
|
error: z.boolean(),
|
||||||
|
message: z.string(),
|
||||||
|
status: z.number()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export async function deleteSiteProvisioningKey(
|
export async function deleteSiteProvisioningKey(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
|
|||||||
@@ -11,11 +11,7 @@
|
|||||||
* This file is not licensed under the AGPLv3.
|
* This file is not licensed under the AGPLv3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { db, siteProvisioningKeyOrg, siteProvisioningKeys } from "@server/db";
|
||||||
db,
|
|
||||||
siteProvisioningKeyOrg,
|
|
||||||
siteProvisioningKeys
|
|
||||||
} from "@server/db";
|
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
@@ -25,6 +21,8 @@ import { z } from "zod";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import type { ListSiteProvisioningKeysResponse } from "@server/routers/siteProvisioning/types";
|
import type { ListSiteProvisioningKeysResponse } from "@server/routers/siteProvisioning/types";
|
||||||
|
import { createApiResponseSchema } from "@server/lib/openapi/createApiResponseSchema";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
const paramsSchema = z.object({
|
||||||
orgId: z.string().nonempty()
|
orgId: z.string().nonempty()
|
||||||
@@ -45,11 +43,55 @@ const querySchema = z.object({
|
|||||||
.pipe(z.int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ListSiteProvisioningKeysResponseDataSchema = z.object({
|
||||||
|
siteProvisioningKeys: z.array(
|
||||||
|
z.object({
|
||||||
|
siteProvisioningKeyId: z.string(),
|
||||||
|
orgId: z.string(),
|
||||||
|
lastChars: z.string(),
|
||||||
|
createdAt: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
lastUsed: z.string().nullable(),
|
||||||
|
maxBatchSize: z.number().nullable(),
|
||||||
|
numUsed: z.number(),
|
||||||
|
validUntil: z.string().nullable(),
|
||||||
|
approveNewSites: z.boolean()
|
||||||
|
})
|
||||||
|
),
|
||||||
|
pagination: z.object({
|
||||||
|
total: z.number(),
|
||||||
|
limit: z.number(),
|
||||||
|
offset: z.number()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "get",
|
||||||
|
path: "/org/{orgId}/site-provisioning-keys",
|
||||||
|
description: "List all site provisioning keys for an organization.",
|
||||||
|
tags: [OpenAPITags.SiteProvisioningKey],
|
||||||
|
request: {
|
||||||
|
params: paramsSchema,
|
||||||
|
query: querySchema
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Successful response",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: createApiResponseSchema(
|
||||||
|
ListSiteProvisioningKeysResponseDataSchema
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function querySiteProvisioningKeys(orgId: string) {
|
function querySiteProvisioningKeys(orgId: string) {
|
||||||
return db
|
return db
|
||||||
.select({
|
.select({
|
||||||
siteProvisioningKeyId:
|
siteProvisioningKeyId: siteProvisioningKeys.siteProvisioningKeyId,
|
||||||
siteProvisioningKeys.siteProvisioningKeyId,
|
|
||||||
orgId: siteProvisioningKeyOrg.orgId,
|
orgId: siteProvisioningKeyOrg.orgId,
|
||||||
lastChars: siteProvisioningKeys.lastChars,
|
lastChars: siteProvisioningKeys.lastChars,
|
||||||
createdAt: siteProvisioningKeys.createdAt,
|
createdAt: siteProvisioningKeys.createdAt,
|
||||||
|
|||||||
@@ -13,11 +13,7 @@
|
|||||||
|
|
||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import {
|
import { db, siteProvisioningKeyOrg, siteProvisioningKeys } from "@server/db";
|
||||||
db,
|
|
||||||
siteProvisioningKeyOrg,
|
|
||||||
siteProvisioningKeys
|
|
||||||
} from "@server/db";
|
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
@@ -25,6 +21,8 @@ import createHttpError from "http-errors";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import type { UpdateSiteProvisioningKeyResponse } from "@server/routers/siteProvisioning/types";
|
import type { UpdateSiteProvisioningKeyResponse } from "@server/routers/siteProvisioning/types";
|
||||||
|
import { createApiResponseSchema } from "@server/lib/openapi/createApiResponseSchema";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
const paramsSchema = z.object({
|
||||||
siteProvisioningKeyId: z.string().nonempty(),
|
siteProvisioningKeyId: z.string().nonempty(),
|
||||||
@@ -38,7 +36,13 @@ const bodySchema = z
|
|||||||
z.null(),
|
z.null(),
|
||||||
z.coerce.number().int().positive().max(1_000_000)
|
z.coerce.number().int().positive().max(1_000_000)
|
||||||
])
|
])
|
||||||
.optional(),
|
.optional()
|
||||||
|
.openapi({
|
||||||
|
type: "number",
|
||||||
|
default: null,
|
||||||
|
description:
|
||||||
|
"Maximum number of sites that can be provisioned in a single batch. If null, there is no limit."
|
||||||
|
}),
|
||||||
validUntil: z.string().max(255).optional(),
|
validUntil: z.string().max(255).optional(),
|
||||||
approveNewSites: z.boolean().optional()
|
approveNewSites: z.boolean().optional()
|
||||||
})
|
})
|
||||||
@@ -50,7 +54,8 @@ const bodySchema = z
|
|||||||
) {
|
) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: "custom",
|
code: "custom",
|
||||||
message: "Provide maxBatchSize and/or validUntil and/or approveNewSites",
|
message:
|
||||||
|
"Provide maxBatchSize and/or validUntil and/or approveNewSites",
|
||||||
path: ["maxBatchSize"]
|
path: ["maxBatchSize"]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -69,6 +74,48 @@ const bodySchema = z
|
|||||||
|
|
||||||
export type UpdateSiteProvisioningKeyBody = z.infer<typeof bodySchema>;
|
export type UpdateSiteProvisioningKeyBody = z.infer<typeof bodySchema>;
|
||||||
|
|
||||||
|
const UpdateSiteProvisioningKeyResponseDataSchema = z.object({
|
||||||
|
siteProvisioningKeyId: z.string(),
|
||||||
|
orgId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
lastChars: z.string(),
|
||||||
|
createdAt: z.string(),
|
||||||
|
lastUsed: z.string().nullable(),
|
||||||
|
maxBatchSize: z.number().nullable(),
|
||||||
|
numUsed: z.number(),
|
||||||
|
validUntil: z.string().nullable(),
|
||||||
|
approveNewSites: z.boolean()
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "patch",
|
||||||
|
path: "/org/{orgId}/site-provisioning-key/{siteProvisioningKeyId}",
|
||||||
|
description: "Update a site provisioning key.",
|
||||||
|
tags: [OpenAPITags.SiteProvisioningKey],
|
||||||
|
request: {
|
||||||
|
params: paramsSchema,
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: bodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Successful response",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: createApiResponseSchema(
|
||||||
|
UpdateSiteProvisioningKeyResponseDataSchema
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export async function updateSiteProvisioningKey(
|
export async function updateSiteProvisioningKey(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
|
|||||||
@@ -143,6 +143,13 @@ function getActionsCategories(root: boolean) {
|
|||||||
Logs: {
|
Logs: {
|
||||||
[t("actionExportLogs")]: "exportLogs",
|
[t("actionExportLogs")]: "exportLogs",
|
||||||
[t("actionViewLogs")]: "viewLogs"
|
[t("actionViewLogs")]: "viewLogs"
|
||||||
|
},
|
||||||
|
|
||||||
|
"Site Provisioning Key": {
|
||||||
|
[t("actionCreateSiteProvisioningKey")]: "createSiteProvisioningKey",
|
||||||
|
[t("actionListSiteProvisioningKeys")]: "listSiteProvisioningKeys",
|
||||||
|
[t("actionUpdateSiteProvisioningKey")]: "updateSiteProvisioningKey",
|
||||||
|
[t("actionDeleteSiteProvisioningKey")]: "deleteSiteProvisioningKey"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user