diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index 1f571ab4..1e634cc9 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -71,7 +71,7 @@ export const resources = pgTable("resources", { onDelete: "cascade" }) .notNull(), - niceId: text("niceId"), // TODO: SHOULD THIS BE NULLABLE? + niceId: text("niceId").notNull(), name: varchar("name").notNull(), subdomain: varchar("subdomain"), fullDomain: varchar("fullDomain"), diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 466a937a..4db6fd7a 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -77,7 +77,7 @@ export const resources = sqliteTable("resources", { onDelete: "cascade" }) .notNull(), - niceId: text("niceId"), // TODO: SHOULD THIS BE NULLABLE? + niceId: text("niceId").notNull(), name: text("name").notNull(), subdomain: text("subdomain"), fullDomain: text("fullDomain"), diff --git a/server/routers/external.ts b/server/routers/external.ts index 44629db7..ae0a99f2 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -345,6 +345,12 @@ authenticated.get( verifyUserHasAction(ActionsEnum.getResource), resource.getResource ); +authenticated.get( + "/org/:orgId/resource/:niceId", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.getResource), + resource.getResource +); authenticated.post( "/resource/:resourceId", verifyResourceAccess, diff --git a/server/routers/resource/getResource.ts b/server/routers/resource/getResource.ts index a2c1c0d1..d2aebedd 100644 --- a/server/routers/resource/getResource.ts +++ b/server/routers/resource/getResource.ts @@ -2,32 +2,72 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; import { Resource, resources, sites } from "@server/db"; -import { eq } from "drizzle-orm"; +import { eq, and } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; +import stoi from "@server/lib/stoi"; import { OpenAPITags, registry } from "@server/openApi"; const getResourceSchema = z .object({ resourceId: z .string() - .transform(Number) - .pipe(z.number().int().positive()) + .optional() + .transform(stoi) + .pipe(z.number().int().positive().optional()) + .optional(), + niceId: z.string().optional(), + orgId: z.string().optional() }) .strict(); -export type GetResourceResponse = Resource; +async function query(resourceId?: number, niceId?: string, orgId?: string) { + if (resourceId) { + const [res] = await db + .select() + .from(resources) + .where(eq(resources.resourceId, resourceId)) + .limit(1); + return res; + } else if (niceId && orgId) { + const [res] = await db + .select() + .from(resources) + .where(and(eq(resources.niceId, niceId), eq(resources.orgId, orgId))) + .limit(1); + return res; + } +} + +export type GetResourceResponse = NonNullable>>; + +registry.registerPath({ + method: "get", + path: "/org/{orgId}/resource/{niceId}", + description: + "Get a resource by orgId and niceId. NiceId is a readable ID for the resource and unique on a per org basis.", + tags: [OpenAPITags.Org, OpenAPITags.Resource], + request: { + params: z.object({ + orgId: z.string(), + niceId: z.string() + }) + }, + responses: {} +}); registry.registerPath({ method: "get", path: "/resource/{resourceId}", - description: "Get a resource.", + description: "Get a resource by resourceId.", tags: [OpenAPITags.Resource], request: { - params: getResourceSchema + params: z.object({ + resourceId: z.number() + }) }, responses: {} }); @@ -48,29 +88,18 @@ export async function getResource( ); } - const { resourceId } = parsedParams.data; + const { resourceId, niceId, orgId } = parsedParams.data; - const [resp] = await db - .select() - .from(resources) - .where(eq(resources.resourceId, resourceId)) - .limit(1); - - const resource = resp; + const resource = await query(resourceId, niceId, orgId); if (!resource) { return next( - createHttpError( - HttpCode.NOT_FOUND, - `Resource with ID ${resourceId} not found` - ) + createHttpError(HttpCode.NOT_FOUND, "Resource not found") ); } - return response(res, { - data: { - ...resource - }, + return response(res, { + data: resource, success: true, error: false, message: "Resource retrieved successfully", diff --git a/server/routers/resource/listResources.ts b/server/routers/resource/listResources.ts index 43757b27..27605be6 100644 --- a/server/routers/resource/listResources.ts +++ b/server/routers/resource/listResources.ts @@ -16,6 +16,7 @@ import logger from "@server/logger"; import stoi from "@server/lib/stoi"; import { fromZodError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; +import { warn } from "console"; const listResourcesParamsSchema = z .object({ @@ -54,7 +55,8 @@ function queryResources(accessibleResourceIds: number[], orgId: string) { protocol: resources.protocol, proxyPort: resources.proxyPort, enabled: resources.enabled, - domainId: resources.domainId + domainId: resources.domainId, + niceId: resources.niceId }) .from(resources) .leftJoin( diff --git a/src/app/[orgId]/settings/resources/page.tsx b/src/app/[orgId]/settings/resources/page.tsx index b1b907e6..97abdd4c 100644 --- a/src/app/[orgId]/settings/resources/page.tsx +++ b/src/app/[orgId]/settings/resources/page.tsx @@ -76,8 +76,7 @@ export default async function ResourcesPage(props: ResourcesPageProps) { id: resource.resourceId, name: resource.name, orgId: params.orgId, - - + nice: resource.niceId, domain: `${resource.ssl ? "https://" : "http://"}${toUnicode(resource.fullDomain || "")}`, protocol: resource.protocol, proxyPort: resource.proxyPort, @@ -91,7 +90,8 @@ export default async function ResourcesPage(props: ResourcesPageProps) { ? "protected" : "not_protected", enabled: resource.enabled, - domainId: resource.domainId || undefined + domainId: resource.domainId || undefined, + ssl: resource.ssl }; }); diff --git a/src/components/ResourcesTable.tsx b/src/components/ResourcesTable.tsx index 622d42da..8a62c14b 100644 --- a/src/components/ResourcesTable.tsx +++ b/src/components/ResourcesTable.tsx @@ -66,6 +66,7 @@ import { Alert, AlertDescription } from "@app/components/ui/alert"; export type ResourceRow = { id: number; + nice: string | null; name: string; orgId: string; domain: string; @@ -75,6 +76,7 @@ export type ResourceRow = { proxyPort: number | null; enabled: boolean; domainId?: string; + ssl: boolean; }; export type InternalResourceRow = { @@ -336,12 +338,28 @@ export default function ResourcesTable({ ); } }, + { + accessorKey: "nice", + header: ({ column }) => { + return ( + + ); + } + }, { accessorKey: "protocol", header: t("protocol"), cell: ({ row }) => { const resourceRow = row.original; - return {resourceRow.protocol.toUpperCase()}; + return {resourceRow.http ? (resourceRow.ssl ? "HTTPS" : "HTTP") : resourceRow.protocol.toUpperCase()}; } }, {