mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-18 02:46:37 +00:00
Merge branch 'dev' into feat/login-page-customization
This commit is contained in:
@@ -1,22 +1,32 @@
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { pullEnv } from "../pullEnv";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
export async function authCookieHeader() {
|
||||
const env = pullEnv();
|
||||
|
||||
const allCookies = await cookies();
|
||||
const cookieName = env.server.sessionCookieName;
|
||||
const sessionId = allCookies.get(cookieName)?.value ?? null;
|
||||
|
||||
// all other headers
|
||||
// this is needed to pass through x-forwarded-for, x-forwarded-proto, etc.
|
||||
const otherHeaders = await headers();
|
||||
const otherHeadersObject = Object.fromEntries(otherHeaders.entries());
|
||||
|
||||
return {
|
||||
headers: {
|
||||
Cookie: `${cookieName}=${sessionId}`,
|
||||
...otherHeadersObject
|
||||
},
|
||||
cookie:
|
||||
otherHeadersObject["cookie"] || otherHeadersObject["Cookie"],
|
||||
host: otherHeadersObject["host"] || otherHeadersObject["Host"],
|
||||
"user-agent":
|
||||
otherHeadersObject["user-agent"] ||
|
||||
otherHeadersObject["User-Agent"],
|
||||
"x-forwarded-for":
|
||||
otherHeadersObject["x-forwarded-for"] ||
|
||||
otherHeadersObject["X-Forwarded-For"],
|
||||
"x-forwarded-host":
|
||||
otherHeadersObject["fx-forwarded-host"] ||
|
||||
otherHeadersObject["Fx-Forwarded-Host"],
|
||||
"x-forwarded-port":
|
||||
otherHeadersObject["x-forwarded-port"] ||
|
||||
otherHeadersObject["X-Forwarded-Port"],
|
||||
"x-forwarded-proto":
|
||||
otherHeadersObject["x-forwarded-proto"] ||
|
||||
otherHeadersObject["X-Forwarded-Proto"],
|
||||
"x-real-ip":
|
||||
otherHeadersObject["x-real-ip"] ||
|
||||
otherHeadersObject["X-Real-IP"]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,15 +6,21 @@ import { pullEnv } from "../pullEnv";
|
||||
import { cache } from "react";
|
||||
|
||||
export const verifySession = cache(async function ({
|
||||
skipCheckVerifyEmail
|
||||
skipCheckVerifyEmail,
|
||||
forceLogin
|
||||
}: {
|
||||
skipCheckVerifyEmail?: boolean;
|
||||
forceLogin?: boolean;
|
||||
} = {}): Promise<GetUserResponse | null> {
|
||||
const env = pullEnv();
|
||||
|
||||
try {
|
||||
const search = new URLSearchParams();
|
||||
if (forceLogin) {
|
||||
search.set("forceLogin", "true");
|
||||
}
|
||||
const res = await internal.get<AxiosResponse<GetUserResponse>>(
|
||||
"/user",
|
||||
`/user?${search.toString()}`,
|
||||
await authCookieHeader()
|
||||
);
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ type PatternConfig = {
|
||||
const patterns: PatternConfig[] = [
|
||||
{ name: "Invite Token", regex: /^\/invite\?token=[a-zA-Z0-9-]+$/ },
|
||||
{ name: "Setup", regex: /^\/setup$/ },
|
||||
{ name: "Resource Auth Portal", regex: /^\/auth\/resource\/\d+$/ }
|
||||
{ name: "Resource Auth Portal", regex: /^\/auth\/resource\/\d+$/ },
|
||||
{ name: "Device Login", regex: /^\/auth\/login\/device(\?code=[a-zA-Z0-9-]+)?$/ }
|
||||
];
|
||||
|
||||
export function cleanRedirect(input: string, fallback?: string): string {
|
||||
|
||||
@@ -55,8 +55,6 @@ export function pullEnv(): Env {
|
||||
process.env.FLAGS_DISABLE_BASIC_WIREGUARD_SITES === "true"
|
||||
? true
|
||||
: false,
|
||||
enableClients:
|
||||
process.env.FLAGS_ENABLE_CLIENTS === "true" ? true : false,
|
||||
hideSupporterKey:
|
||||
process.env.HIDE_SUPPORTER_KEY === "true" ? true : false,
|
||||
usePangolinDns:
|
||||
@@ -66,6 +64,10 @@ export function pullEnv(): Env {
|
||||
branding: {
|
||||
appName: process.env.BRANDING_APP_NAME as string,
|
||||
background_image_path: process.env.BACKGROUND_IMAGE_PATH as string,
|
||||
hideAuthLayoutFooter:
|
||||
process.env.BRANDING_HIDE_AUTH_LAYOUT_FOOTER === "true"
|
||||
? true
|
||||
: false,
|
||||
logo: {
|
||||
lightPath: process.env.BRANDING_LOGO_LIGHT_PATH as string,
|
||||
darkPath: process.env.BRANDING_LOGO_DARK_PATH as string,
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import { keepPreviousData, queryOptions } from "@tanstack/react-query";
|
||||
import { durationToMs } from "./durationToMs";
|
||||
import { build } from "@server/build";
|
||||
import { remote } from "./api";
|
||||
import type { ListClientsResponse } from "@server/routers/client";
|
||||
import type { ListRolesResponse } from "@server/routers/role";
|
||||
import type { ListSitesResponse } from "@server/routers/site";
|
||||
import type {
|
||||
ListSiteResourceClientsResponse,
|
||||
ListSiteResourceRolesResponse,
|
||||
ListSiteResourceUsersResponse
|
||||
} from "@server/routers/siteResource";
|
||||
import type { ListUsersResponse } from "@server/routers/user";
|
||||
import type ResponseT from "@server/types/Response";
|
||||
import z from "zod";
|
||||
import { keepPreviousData, queryOptions } from "@tanstack/react-query";
|
||||
import type { AxiosInstance, AxiosResponse } from "axios";
|
||||
import z from "zod";
|
||||
import { remote } from "./api";
|
||||
import { durationToMs } from "./durationToMs";
|
||||
import type { QueryRequestAnalyticsResponse } from "@server/routers/auditLogs";
|
||||
import type { ListResourceNamesResponse } from "@server/routers/resource";
|
||||
|
||||
@@ -70,6 +79,69 @@ export const productUpdatesQueries = {
|
||||
})
|
||||
};
|
||||
|
||||
export const clientFilterSchema = z.object({
|
||||
filter: z.enum(["machine", "user"]),
|
||||
limit: z.int().prefault(1000).optional()
|
||||
});
|
||||
|
||||
export const orgQueries = {
|
||||
clients: ({
|
||||
orgId,
|
||||
filters
|
||||
}: {
|
||||
orgId: string;
|
||||
filters: z.infer<typeof clientFilterSchema>;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "CLIENTS", filters] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const sp = new URLSearchParams({
|
||||
...filters,
|
||||
limit: (filters.limit ?? 1000).toString()
|
||||
});
|
||||
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<ListClientsResponse>
|
||||
>(`/org/${orgId}/clients?${sp.toString()}`, { signal });
|
||||
|
||||
return res.data.data.clients;
|
||||
}
|
||||
}),
|
||||
users: ({ orgId }: { orgId: string }) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "USERS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<ListUsersResponse>
|
||||
>(`/org/${orgId}/users`, { signal });
|
||||
|
||||
return res.data.data.users;
|
||||
}
|
||||
}),
|
||||
roles: ({ orgId }: { orgId: string }) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "ROLES"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<ListRolesResponse>
|
||||
>(`/org/${orgId}/roles`, { signal });
|
||||
|
||||
return res.data.data.roles;
|
||||
}
|
||||
}),
|
||||
|
||||
sites: ({ orgId }: { orgId: string }) =>
|
||||
queryOptions({
|
||||
queryKey: ["ORG", orgId, "SITES"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<ListSitesResponse>
|
||||
>(`/org/${orgId}/sites`, { signal });
|
||||
return res.data.data.sites;
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
export const logAnalyticsFiltersSchema = z.object({
|
||||
timeStart: z
|
||||
.string()
|
||||
@@ -124,6 +196,38 @@ export const logQueries = {
|
||||
};
|
||||
|
||||
export const resourceQueries = {
|
||||
resourceUsers: ({ resourceId }: { resourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["RESOURCES", resourceId, "USERS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<ListSiteResourceUsersResponse>
|
||||
>(`/site-resource/${resourceId}/users`, { signal });
|
||||
return res.data.data.users;
|
||||
}
|
||||
}),
|
||||
resourceRoles: ({ resourceId }: { resourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["RESOURCES", resourceId, "ROLES"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<ListSiteResourceRolesResponse>
|
||||
>(`/site-resource/${resourceId}/roles`, { signal });
|
||||
|
||||
return res.data.data.roles;
|
||||
}
|
||||
}),
|
||||
resourceClients: ({ resourceId }: { resourceId: number }) =>
|
||||
queryOptions({
|
||||
queryKey: ["RESOURCES", resourceId, "CLIENTS"] as const,
|
||||
queryFn: async ({ signal, meta }) => {
|
||||
const res = await meta!.api.get<
|
||||
AxiosResponse<ListSiteResourceClientsResponse>
|
||||
>(`/site-resource/${resourceId}/clients`, { signal });
|
||||
|
||||
return res.data.data.clients;
|
||||
}
|
||||
}),
|
||||
listNamesPerOrg: (orgId: string, api: AxiosInstance) =>
|
||||
queryOptions({
|
||||
queryKey: ["RESOURCES_NAMES", orgId] as const,
|
||||
|
||||
@@ -30,14 +30,14 @@ export type Env = {
|
||||
allowRawResources: boolean;
|
||||
disableLocalSites: boolean;
|
||||
disableBasicWireguardSites: boolean;
|
||||
enableClients: boolean;
|
||||
hideSupporterKey: boolean;
|
||||
usePangolinDns: boolean;
|
||||
};
|
||||
branding: {
|
||||
appName?: string;
|
||||
background_image_path?: string;
|
||||
logo: {
|
||||
hideAuthLayoutFooter?: boolean;
|
||||
logo?: {
|
||||
lightPath?: string;
|
||||
darkPath?: string;
|
||||
authPage?: {
|
||||
|
||||
Reference in New Issue
Block a user