mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-02 08:09:10 +00:00
✨ add flags for enabling notifications for product updates & new releases
This commit is contained in:
@@ -89,6 +89,16 @@ export class Config {
|
||||
? "true"
|
||||
: "false";
|
||||
|
||||
process.env.PRODUCT_UPDATES_NOTIFICATION_ENABLED = parsedConfig.app
|
||||
.notifications.product_updates
|
||||
? "true"
|
||||
: "false";
|
||||
|
||||
process.env.NEW_RELEASES_NOTIFICATION_ENABLED = parsedConfig.app
|
||||
.notifications.new_releases
|
||||
? "true"
|
||||
: "false";
|
||||
|
||||
if (parsedConfig.server.maxmind_db_path) {
|
||||
process.env.MAXMIND_DB_PATH = parsedConfig.server.maxmind_db_path;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,13 @@ export const configSchema = z
|
||||
anonymous_usage: z.boolean().optional().default(true)
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
notifications: z
|
||||
.object({
|
||||
product_updates: z.boolean().optional().default(true),
|
||||
new_releases: z.boolean().optional().default(true)
|
||||
})
|
||||
.optional()
|
||||
.default({})
|
||||
})
|
||||
.optional()
|
||||
@@ -40,6 +47,10 @@ export const configSchema = z
|
||||
log_failed_attempts: false,
|
||||
telemetry: {
|
||||
anonymous_usage: true
|
||||
},
|
||||
notifications: {
|
||||
product_updates: true,
|
||||
new_releases: true
|
||||
}
|
||||
}),
|
||||
domains: z
|
||||
@@ -205,7 +216,10 @@ export const configSchema = z
|
||||
.default(["newt", "wireguard", "local"]),
|
||||
allow_raw_resources: z.boolean().optional().default(true),
|
||||
file_mode: z.boolean().optional().default(false),
|
||||
pp_transport_prefix: z.string().optional().default("pp-transport-v")
|
||||
pp_transport_prefix: z
|
||||
.string()
|
||||
.optional()
|
||||
.default("pp-transport-v")
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
@@ -315,8 +329,15 @@ export const configSchema = z
|
||||
nameservers: z
|
||||
.array(z.string().optional().optional())
|
||||
.optional()
|
||||
.default(["ns1.pangolin.net", "ns2.pangolin.net", "ns3.pangolin.net"]),
|
||||
cname_extension: z.string().optional().default("cname.pangolin.net")
|
||||
.default([
|
||||
"ns1.pangolin.net",
|
||||
"ns2.pangolin.net",
|
||||
"ns3.pangolin.net"
|
||||
]),
|
||||
cname_extension: z
|
||||
.string()
|
||||
.optional()
|
||||
.default("cname.pangolin.net")
|
||||
})
|
||||
.optional()
|
||||
.default({})
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { useLocalStorage } from "@app/hooks/useLocalStorage";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import { type ProductUpdate, productUpdatesQueries } from "@app/lib/queries";
|
||||
import {
|
||||
type LatestVersionResponse,
|
||||
type ProductUpdate,
|
||||
productUpdatesQueries
|
||||
} from "@app/lib/queries";
|
||||
import { useQueries } from "@tanstack/react-query";
|
||||
import {
|
||||
ArrowRight,
|
||||
@@ -32,10 +36,14 @@ export default function ProductUpdates({
|
||||
}: {
|
||||
isCollapsed?: boolean;
|
||||
}) {
|
||||
const { env } = useEnvContext();
|
||||
|
||||
const data = useQueries({
|
||||
queries: [
|
||||
productUpdatesQueries.list,
|
||||
productUpdatesQueries.latestVersion
|
||||
productUpdatesQueries.list(env.app.notifications.product_updates),
|
||||
productUpdatesQueries.latestVersion(
|
||||
env.app.notifications.new_releases
|
||||
)
|
||||
],
|
||||
combine(result) {
|
||||
if (result[0].isLoading || result[1].isLoading) return null;
|
||||
@@ -45,7 +53,6 @@ export default function ProductUpdates({
|
||||
};
|
||||
}
|
||||
});
|
||||
const { env } = useEnvContext();
|
||||
const t = useTranslations();
|
||||
const [showMoreUpdatesText, setShowMoreUpdatesText] = React.useState(false);
|
||||
|
||||
@@ -302,15 +309,7 @@ function ProductUpdatesListPopup({
|
||||
type NewVersionAvailableProps = {
|
||||
onDimiss: () => void;
|
||||
show: boolean;
|
||||
version:
|
||||
| Awaited<
|
||||
ReturnType<
|
||||
NonNullable<
|
||||
typeof productUpdatesQueries.latestVersion.queryFn
|
||||
>
|
||||
>
|
||||
>["data"]
|
||||
| undefined;
|
||||
version: LatestVersionResponse | null | undefined;
|
||||
};
|
||||
|
||||
function NewVersionAvailable({
|
||||
|
||||
@@ -21,6 +21,14 @@ const envSchema = z.object({
|
||||
.transform((val) => val === "true"),
|
||||
APP_VERSION: z.string(),
|
||||
DASHBOARD_URL: z.string(),
|
||||
PRODUCT_UPDATES_NOTIFICATION_ENABLED: z
|
||||
.string()
|
||||
.default("true")
|
||||
.transform((val) => val === "true"),
|
||||
NEW_RELEASES_NOTIFICATION_ENABLED: z
|
||||
.string()
|
||||
.default("true")
|
||||
.transform((val) => val === "true"),
|
||||
|
||||
// Email configuration
|
||||
EMAIL_ENABLED: z
|
||||
@@ -112,7 +120,11 @@ export function pullEnv(): Env {
|
||||
environment: env.ENVIRONMENT,
|
||||
sandbox_mode: env.SANDBOX_MODE,
|
||||
version: env.APP_VERSION,
|
||||
dashboardUrl: env.DASHBOARD_URL
|
||||
dashboardUrl: env.DASHBOARD_URL,
|
||||
notifications: {
|
||||
product_updates: env.PRODUCT_UPDATES_NOTIFICATION_ENABLED,
|
||||
new_releases: env.NEW_RELEASES_NOTIFICATION_ENABLED
|
||||
}
|
||||
},
|
||||
email: {
|
||||
emailEnabled: env.EMAIL_ENABLED
|
||||
|
||||
@@ -15,47 +15,53 @@ export type ProductUpdate = {
|
||||
showUntil: Date;
|
||||
};
|
||||
|
||||
export const productUpdatesQueries = {
|
||||
list: queryOptions({
|
||||
queryKey: ["PRODUCT_UPDATES"] as const,
|
||||
queryFn: async ({ signal }) => {
|
||||
const sp = new URLSearchParams({
|
||||
build
|
||||
});
|
||||
const data = await remote.get<ResponseT<ProductUpdate[]>>(
|
||||
`/product-updates?${sp.toString()}`,
|
||||
{ signal }
|
||||
);
|
||||
return data.data;
|
||||
},
|
||||
refetchInterval: (query) => {
|
||||
if (query.state.data) {
|
||||
return durationToMs(5, "minutes");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
latestVersion: queryOptions({
|
||||
queryKey: ["LATEST_VERSION"] as const,
|
||||
queryFn: async ({ signal }) => {
|
||||
const data = await remote.get<
|
||||
ResponseT<{
|
||||
pangolin: {
|
||||
latestVersion: string;
|
||||
releaseNotes: string;
|
||||
};
|
||||
}>
|
||||
>("/versions", { signal });
|
||||
return data.data;
|
||||
},
|
||||
placeholderData: keepPreviousData,
|
||||
refetchInterval: (query) => {
|
||||
if (query.state.data) {
|
||||
return durationToMs(30, "minutes");
|
||||
}
|
||||
return false;
|
||||
},
|
||||
enabled: build === "oss" || build === "enterprise" // disabled in cloud version
|
||||
// because we don't need to listen for new versions there
|
||||
})
|
||||
export type LatestVersionResponse = {
|
||||
pangolin: {
|
||||
latestVersion: string;
|
||||
releaseNotes: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const productUpdatesQueries = {
|
||||
list: (enabled: boolean) =>
|
||||
queryOptions({
|
||||
queryKey: ["PRODUCT_UPDATES"] as const,
|
||||
queryFn: async ({ signal }) => {
|
||||
const sp = new URLSearchParams({
|
||||
build
|
||||
});
|
||||
const data = await remote.get<ResponseT<ProductUpdate[]>>(
|
||||
`/product-updates?${sp.toString()}`,
|
||||
{ signal }
|
||||
);
|
||||
return data.data;
|
||||
},
|
||||
refetchInterval: (query) => {
|
||||
if (query.state.data) {
|
||||
return durationToMs(5, "minutes");
|
||||
}
|
||||
return false;
|
||||
},
|
||||
enabled
|
||||
}),
|
||||
latestVersion: (enabled: boolean) =>
|
||||
queryOptions({
|
||||
queryKey: ["LATEST_VERSION"] as const,
|
||||
queryFn: async ({ signal }) => {
|
||||
const data = await remote.get<ResponseT<LatestVersionResponse>>(
|
||||
"/versions",
|
||||
{ signal }
|
||||
);
|
||||
return data.data;
|
||||
},
|
||||
placeholderData: keepPreviousData,
|
||||
refetchInterval: (query) => {
|
||||
if (query.state.data) {
|
||||
return durationToMs(30, "minutes");
|
||||
}
|
||||
return false;
|
||||
},
|
||||
enabled: enabled && (build === "oss" || build === "enterprise") // disabled in cloud version
|
||||
// because we don't need to listen for new versions there
|
||||
})
|
||||
};
|
||||
|
||||
@@ -4,6 +4,10 @@ export type Env = {
|
||||
sandbox_mode: boolean;
|
||||
version: string;
|
||||
dashboardUrl: string;
|
||||
notifications: {
|
||||
product_updates: boolean;
|
||||
new_releases: boolean;
|
||||
};
|
||||
};
|
||||
server: {
|
||||
externalPort: string;
|
||||
|
||||
Reference in New Issue
Block a user