mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-04 19:51:29 +02:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7ff3f4815 | |||
| efe22c889c | |||
| 7f2b3eb481 | |||
| 81be4a35d9 | |||
| e84da6a8df | |||
| 3ef3ede7df | |||
| 1e521b0b54 | |||
| 59ea701304 | |||
| 7d7c54107d | |||
| f0f6673d69 | |||
| 71561d0e65 | |||
| af87edf3a6 | |||
| 13caad18c7 | |||
| 47522b7e3a | |||
| c8c8d74452 | |||
| e7098963d6 | |||
| f015fb592b | |||
| c099167905 | |||
| b0e274f5a9 | |||
| e0a8721207 |
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:24-alpine
|
||||
FROM node:26-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -258,7 +258,24 @@ export const configSchema = z
|
||||
pp_transport_prefix: z
|
||||
.string()
|
||||
.optional()
|
||||
.default("pp-transport-v")
|
||||
.default("pp-transport-v"),
|
||||
rate_limit: z
|
||||
.object({
|
||||
average: z
|
||||
.number()
|
||||
.positive()
|
||||
.gt(0)
|
||||
.optional()
|
||||
.default(10),
|
||||
burst: z
|
||||
.number()
|
||||
.positive()
|
||||
.gt(0)
|
||||
.optional()
|
||||
.default(16)
|
||||
})
|
||||
.optional()
|
||||
.prefault({})
|
||||
})
|
||||
.optional()
|
||||
.prefault({}),
|
||||
|
||||
@@ -58,6 +58,8 @@ import { build } from "@server/build";
|
||||
const redirectHttpsMiddlewareName = "redirect-to-https";
|
||||
const redirectToRootMiddlewareName = "redirect-to-root";
|
||||
const badgerMiddlewareName = "badger";
|
||||
const landingRateLimitMiddlewareName = "landing-ratelimit";
|
||||
const bgRateLimitMiddlewareName = "bg-ratelimit";
|
||||
|
||||
// Define extended target type with site information
|
||||
type TargetWithSite = Target & {
|
||||
@@ -418,6 +420,8 @@ export async function getTraefikConfig(
|
||||
// logger.debug(`Valid certs for domains: ${JSON.stringify(validCerts)}`);
|
||||
}
|
||||
|
||||
const traefikRateLimit = config.getRawConfig().traefik.rate_limit;
|
||||
|
||||
const config_output: any = {
|
||||
http: {
|
||||
middlewares: {
|
||||
@@ -432,6 +436,18 @@ export async function getTraefikConfig(
|
||||
replacement: "${1}://${2}/auth/org",
|
||||
permanent: false
|
||||
}
|
||||
},
|
||||
[landingRateLimitMiddlewareName]: {
|
||||
rateLimit: {
|
||||
average: traefikRateLimit.average,
|
||||
burst: traefikRateLimit.burst
|
||||
}
|
||||
},
|
||||
[bgRateLimitMiddlewareName]: {
|
||||
rateLimit: {
|
||||
average: traefikRateLimit.average,
|
||||
burst: traefikRateLimit.burst
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1055,6 +1071,7 @@ export async function getTraefikConfig(
|
||||
config.getRawConfig().traefik.additional_middlewares || [];
|
||||
const routerMiddlewares = [
|
||||
badgerMiddlewareName,
|
||||
bgRateLimitMiddlewareName,
|
||||
...additionalMiddlewares
|
||||
];
|
||||
|
||||
@@ -1539,6 +1556,7 @@ export async function getTraefikConfig(
|
||||
entryPoints: [
|
||||
config.getRawConfig().traefik.https_entrypoint
|
||||
],
|
||||
middlewares: [landingRateLimitMiddlewareName],
|
||||
service: "landing-service",
|
||||
rule: `Host(\`${fullDomain}\`) && (PathRegexp(\`^/auth/resource/[^/]+$\`) || PathRegexp(\`^/auth/idp/[0-9]+/oidc/callback\`) || PathPrefix(\`/_next\`) || Path(\`/auth/org\`) || PathRegexp(\`^/__nextjs*\`) || Path(\`/favicon.ico\`))`,
|
||||
priority: 203,
|
||||
@@ -1557,7 +1575,10 @@ export async function getTraefikConfig(
|
||||
entryPoints: [
|
||||
config.getRawConfig().traefik.https_entrypoint
|
||||
],
|
||||
middlewares: [redirectToRootMiddlewareName],
|
||||
middlewares: [
|
||||
landingRateLimitMiddlewareName,
|
||||
redirectToRootMiddlewareName
|
||||
],
|
||||
service: "landing-service",
|
||||
rule: `Host(\`${fullDomain}\`)`,
|
||||
priority: 202,
|
||||
|
||||
@@ -46,7 +46,7 @@ const getCertificateQuerySchema = z.object({
|
||||
|
||||
async function query(orgId: string, domainList: string[]) {
|
||||
// Try to get CNAME certificates first
|
||||
let existingCertificates = await db
|
||||
const existingCertificates = await db
|
||||
.select({
|
||||
certId: certificates.certId,
|
||||
domain: certificates.domain,
|
||||
@@ -73,16 +73,19 @@ async function query(orgId: string, domainList: string[]) {
|
||||
.where(and(inArray(certificates.domain, domainList)));
|
||||
|
||||
// All non resolved domain certificates might be `ns` or `wildcard`,
|
||||
// which means exact domain certificates do not
|
||||
const nonAvailableCertificates = existingCertificates
|
||||
.filter((cert) => !domainList.includes(cert.domain))
|
||||
.map((cert) => cert.domain);
|
||||
// which means exact domain certificates do not exist
|
||||
const foundDomains = new Set(
|
||||
existingCertificates.map((cert) => cert.domain)
|
||||
);
|
||||
const domainsWithMissingCertificates = domainList.filter(
|
||||
(domain) => !foundDomains.has(domain)
|
||||
);
|
||||
|
||||
if (nonAvailableCertificates.length > 0) {
|
||||
if (domainsWithMissingCertificates.length > 0) {
|
||||
const domainLevelDownSet = new Set<string>();
|
||||
const wildcardDomainSet = new Set<string>();
|
||||
|
||||
for (const domain of nonAvailableCertificates) {
|
||||
for (const domain of domainsWithMissingCertificates) {
|
||||
const domainLevelDown = domain.split(".").slice(1).join(".");
|
||||
const wildcardPrefixed = `*.${domainLevelDown}`;
|
||||
domainLevelDownSet.add(domainLevelDown);
|
||||
@@ -131,6 +134,7 @@ async function query(orgId: string, domainList: string[]) {
|
||||
for (const domain of domainList) {
|
||||
const domainLevelDown = domain.split(".").slice(1).join(".");
|
||||
const wildcardPrefixed = `*.${domainLevelDown}`;
|
||||
|
||||
certificateMap[domain] =
|
||||
existingCertificates.find(
|
||||
(cert) =>
|
||||
|
||||
@@ -67,12 +67,12 @@ const listUserDevicesSchema = z.strictObject({
|
||||
}),
|
||||
query: z.string().optional(),
|
||||
sort_by: z
|
||||
.enum(["megabytesIn", "megabytesOut"])
|
||||
.enum(["megabytesIn", "megabytesOut", "firstSeen", "lastSeen"])
|
||||
.optional()
|
||||
.catch(undefined)
|
||||
.openapi({
|
||||
type: "string",
|
||||
enum: ["megabytesIn", "megabytesOut"],
|
||||
enum: ["megabytesIn", "megabytesOut", "firstSeen", "lastSeen"],
|
||||
description: "Field to sort by"
|
||||
}),
|
||||
order: z
|
||||
@@ -183,7 +183,9 @@ function queryUserDevicesBase() {
|
||||
fingerprintArch: currentFingerprint.arch,
|
||||
fingerprintSerialNumber: currentFingerprint.serialNumber,
|
||||
fingerprintUsername: currentFingerprint.username,
|
||||
fingerprintHostname: currentFingerprint.hostname
|
||||
fingerprintHostname: currentFingerprint.hostname,
|
||||
firstSeen: currentFingerprint.firstSeen,
|
||||
lastSeen: currentFingerprint.lastSeen
|
||||
})
|
||||
.from(clients)
|
||||
.leftJoin(orgs, eq(clients.orgId, orgs.orgId))
|
||||
@@ -389,14 +391,23 @@ export async function listUserDevices(
|
||||
|
||||
const countQuery = db.$count(baseQuery.as("filtered_clients"));
|
||||
|
||||
const sortColumn =
|
||||
sort_by === "firstSeen"
|
||||
? currentFingerprint.firstSeen
|
||||
: sort_by === "lastSeen"
|
||||
? currentFingerprint.lastSeen
|
||||
: sort_by
|
||||
? clients[sort_by]
|
||||
: undefined;
|
||||
|
||||
const listDevicesQuery = baseQuery
|
||||
.limit(pageSize)
|
||||
.offset(pageSize * (page - 1))
|
||||
.orderBy(
|
||||
sort_by
|
||||
sortColumn
|
||||
? order === "asc"
|
||||
? asc(clients[sort_by])
|
||||
: desc(clients[sort_by])
|
||||
? asc(sortColumn)
|
||||
: desc(sortColumn)
|
||||
: asc(clients.clientId)
|
||||
);
|
||||
|
||||
|
||||
@@ -112,7 +112,9 @@ export async function updateHolePunch(
|
||||
destinations: destinations
|
||||
});
|
||||
} catch (error) {
|
||||
if (!(error instanceof Error && error.message === "Exit node not allowed")) {
|
||||
logger.error(error);
|
||||
}
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.INTERNAL_SERVER_ERROR,
|
||||
|
||||
@@ -104,7 +104,9 @@ export default async function ClientsPage(props: ClientsPageProps) {
|
||||
archived: Boolean(client.archived),
|
||||
blocked: Boolean(client.blocked),
|
||||
approvalState: client.approvalState,
|
||||
fingerprint
|
||||
fingerprint,
|
||||
firstSeen: client.firstSeen ?? null,
|
||||
lastSeen: client.lastSeen ?? null
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ export default async function Page(props: {
|
||||
let loginIdps: LoginFormIDP[] = [];
|
||||
let lastUsedIdpForSmartLogin: (LoginFormIDP & { orgId?: string }) | null =
|
||||
null;
|
||||
|
||||
if (!useSmartLogin) {
|
||||
// Load IdPs for DashboardLoginForm (OSS or org-only IdP mode)
|
||||
if (build === "oss" || env.app.identityProviderMode !== "org") {
|
||||
@@ -117,12 +118,12 @@ export default async function Page(props: {
|
||||
`/idp/${persistedData.idpId}`
|
||||
);
|
||||
|
||||
const idp = idpRes.data.data.idp;
|
||||
const res = idpRes.data.data;
|
||||
|
||||
lastUsedIdpForSmartLogin = {
|
||||
idpId: idp.idpId,
|
||||
name: idp.name,
|
||||
variant: idp.type,
|
||||
idpId: res.idp.idpId,
|
||||
name: res.idp.name,
|
||||
variant: res.idpOidcConfig?.variant ?? res.idp.type,
|
||||
orgId: persistedData.orgId,
|
||||
lastUsed: true
|
||||
};
|
||||
|
||||
@@ -26,12 +26,14 @@ type IdpLoginButtonsProps = {
|
||||
idps: LoginFormIDP[];
|
||||
redirect?: string;
|
||||
orgId?: string;
|
||||
passOrgIdToOidcUrl?: boolean;
|
||||
};
|
||||
|
||||
export default function IdpLoginButtons({
|
||||
idps,
|
||||
redirect,
|
||||
orgId
|
||||
orgId,
|
||||
passOrgIdToOidcUrl = true
|
||||
}: IdpLoginButtonsProps) {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const t = useTranslations();
|
||||
@@ -68,12 +70,13 @@ export default function IdpLoginButtons({
|
||||
|
||||
let redirectToUrl: string | undefined;
|
||||
try {
|
||||
console.log("generating", idpId, redirect || "/", orgId);
|
||||
const oidcOrgId = passOrgIdToOidcUrl ? orgId : undefined;
|
||||
console.log("generating", idpId, redirect || "/", oidcOrgId);
|
||||
const safeRedirect = cleanRedirect(redirect || "/");
|
||||
const response = await generateOidcUrlProxy(
|
||||
idpId,
|
||||
safeRedirect,
|
||||
orgId
|
||||
oidcOrgId
|
||||
);
|
||||
|
||||
if (response.error) {
|
||||
@@ -114,7 +117,6 @@ export default function IdpLoginButtons({
|
||||
|
||||
<div className="space-y-4">
|
||||
{params.get("gotoapp") ? (
|
||||
<>
|
||||
<Button
|
||||
type="button"
|
||||
className="w-full"
|
||||
@@ -124,18 +126,13 @@ export default function IdpLoginButtons({
|
||||
>
|
||||
{t("continueToApplication")}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{idps.map((idp) => {
|
||||
idps.map((idp) => {
|
||||
const effectiveType =
|
||||
idp.variant || idp.name.toLowerCase();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full relative"
|
||||
key={idp.idpId}
|
||||
>
|
||||
<div className="w-full relative" key={idp.idpId}>
|
||||
<Button
|
||||
key={idp.idpId}
|
||||
type="button"
|
||||
@@ -165,8 +162,7 @@ export default function IdpLoginButtons({
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,6 +23,8 @@ export default function IdpTypeIcon({
|
||||
}: Props) {
|
||||
const effectiveType = (variant || type || "").toLowerCase();
|
||||
|
||||
console.log(`[IdpTypeIcon]`, { effectiveType, variant, type });
|
||||
|
||||
let src: string | null = null;
|
||||
let defaultAlt = "";
|
||||
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
||||
import { IdpDataTable } from "@app/components/OrgIdpDataTable";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList
|
||||
} from "@app/components/ui/command";
|
||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||
import {
|
||||
Credenza,
|
||||
CredenzaBody,
|
||||
@@ -22,37 +11,42 @@ import {
|
||||
CredenzaHeader,
|
||||
CredenzaTitle
|
||||
} from "@app/components/Credenza";
|
||||
import { isIdpGlobalModeBannerVisible } from "@app/components/IdpGlobalModeBanner";
|
||||
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
||||
import IdpTypeIcon from "@app/components/IdpTypeIcon";
|
||||
import { IdpDataTable } from "@app/components/OrgIdpDataTable";
|
||||
import { Badge } from "@app/components/ui/badge";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
ArrowRight,
|
||||
ArrowUpDown,
|
||||
MoreHorizontal
|
||||
} from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { formatAxiosError } from "@app/lib/api";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { useUserContext } from "@app/hooks/useUserContext";
|
||||
import { useRouter } from "next/navigation";
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList
|
||||
} from "@app/components/ui/command";
|
||||
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger
|
||||
} from "@app/components/ui/dropdown-menu";
|
||||
import Link from "next/link";
|
||||
import { useTranslations } from "next-intl";
|
||||
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
||||
import IdpTypeIcon from "@app/components/IdpTypeIcon";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useDebounce } from "use-debounce";
|
||||
import type { ListUserAdminOrgIdpsResponse } from "@server/routers/orgIdp/types";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import { Badge } from "@app/components/ui/badge";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { useUserContext } from "@app/hooks/useUserContext";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import { isIdpGlobalModeBannerVisible } from "@app/components/IdpGlobalModeBanner";
|
||||
import type { ListUserAdminOrgIdpsResponse } from "@server/routers/orgIdp/types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ArrowRight, ArrowUpDown, MoreHorizontal } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useDebounce } from "use-debounce";
|
||||
|
||||
export type IdpRow = {
|
||||
idpId: number;
|
||||
@@ -483,7 +477,8 @@ export default function IdpTable({ idps, orgId }: Props) {
|
||||
{group.name}
|
||||
</div>
|
||||
<div className="mt-1 flex flex-wrap gap-1">
|
||||
{group.sources.map((src) => (
|
||||
{group.sources.map(
|
||||
(src) => (
|
||||
<Badge
|
||||
key={src.orgId}
|
||||
variant="secondary"
|
||||
@@ -491,7 +486,8 @@ export default function IdpTable({ idps, orgId }: Props) {
|
||||
>
|
||||
{src.orgName}
|
||||
</Badge>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CommandItem>
|
||||
|
||||
@@ -303,6 +303,7 @@ export default function SmartLoginForm({
|
||||
<IdpLoginButtons
|
||||
idps={[lastUsedIdp]}
|
||||
orgId={lastUsedIdp.orgId}
|
||||
passOrgIdToOidcUrl={false}
|
||||
redirect={redirect}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -134,7 +134,9 @@ export default function UptimeBar({
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
const allNoData = data.days.every((d) => d.status === "no_data");
|
||||
const allNoData = data.days.every(
|
||||
(d) => d.status === "no_data" || d.status === "unknown"
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={cn("space-y-3", className)}>
|
||||
|
||||
@@ -124,7 +124,9 @@ export function UptimeMiniBar({
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
const allNoData = data.days.every((d) => d.status === "no_data");
|
||||
const allNoData = data.days.every(
|
||||
(d) => d.status === "no_data" || d.status === "unknown"
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -77,6 +77,8 @@ export type ClientRow = {
|
||||
username: string | null;
|
||||
hostname: string | null;
|
||||
} | null;
|
||||
firstSeen: number | null;
|
||||
lastSeen: number | null;
|
||||
};
|
||||
|
||||
type ClientTableProps = {
|
||||
@@ -112,7 +114,9 @@ export default function UserDevicesTable({
|
||||
|
||||
const defaultUserColumnVisibility = {
|
||||
subnet: false,
|
||||
niceId: false
|
||||
niceId: false,
|
||||
firstSeen: false,
|
||||
lastSeen: false
|
||||
};
|
||||
|
||||
const refreshData = () => {
|
||||
@@ -621,6 +625,68 @@ export default function UserDevicesTable({
|
||||
accessorKey: "subnet",
|
||||
friendlyName: t("address"),
|
||||
header: () => <span className="px-3">{t("address")}</span>
|
||||
},
|
||||
{
|
||||
accessorKey: "firstSeen",
|
||||
friendlyName: t("firstSeen"),
|
||||
header: () => {
|
||||
const firstSeenOrder = getSortDirection(
|
||||
"firstSeen",
|
||||
searchParams
|
||||
);
|
||||
|
||||
const Icon =
|
||||
firstSeenOrder === "asc"
|
||||
? ArrowDown01Icon
|
||||
: firstSeenOrder === "desc"
|
||||
? ArrowUp10Icon
|
||||
: ChevronsUpDownIcon;
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => toggleSort("firstSeen")}
|
||||
>
|
||||
{t("firstSeen")}
|
||||
<Icon className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const firstSeen = row.original.firstSeen;
|
||||
if (!firstSeen) return "-";
|
||||
return new Date(firstSeen * 1000).toLocaleString();
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "lastSeen",
|
||||
friendlyName: t("lastSeen"),
|
||||
header: () => {
|
||||
const lastSeenOrder = getSortDirection(
|
||||
"lastSeen",
|
||||
searchParams
|
||||
);
|
||||
|
||||
const Icon =
|
||||
lastSeenOrder === "asc"
|
||||
? ArrowDown01Icon
|
||||
: lastSeenOrder === "desc"
|
||||
? ArrowUp10Icon
|
||||
: ChevronsUpDownIcon;
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => toggleSort("lastSeen")}
|
||||
>
|
||||
{t("lastSeen")}
|
||||
<Icon className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const lastSeen = row.original.lastSeen;
|
||||
if (!lastSeen) return "-";
|
||||
return new Date(lastSeen * 1000).toLocaleString();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -111,7 +111,8 @@ export function useCertificate({
|
||||
let certError: string | null = null;
|
||||
if (restartCert.isError) {
|
||||
certError = "Failed to restart";
|
||||
} else if (isError) {
|
||||
} else if (isError || initialCertValue === null) {
|
||||
// Null value means failed to get the certificate
|
||||
certError = "Failed";
|
||||
}
|
||||
|
||||
|
||||
+27
-29
@@ -1,4 +1,10 @@
|
||||
import type { LauncherQueryFilters } from "@app/lib/launcherSearchParams";
|
||||
import { buildLauncherSearchParams } from "@app/lib/launcherSearchParams";
|
||||
import { build } from "@server/build";
|
||||
import {
|
||||
StatusHistoryResponse,
|
||||
type BatchedStatusHistoryResponse
|
||||
} from "@server/lib/statusHistory";
|
||||
import type { ListAlertRulesResponse } from "@server/routers/alertRule/types";
|
||||
import type { QueryRequestAnalyticsResponse } from "@server/routers/auditLogs";
|
||||
import type {
|
||||
@@ -7,6 +13,7 @@ import type {
|
||||
QueryConnectionAuditLogResponse,
|
||||
QueryRequestAuditLogResponse
|
||||
} from "@server/routers/auditLogs/types";
|
||||
import type { GetCertificateResponse } from "@server/routers/certificates/types";
|
||||
import type {
|
||||
ListClientsResponse,
|
||||
ListUserDevicesResponse
|
||||
@@ -16,15 +23,30 @@ import type {
|
||||
ListDomainsResponse
|
||||
} from "@server/routers/domain";
|
||||
import type { GetDomainResponse } from "@server/routers/domain/getDomain";
|
||||
import { ListHealthChecksResponse } from "@server/routers/healthChecks/types";
|
||||
import type { ListOrgLabelsResponse } from "@server/routers/labels/types";
|
||||
import type {
|
||||
LauncherResource,
|
||||
ListLauncherGroupsResponse,
|
||||
ListLauncherLabelsResponse,
|
||||
ListLauncherResourcesResponse,
|
||||
ListLauncherScaleResponse,
|
||||
ListLauncherSitesResponse,
|
||||
ListLauncherViewsResponse
|
||||
} from "@server/routers/launcher/types";
|
||||
import type { GetResourcePolicyResponse } from "@server/routers/policy";
|
||||
import type {
|
||||
GetResourceWhitelistResponse,
|
||||
GetResourcePoliciesResponse,
|
||||
GetResourceWhitelistResponse,
|
||||
ListResourceNamesResponse,
|
||||
ListResourcesResponse,
|
||||
ListResourceRolesResponse,
|
||||
ListResourceRulesResponse,
|
||||
ListResourcesResponse,
|
||||
ListResourceUsersResponse
|
||||
} from "@server/routers/resource";
|
||||
import type { GetResourceResponse } from "@server/routers/resource/getResource";
|
||||
import type { GetResourceAuthInfoResponse } from "@server/routers/resource/getResourceAuthInfo";
|
||||
import type { ListResourcePoliciesResponse } from "@server/routers/resource/types";
|
||||
import type { ListRolesResponse } from "@server/routers/role";
|
||||
import type { ListSitesResponse } from "@server/routers/site";
|
||||
import type {
|
||||
@@ -33,6 +55,7 @@ import type {
|
||||
ListSiteResourceRolesResponse,
|
||||
ListSiteResourceUsersResponse
|
||||
} from "@server/routers/siteResource";
|
||||
import type { GetSiteResourceResponse } from "@server/routers/siteResource/getSiteResource";
|
||||
import type { ListTargetsResponse } from "@server/routers/target";
|
||||
import type { ListUsersResponse } from "@server/routers/user";
|
||||
import type ResponseT from "@server/types/Response";
|
||||
@@ -42,37 +65,12 @@ import {
|
||||
queryOptions
|
||||
} from "@tanstack/react-query";
|
||||
import { isAxiosError, type AxiosResponse } from "axios";
|
||||
import z, { meta } from "zod";
|
||||
import z from "zod";
|
||||
import { remote } from "./api";
|
||||
import { durationToMs } from "./durationToMs";
|
||||
import type { ListOrgLabelsResponse } from "@server/routers/labels/types";
|
||||
import { ListHealthChecksResponse } from "@server/routers/healthChecks/types";
|
||||
import {
|
||||
StatusHistoryResponse,
|
||||
type BatchedStatusHistoryResponse
|
||||
} from "@server/lib/statusHistory";
|
||||
import type { ListResourcePoliciesResponse } from "@server/routers/resource/types";
|
||||
import type { GetResourcePolicyResponse } from "@server/routers/policy";
|
||||
import type {
|
||||
ListLauncherGroupsResponse,
|
||||
ListLauncherLabelsResponse,
|
||||
ListLauncherResourcesResponse,
|
||||
ListLauncherScaleResponse,
|
||||
ListLauncherSitesResponse,
|
||||
ListLauncherViewsResponse,
|
||||
LauncherListQuery,
|
||||
LauncherResource,
|
||||
LauncherViewConfig
|
||||
} from "@server/routers/launcher/types";
|
||||
import type { GetResourceResponse } from "@server/routers/resource/getResource";
|
||||
import type { GetResourceAuthInfoResponse } from "@server/routers/resource/getResourceAuthInfo";
|
||||
import type { GetSiteResourceResponse } from "@server/routers/siteResource/getSiteResource";
|
||||
import type { LauncherQueryFilters } from "@app/lib/launcherSearchParams";
|
||||
import { buildLauncherSearchParams } from "@app/lib/launcherSearchParams";
|
||||
import type { GetCertificateResponse } from "@server/routers/certificates/types";
|
||||
|
||||
export type { LauncherQueryFilters } from "@app/lib/launcherSearchParams";
|
||||
export { buildLauncherSearchParams } from "@app/lib/launcherSearchParams";
|
||||
export type { LauncherQueryFilters } from "@app/lib/launcherSearchParams";
|
||||
|
||||
export type ProductUpdate = {
|
||||
link: string | null;
|
||||
|
||||
Reference in New Issue
Block a user