diff --git a/config.example.yml b/config.example.yml index f84b60a9..0c8ed323 100644 --- a/config.example.yml +++ b/config.example.yml @@ -14,6 +14,7 @@ traefik: cert_resolver: letsencrypt http_entrypoint: web https_entrypoint: websecure + prefer_wildcard_cert: true badger: session_query_parameter: __pang_sess diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index a733c01e..cd8529a1 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -165,7 +165,7 @@ function notAllowed(res: Response, redirectUrl?: string) { error: false, message: "Access denied", status: HttpCode.OK, - } + }; logger.debug(JSON.stringify(data)); return response(res, data); } @@ -177,7 +177,7 @@ function allowed(res: Response) { error: false, message: "Access allowed", status: HttpCode.OK, - } + }; logger.debug(JSON.stringify(data)); return response(res, data); } diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index 50473fad..002e5434 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -94,6 +94,7 @@ export async function createResource( orgId, name, subdomain, + ssl: true, }) .returning(); diff --git a/server/routers/resource/listResources.ts b/server/routers/resource/listResources.ts index 10f19a8e..14b7c4b4 100644 --- a/server/routers/resource/listResources.ts +++ b/server/routers/resource/listResources.ts @@ -6,6 +6,8 @@ import { sites, userResources, roleResources, + resourcePassword, + resourcePincode, } from "@server/db/schema"; import response from "@server/utils/response"; import HttpCode from "@server/types/HttpCode"; @@ -46,39 +48,63 @@ const listResourcesSchema = z.object({ function queryResources( accessibleResourceIds: number[], siteId?: number, - orgId?: string + orgId?: string, ) { if (siteId) { return db .select({ resourceId: resources.resourceId, name: resources.name, - subdomain: resources.subdomain, + fullDomain: resources.fullDomain, + ssl: resources.ssl, siteName: sites.name, + passwordId: resourcePassword.passwordId, + pincodeId: resourcePincode.pincodeId, + sso: resources.sso, }) .from(resources) .leftJoin(sites, eq(resources.siteId, sites.siteId)) + .leftJoin( + resourcePassword, + eq(resourcePassword.resourceId, resources.resourceId), + ) + .leftJoin( + resourcePincode, + eq(resourcePincode.resourceId, resources.resourceId), + ) .where( and( inArray(resources.resourceId, accessibleResourceIds), - eq(resources.siteId, siteId) - ) + eq(resources.siteId, siteId), + ), ); } else if (orgId) { return db .select({ resourceId: resources.resourceId, name: resources.name, - subdomain: resources.subdomain, + ssl: resources.ssl, + fullDomain: resources.fullDomain, siteName: sites.name, + passwordId: resourcePassword.passwordId, + sso: resources.sso, + pincodeId: resourcePincode.pincodeId, }) .from(resources) .leftJoin(sites, eq(resources.siteId, sites.siteId)) + .leftJoin( + resourcePassword, + eq(resourcePassword.resourceId, resources.resourceId), + ) + .leftJoin( + resourcePincode, + eq(resourcePincode.resourceId, resources.resourceId), + ) .where( and( inArray(resources.resourceId, accessibleResourceIds), - eq(resources.orgId, orgId) - ) + eq(resources.orgId, orgId), + ), ); } } @@ -91,7 +117,7 @@ export type ListResourcesResponse = { export async function listResources( req: Request, res: Response, - next: NextFunction + next: NextFunction, ): Promise { try { const parsedQuery = listResourcesSchema.safeParse(req.query); @@ -99,8 +125,8 @@ export async function listResources( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedQuery.error.errors.map((e) => e.message).join(", ") - ) + parsedQuery.error.errors.map((e) => e.message).join(", "), + ), ); } const { limit, offset } = parsedQuery.data; @@ -110,8 +136,8 @@ export async function listResources( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedParams.error.errors.map((e) => e.message).join(", ") - ) + parsedParams.error.errors.map((e) => e.message).join(", "), + ), ); } const { siteId, orgId } = parsedParams.data; @@ -120,8 +146,8 @@ export async function listResources( return next( createHttpError( HttpCode.FORBIDDEN, - "User does not have access to this organization" - ) + "User does not have access to this organization", + ), ); } @@ -132,17 +158,17 @@ export async function listResources( .from(userResources) .fullJoin( roleResources, - eq(userResources.resourceId, roleResources.resourceId) + eq(userResources.resourceId, roleResources.resourceId), ) .where( or( eq(userResources.userId, req.user!.userId), - eq(roleResources.roleId, req.userOrgRoleId!) - ) + eq(roleResources.roleId, req.userOrgRoleId!), + ), ); const accessibleResourceIds = accessibleResources.map( - (resource) => resource.resourceId + (resource) => resource.resourceId, ); let countQuery: any = db @@ -173,7 +199,10 @@ export async function listResources( } catch (error) { logger.error(error); return next( - createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "An error occurred", + ), ); } } diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 5ed9958f..dc2ecb56 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -117,7 +117,7 @@ export async function traefikConfigProvider( ? config.traefik.https_entrypoint : config.traefik.http_entrypoint, ], - middlewares: resource.ssl ? [badgerMiddlewareName] : [], + middlewares: [badgerMiddlewareName], service: serviceName, rule: `Host(\`${fullDomain}\`)`, ...(resource.ssl ? { tls } : {}), diff --git a/src/app/[orgId]/settings/layout.tsx b/src/app/[orgId]/settings/layout.tsx index b6ea8448..45095896 100644 --- a/src/app/[orgId]/settings/layout.tsx +++ b/src/app/[orgId]/settings/layout.tsx @@ -91,7 +91,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { return ( <> -
+
); }, + cell: ({ row }) => { + const resourceRow = row.original; + return ( + + ); + }, }, { accessorKey: "domain", header: "Domain", + cell: ({ row }) => { + const resourceRow = row.original; + return ( +
+ + {resourceRow.domain} + + +
+ ); + }, + }, + { + accessorKey: "hasAuth", + header: "Authentication", + cell: ({ row }) => { + const resourceRow = row.original; + return ( +
+ {resourceRow.hasAuth ? ( + + + Protected + + ) : ( + + + Not Protected + + )} +
+ ); + }, }, { id: "actions", @@ -130,11 +229,11 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { @@ -146,7 +245,7 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { className="ml-2" onClick={() => router.push( - `/${resourceRow.orgId}/settings/resources/${resourceRow.id}` + `/${resourceRow.orgId}/settings/resources/${resourceRow.id}`, ) } > diff --git a/src/app/[orgId]/settings/resources/page.tsx b/src/app/[orgId]/settings/resources/page.tsx index fdd30487..e9aa41c9 100644 --- a/src/app/[orgId]/settings/resources/page.tsx +++ b/src/app/[orgId]/settings/resources/page.tsx @@ -19,7 +19,7 @@ export default async function ResourcesPage(props: ResourcesPageProps) { try { const res = await internal.get>( `/org/${params.orgId}/resources`, - await authCookieHeader() + await authCookieHeader(), ); resources = res.data.data.resources; } catch (e) { @@ -31,8 +31,8 @@ export default async function ResourcesPage(props: ResourcesPageProps) { const getOrg = cache(async () => internal.get>( `/org/${params.orgId}`, - await authCookieHeader() - ) + await authCookieHeader(), + ), ); const res = await getOrg(); org = res.data.data; @@ -49,8 +49,12 @@ export default async function ResourcesPage(props: ResourcesPageProps) { id: resource.resourceId, name: resource.name, orgId: params.orgId, - domain: resource.subdomain || "", + domain: `${resource.ssl ? "https://" : "http://"}${resource.fullDomain}`, site: resource.siteName || "None", + hasAuth: + resource.sso || + resource.pincodeId !== null || + resource.pincodeId !== null, }; }); diff --git a/src/app/[orgId]/settings/sites/components/SitesTable.tsx b/src/app/[orgId]/settings/sites/components/SitesTable.tsx index 124f952e..277dadb0 100644 --- a/src/app/[orgId]/settings/sites/components/SitesTable.tsx +++ b/src/app/[orgId]/settings/sites/components/SitesTable.tsx @@ -24,8 +24,8 @@ export type SiteRow = { id: number; nice: string; name: string; - mbIn: number; - mbOut: number; + mbIn: string; + mbOut: string; orgId: string; }; diff --git a/src/app/[orgId]/settings/sites/page.tsx b/src/app/[orgId]/settings/sites/page.tsx index fc758e92..6aface72 100644 --- a/src/app/[orgId]/settings/sites/page.tsx +++ b/src/app/[orgId]/settings/sites/page.tsx @@ -15,20 +15,30 @@ export default async function SitesPage(props: SitesPageProps) { try { const res = await internal.get>( `/org/${params.orgId}/sites`, - await authCookieHeader() + await authCookieHeader(), ); sites = res.data.data.sites; } catch (e) { console.error("Error fetching sites", e); } + function formatSize(mb: number): string { + if (mb >= 1024 * 1024) { + return `${(mb / (1024 * 1024)).toFixed(2)} TB`; + } else if (mb >= 1024) { + return `${(mb / 1024).toFixed(2)} GB`; + } else { + return `${mb.toFixed(2)} MB`; + } + } + const siteRows: SiteRow[] = sites.map((site) => { return { name: site.name, id: site.siteId, nice: site.niceId.toString(), - mbIn: site.megabytesIn || 0, - mbOut: site.megabytesOut || 0, + mbIn: formatSize(site.megabytesIn || 0), + mbOut: formatSize(site.megabytesOut || 0), orgId: params.orgId, }; }); diff --git a/src/app/globals.css b/src/app/globals.css index fe141f3f..9fe9acbe 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -6,11 +6,11 @@ @layer base { :root { --background: 0 0% 100%; - --foreground: 20 14.3% 4.1%; + --foreground: 20 5.0% 10.0%; --card: 0 0% 100%; - --card-foreground: 20 14.3% 4.1%; + --card-foreground: 20 5.0% 10.0%; --popover: 0 0% 100%; - --popover-foreground: 20 14.3% 4.1%; + --popover-foreground: 20 5.0% 10.0%; --primary: 24.6 95% 53.1%; --primary-foreground: 60 9.1% 97.8%; --secondary: 60 4.8% 95.9%; @@ -33,24 +33,24 @@ } .dark { - --background: 20 14.3% 4.1%; + --background: 20 5.0% 10.0%; --foreground: 60 9.1% 97.8%; - --card: 20 14.3% 4.1%; + --card: 20 5.0% 10.0%; --card-foreground: 60 9.1% 97.8%; - --popover: 20 14.3% 4.1%; + --popover: 20 5.0% 10.0%; --popover-foreground: 60 9.1% 97.8%; --primary: 20.5 90.2% 48.2%; --primary-foreground: 60 9.1% 97.8%; - --secondary: 12 6.5% 15.1%; + --secondary: 12 6.5% 25.0%; --secondary-foreground: 60 9.1% 97.8%; - --muted: 12 6.5% 15.1%; + --muted: 12 6.5% 25.0%; --muted-foreground: 24 5.4% 63.9%; - --accent: 12 6.5% 15.1%; + --accent: 12 6.5% 25.0%; --accent-foreground: 60 9.1% 97.8%; --destructive: 0 72.2% 50.6%; --destructive-foreground: 60 9.1% 97.8%; - --border: 12 6.5% 15.1%; - --input: 12 6.5% 15.1%; + --border: 12 6.5% 25.0%; + --input: 12 6.5% 25.0%; --ring: 20.5 90.2% 48.2%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%;