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