Merge branch 'provisioning-room' into dev

This commit is contained in:
Owen
2026-03-30 16:19:11 -07:00
21 changed files with 875 additions and 110 deletions

View File

@@ -392,7 +392,8 @@ export const siteProvisioningKeys = pgTable("siteProvisioningKeys", {
lastUsed: varchar("lastUsed", { length: 255 }),
maxBatchSize: integer("maxBatchSize"), // null = no limit
numUsed: integer("numUsed").notNull().default(0),
validUntil: varchar("validUntil", { length: 255 })
validUntil: varchar("validUntil", { length: 255 }),
approveNewSites: boolean("approveNewSites").notNull().default(true)
});
export const siteProvisioningKeyOrg = pgTable(

View File

@@ -100,7 +100,8 @@ export const sites = pgTable("sites", {
publicKey: varchar("publicKey"),
lastHolePunch: bigint("lastHolePunch", { mode: "number" }),
listenPort: integer("listenPort"),
dockerSocketEnabled: boolean("dockerSocketEnabled").notNull().default(true)
dockerSocketEnabled: boolean("dockerSocketEnabled").notNull().default(true),
status: varchar("status").$type<"pending" | "approved">()
});
export const resources = pgTable("resources", {

View File

@@ -376,7 +376,10 @@ export const siteProvisioningKeys = sqliteTable("siteProvisioningKeys", {
lastUsed: text("lastUsed"),
maxBatchSize: integer("maxBatchSize"), // null = no limit
numUsed: integer("numUsed").notNull().default(0),
validUntil: text("validUntil")
validUntil: text("validUntil"),
approveNewSites: integer("approveNewSites", { mode: "boolean" })
.notNull()
.default(true)
});
export const siteProvisioningKeyOrg = sqliteTable(

View File

@@ -110,7 +110,8 @@ export const sites = sqliteTable("sites", {
listenPort: integer("listenPort"),
dockerSocketEnabled: integer("dockerSocketEnabled", { mode: "boolean" })
.notNull()
.default(true)
.default(true),
status: text("status").$type<"pending" | "approved">()
});
export const resources = sqliteTable("resources", {

View File

@@ -38,7 +38,8 @@ const bodySchema = z
z.null(),
z.coerce.number().int().positive().max(1_000_000)
]),
validUntil: z.string().max(255).optional()
validUntil: z.string().max(255).optional(),
approveNewSites: z.boolean().optional().default(true)
})
.superRefine((data, ctx) => {
const v = data.validUntil;
@@ -82,7 +83,7 @@ export async function createSiteProvisioningKey(
}
const { orgId } = parsedParams.data;
const { name, maxBatchSize } = parsedBody.data;
const { name, maxBatchSize, approveNewSites } = parsedBody.data;
const vuRaw = parsedBody.data.validUntil;
const validUntil =
vuRaw == null || vuRaw.trim() === ""
@@ -106,7 +107,8 @@ export async function createSiteProvisioningKey(
lastUsed: null,
maxBatchSize,
numUsed: 0,
validUntil
validUntil,
approveNewSites
});
await trx.insert(siteProvisioningKeyOrg).values({
@@ -127,7 +129,8 @@ export async function createSiteProvisioningKey(
lastUsed: null,
maxBatchSize,
numUsed: 0,
validUntil
validUntil,
approveNewSites
},
success: true,
error: false,

View File

@@ -57,7 +57,8 @@ function querySiteProvisioningKeys(orgId: string) {
lastUsed: siteProvisioningKeys.lastUsed,
maxBatchSize: siteProvisioningKeys.maxBatchSize,
numUsed: siteProvisioningKeys.numUsed,
validUntil: siteProvisioningKeys.validUntil
validUntil: siteProvisioningKeys.validUntil,
approveNewSites: siteProvisioningKeys.approveNewSites
})
.from(siteProvisioningKeyOrg)
.innerJoin(

View File

@@ -39,16 +39,18 @@ const bodySchema = z
z.coerce.number().int().positive().max(1_000_000)
])
.optional(),
validUntil: z.string().max(255).optional()
validUntil: z.string().max(255).optional(),
approveNewSites: z.boolean().optional()
})
.superRefine((data, ctx) => {
if (
data.maxBatchSize === undefined &&
data.validUntil === undefined
data.validUntil === undefined &&
data.approveNewSites === undefined
) {
ctx.addIssue({
code: "custom",
message: "Provide maxBatchSize and/or validUntil",
message: "Provide maxBatchSize and/or validUntil and/or approveNewSites",
path: ["maxBatchSize"]
});
}
@@ -129,6 +131,7 @@ export async function updateSiteProvisioningKey(
const setValues: {
maxBatchSize?: number | null;
validUntil?: string | null;
approveNewSites?: boolean;
} = {};
if (body.maxBatchSize !== undefined) {
setValues.maxBatchSize = body.maxBatchSize;
@@ -139,6 +142,9 @@ export async function updateSiteProvisioningKey(
? null
: new Date(Date.parse(body.validUntil)).toISOString();
}
if (body.approveNewSites !== undefined) {
setValues.approveNewSites = body.approveNewSites;
}
await db
.update(siteProvisioningKeys)
@@ -160,7 +166,8 @@ export async function updateSiteProvisioningKey(
lastUsed: siteProvisioningKeys.lastUsed,
maxBatchSize: siteProvisioningKeys.maxBatchSize,
numUsed: siteProvisioningKeys.numUsed,
validUntil: siteProvisioningKeys.validUntil
validUntil: siteProvisioningKeys.validUntil,
approveNewSites: siteProvisioningKeys.approveNewSites
})
.from(siteProvisioningKeys)
.where(

View File

@@ -82,7 +82,8 @@ export async function registerNewt(
orgId: siteProvisioningKeyOrg.orgId,
maxBatchSize: siteProvisioningKeys.maxBatchSize,
numUsed: siteProvisioningKeys.numUsed,
validUntil: siteProvisioningKeys.validUntil
validUntil: siteProvisioningKeys.validUntil,
approveNewSites: siteProvisioningKeys.approveNewSites,
})
.from(siteProvisioningKeys)
.innerJoin(
@@ -196,7 +197,8 @@ export async function registerNewt(
name: niceId,
niceId,
type: "newt",
dockerSocketEnabled: true
dockerSocketEnabled: true,
status: keyRecord.approveNewSites ? "approved" : "pending",
})
.returning();

View File

@@ -298,7 +298,8 @@ export async function createSite(
niceId,
address: updatedAddress || null,
type,
dockerSocketEnabled: true
dockerSocketEnabled: true,
status: "approved"
})
.returning();
} else if (type == "wireguard") {
@@ -355,7 +356,8 @@ export async function createSite(
niceId,
subnet,
type,
pubKey: pubKey || null
pubKey: pubKey || null,
status: "approved"
})
.returning();
} else if (type == "local") {
@@ -370,7 +372,8 @@ export async function createSite(
type,
dockerSocketEnabled: false,
online: true,
subnet: "0.0.0.0/32"
subnet: "0.0.0.0/32",
status: "approved"
})
.returning();
} else {

View File

@@ -135,6 +135,15 @@ const listSitesSchema = z.object({
.openapi({
type: "boolean",
description: "Filter by online status"
}),
status: z
.enum(["pending", "approved"])
.optional()
.catch(undefined)
.openapi({
type: "string",
enum: ["pending", "approved"],
description: "Filter by site status"
})
});
@@ -156,7 +165,8 @@ function querySitesBase() {
exitNodeId: sites.exitNodeId,
exitNodeName: exitNodes.name,
exitNodeEndpoint: exitNodes.endpoint,
remoteExitNodeId: remoteExitNodes.remoteExitNodeId
remoteExitNodeId: remoteExitNodes.remoteExitNodeId,
status: sites.status
})
.from(sites)
.leftJoin(orgs, eq(sites.orgId, orgs.orgId))
@@ -245,7 +255,7 @@ export async function listSites(
.where(eq(sites.orgId, orgId));
}
const { pageSize, page, query, sort_by, order, online } =
const { pageSize, page, query, sort_by, order, online, status } =
parsedQuery.data;
const accessibleSiteIds = accessibleSites.map((site) => site.siteId);
@@ -273,6 +283,9 @@ export async function listSites(
if (typeof online !== "undefined") {
conditions.push(eq(sites.online, online));
}
if (typeof status !== "undefined") {
conditions.push(eq(sites.status, status));
}
const baseQuery = querySitesBase().where(and(...conditions));

View File

@@ -19,7 +19,8 @@ const updateSiteBodySchema = z
.strictObject({
name: z.string().min(1).max(255).optional(),
niceId: z.string().min(1).max(255).optional(),
dockerSocketEnabled: z.boolean().optional()
dockerSocketEnabled: z.boolean().optional(),
status: z.enum(["pending", "approved"]).optional(),
// remoteSubnets: z.string().optional()
// subdomain: z
// .string()

View File

@@ -8,6 +8,7 @@ export type SiteProvisioningKeyListItem = {
maxBatchSize: number | null;
numUsed: number;
validUntil: string | null;
approveNewSites: boolean;
};
export type ListSiteProvisioningKeysResponse = {
@@ -26,6 +27,7 @@ export type CreateSiteProvisioningKeyResponse = {
maxBatchSize: number | null;
numUsed: number;
validUntil: string | null;
approveNewSites: boolean;
};
export type UpdateSiteProvisioningKeyResponse = {
@@ -38,4 +40,5 @@ export type UpdateSiteProvisioningKeyResponse = {
maxBatchSize: number | null;
numUsed: number;
validUntil: string | null;
approveNewSites: boolean;
};