mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-17 21:01:53 +00:00
Compare commits
15 Commits
copilot/ad
...
1.19.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e271028f3 | ||
|
|
820f66e58f | ||
|
|
b0fdc10e06 | ||
|
|
b82b41ed26 | ||
|
|
3e977ba00d | ||
|
|
a724b07846 | ||
|
|
5f0bc71bcd | ||
|
|
aea7827c1a | ||
|
|
d865c4c55b | ||
|
|
cfe33eb974 | ||
|
|
3cc244a1d3 | ||
|
|
10542d7282 | ||
|
|
7fa1180d10 | ||
|
|
8b50f1fb65 | ||
|
|
527d4cc777 |
@@ -34,4 +34,5 @@ build.ts
|
||||
tsconfig.json
|
||||
Dockerfile*
|
||||
drizzle.config.ts
|
||||
allowedDevOrigins.json
|
||||
allowedDevOrigins.json
|
||||
scratch/
|
||||
|
||||
@@ -984,7 +984,7 @@
|
||||
"sharedPolicy": "Shared Policy",
|
||||
"sharedPolicyNoneDescription": "This resource has its own policy.",
|
||||
"resourceSharedPolicyOwnDescription": "This resource has its own authentication and access rules controls.",
|
||||
"resourceSharedPolicyInheritedDescription": "This resource inherits authentication and access rules controls from <policyLink>{policyName}</policyLink>.",
|
||||
"resourceSharedPolicyInheritedDescription": "This resource inherits from <policyLink>{policyName}</policyLink>.",
|
||||
"resourceSharedPolicyAuthenticationNotice": "This resource is using a shared policy. Some authentication settings can be edited on this resource to add to the policy. To change the underlying policy, you must edit to <policyLink>{policyName}</policyLink>.",
|
||||
"resourceSharedPolicyRulesNotice": "This resource is using a shared policy. Some access rules can be edited on this resource. To change the underlying policy, you must edit <policyLink>{policyName}</policyLink>.",
|
||||
"resourceUsersRoles": "Access Controls",
|
||||
|
||||
@@ -228,7 +228,7 @@ export default async function migration() {
|
||||
).run();
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE 'siteResources' SET 'destination2' = 'destination';
|
||||
UPDATE 'siteResources' SET "destination2" = "destination";
|
||||
`
|
||||
).run();
|
||||
db.prepare(
|
||||
@@ -349,9 +349,9 @@ export default async function migration() {
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE 'targets'
|
||||
SET 'mode' = (
|
||||
SELECT 'mode' FROM 'resources'
|
||||
WHERE 'resources'.'resourceId' = 'targets'.'resourceId'
|
||||
SET "mode" = (
|
||||
SELECT "mode" FROM 'resources'
|
||||
WHERE "resources"."resourceId" = "targets"."resourceId"
|
||||
);
|
||||
`
|
||||
).run();
|
||||
|
||||
@@ -3,7 +3,9 @@ import { authCookieHeader } from "@app/lib/api/cookies";
|
||||
import { ListOrgLabelsResponse } from "@server/routers/labels/types";
|
||||
import { AxiosResponse } from "axios";
|
||||
import OrgLabelsTable from "@app/components/OrgLabelsTable";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import type { Metadata } from "next";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
|
||||
@@ -49,6 +51,8 @@ export default async function LabelsPage({ params, searchParams }: Props) {
|
||||
description={t("orgLabelsDescription")}
|
||||
/>
|
||||
|
||||
<PaidFeaturesAlert tiers={tierMatrix.labels} />
|
||||
|
||||
<OrgLabelsTable
|
||||
labels={labels}
|
||||
orgId={orgId}
|
||||
|
||||
@@ -43,6 +43,7 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { tierMatrix, TierFeature } from "@server/lib/billing/tierMatrix";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { env } from "process";
|
||||
|
||||
// Schema for general organization settings
|
||||
const GeneralFormSchema = z.object({
|
||||
@@ -165,6 +166,7 @@ function DeleteForm({ org }: SectionFormProps) {
|
||||
|
||||
function GeneralSectionForm({ org }: SectionFormProps) {
|
||||
const { updateOrg } = useOrgContext();
|
||||
const { env } = useEnvContext();
|
||||
const form = useForm({
|
||||
resolver: zodResolver(
|
||||
GeneralFormSchema.pick({
|
||||
@@ -265,36 +267,42 @@ function GeneralSectionForm({ org }: SectionFormProps) {
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.newtAutoUpdate}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="settingsEnableGlobalNewtAutoUpdate"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<SwitchInput
|
||||
id="settings-enable-global-newt-auto-update"
|
||||
label={t("newtAutoUpdate")}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!hasAutoUpdateFeature}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t("newtAutoUpdateDescription")}{" "}
|
||||
<a
|
||||
href="https://docs.pangolin.net/manage/sites/auto-update"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline inline-flex items-center gap-1"
|
||||
>
|
||||
{t("learnMore")}
|
||||
<ExternalLink className="size-3.5 shrink-0" />
|
||||
</a>
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{!env.flags.disableEnterpriseFeatures && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="settingsEnableGlobalNewtAutoUpdate"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<SwitchInput
|
||||
id="settings-enable-global-newt-auto-update"
|
||||
label={t("newtAutoUpdate")}
|
||||
checked={field.value}
|
||||
onCheckedChange={
|
||||
field.onChange
|
||||
}
|
||||
disabled={
|
||||
!hasAutoUpdateFeature
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t("newtAutoUpdateDescription")}{" "}
|
||||
<a
|
||||
href="https://docs.pangolin.net/manage/sites/auto-update"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline inline-flex items-center gap-1"
|
||||
>
|
||||
{t("learnMore")}
|
||||
<ExternalLink className="size-3.5 shrink-0" />
|
||||
</a>
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
</Form>
|
||||
</SettingsSectionForm>
|
||||
|
||||
@@ -19,14 +19,14 @@ import {
|
||||
SettingsSectionBody,
|
||||
SettingsSectionDescription,
|
||||
SettingsSectionFooter,
|
||||
SettingsFormCell,
|
||||
SettingsFormGrid,
|
||||
SettingsSectionForm,
|
||||
SettingsSectionHeader,
|
||||
SettingsSectionTitle,
|
||||
SettingsSubsectionDescription,
|
||||
SettingsSubsectionHeader,
|
||||
SettingsSubsectionTitle
|
||||
SettingsSubsectionTitle,
|
||||
SettingsFormCell
|
||||
} from "@app/components/Settings";
|
||||
import { SwitchInput } from "@app/components/SwitchInput";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
@@ -70,7 +70,7 @@ export default function GeneralForm() {
|
||||
|
||||
const api = createApiClient({ env });
|
||||
|
||||
const showResourcePolicy =
|
||||
const hasResourcePolicies =
|
||||
build !== "oss" &&
|
||||
isPaidUser(tierMatrix[TierFeature.ResourcePolicies]);
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function GeneralForm() {
|
||||
...orgQueries.resourcePolicy({
|
||||
resourcePolicyId: selectedSharedPolicyId!
|
||||
}),
|
||||
enabled: showResourcePolicy && selectedSharedPolicyId !== null
|
||||
enabled: hasResourcePolicies && selectedSharedPolicyId !== null
|
||||
});
|
||||
|
||||
const [resourceFullDomain, setResourceFullDomain] = useState(
|
||||
@@ -153,11 +153,10 @@ export default function GeneralForm() {
|
||||
|
||||
let resourcePolicyId: number | null | undefined;
|
||||
|
||||
if (
|
||||
showResourcePolicy &&
|
||||
!["tcp", "udp"].includes(resource.mode)
|
||||
) {
|
||||
resourcePolicyId = selectedSharedPolicyId;
|
||||
if (!["tcp", "udp"].includes(resource.mode)) {
|
||||
if (hasResourcePolicies || selectedSharedPolicyId === null) {
|
||||
resourcePolicyId = selectedSharedPolicyId;
|
||||
}
|
||||
}
|
||||
|
||||
const res = await api
|
||||
@@ -297,28 +296,6 @@ export default function GeneralForm() {
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
|
||||
<SettingsFormCell span="full">
|
||||
<SettingsSubsectionHeader>
|
||||
<SettingsSubsectionTitle>
|
||||
{t(
|
||||
"resourceGeneralDetailsSubsection"
|
||||
)}
|
||||
</SettingsSubsectionTitle>
|
||||
<SettingsSubsectionDescription>
|
||||
{t(
|
||||
[
|
||||
"tcp",
|
||||
"udp",
|
||||
].includes(
|
||||
resource.mode
|
||||
)
|
||||
? "resourceGeneralDetailsSubsectionPortDescription"
|
||||
: "resourceGeneralDetailsSubsectionDescription"
|
||||
)}
|
||||
</SettingsSubsectionDescription>
|
||||
</SettingsSubsectionHeader>
|
||||
</SettingsFormCell>
|
||||
|
||||
<SettingsFormCell span="half">
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -476,10 +453,9 @@ export default function GeneralForm() {
|
||||
</div>
|
||||
</SettingsFormCell>
|
||||
)}
|
||||
{showResourcePolicy &&
|
||||
!["tcp", "udp"].includes(
|
||||
{ !["tcp", "udp"].includes(
|
||||
resource.mode
|
||||
) && (
|
||||
) && !env.flags.disableEnterpriseFeatures && (
|
||||
<>
|
||||
<SettingsFormCell span="full">
|
||||
<SettingsSubsectionHeader>
|
||||
|
||||
@@ -169,20 +169,27 @@ export default function ResourceMaintenancePage() {
|
||||
{
|
||||
id: "automatic",
|
||||
title: `${t("automatic")} (${t("recommended")})`,
|
||||
description: t("automaticModeDescription"),
|
||||
disabled: isMaintenanceDisabled
|
||||
description: t("automaticModeDescription")
|
||||
},
|
||||
{
|
||||
id: "forced",
|
||||
title: t("forced"),
|
||||
description: t("forcedModeDescription"),
|
||||
disabled: isMaintenanceDisabled
|
||||
description: t("forcedModeDescription")
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<SettingsSection>
|
||||
<>
|
||||
<PaidFeaturesAlert tiers={tierMatrix.maintencePage} />
|
||||
<div
|
||||
className={
|
||||
isMaintenanceDisabled
|
||||
? "pointer-events-none opacity-50"
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<SettingsContainer>
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>
|
||||
{t("maintenanceMode")}
|
||||
@@ -193,7 +200,6 @@ export default function ResourceMaintenancePage() {
|
||||
</SettingsSectionHeader>
|
||||
|
||||
<SettingsSectionBody>
|
||||
<PaidFeaturesAlert tiers={tierMatrix.maintencePage} />
|
||||
<SettingsSectionForm variant="half">
|
||||
<Form {...maintenanceForm}>
|
||||
<form
|
||||
@@ -205,46 +211,33 @@ export default function ResourceMaintenancePage() {
|
||||
<FormField
|
||||
control={maintenanceForm.control}
|
||||
name="maintenanceModeEnabled"
|
||||
render={({ field }) => {
|
||||
const isDisabled = !isPaidUser(
|
||||
tierMatrix.maintencePage
|
||||
);
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<SwitchInput
|
||||
id="enable-maintenance"
|
||||
checked={
|
||||
field.value
|
||||
}
|
||||
label={t(
|
||||
"enableMaintenanceMode"
|
||||
)}
|
||||
description={t(
|
||||
"enableMaintenanceModeDescription"
|
||||
)}
|
||||
disabled={
|
||||
isDisabled
|
||||
}
|
||||
onCheckedChange={(
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<SwitchInput
|
||||
id="enable-maintenance"
|
||||
checked={
|
||||
field.value
|
||||
}
|
||||
label={t(
|
||||
"enableMaintenanceMode"
|
||||
)}
|
||||
description={t(
|
||||
"enableMaintenanceModeDescription"
|
||||
)}
|
||||
onCheckedChange={(
|
||||
val
|
||||
) => {
|
||||
maintenanceForm.setValue(
|
||||
"maintenanceModeEnabled",
|
||||
val
|
||||
) => {
|
||||
if (
|
||||
!isDisabled
|
||||
) {
|
||||
maintenanceForm.setValue(
|
||||
"maintenanceModeEnabled",
|
||||
val
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</SettingsFormCell>
|
||||
|
||||
@@ -329,11 +322,6 @@ export default function ResourceMaintenancePage() {
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
disabled={
|
||||
!isPaidUser(
|
||||
tierMatrix.maintencePage
|
||||
)
|
||||
}
|
||||
placeholder="We'll be back soon!"
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -365,11 +353,6 @@ export default function ResourceMaintenancePage() {
|
||||
<Textarea
|
||||
{...field}
|
||||
rows={4}
|
||||
disabled={
|
||||
!isPaidUser(
|
||||
tierMatrix.maintencePage
|
||||
)
|
||||
}
|
||||
placeholder={t(
|
||||
"maintenancePageMessagePlaceholder"
|
||||
)}
|
||||
@@ -402,11 +385,6 @@ export default function ResourceMaintenancePage() {
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
disabled={
|
||||
!isPaidUser(
|
||||
tierMatrix.maintencePage
|
||||
)
|
||||
}
|
||||
placeholder={t(
|
||||
"maintenanceTime"
|
||||
)}
|
||||
@@ -430,20 +408,19 @@ export default function ResourceMaintenancePage() {
|
||||
</SettingsSectionForm>
|
||||
</SettingsSectionBody>
|
||||
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
loading={maintenanceSaveLoading}
|
||||
disabled={
|
||||
maintenanceSaveLoading ||
|
||||
!isPaidUser(tierMatrix.maintencePage)
|
||||
}
|
||||
form="maintenance-settings-form"
|
||||
>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSection>
|
||||
</SettingsContainer>
|
||||
<SettingsSectionFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
loading={maintenanceSaveLoading}
|
||||
disabled={maintenanceSaveLoading}
|
||||
form="maintenance-settings-form"
|
||||
>
|
||||
{t("saveSettings")}
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSection>
|
||||
</SettingsContainer>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -253,85 +253,87 @@ export default function GeneralPage() {
|
||||
<PaidFeaturesAlert
|
||||
tiers={tierMatrix.newtAutoUpdate}
|
||||
/>
|
||||
{site && site.type === "newt" && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="autoUpdateEnabled"
|
||||
render={({ field }) => {
|
||||
const isOverriding = form.watch(
|
||||
"autoUpdateOverrideOrg"
|
||||
);
|
||||
return (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<div className="">
|
||||
<SwitchInput
|
||||
id="auto-update-enabled"
|
||||
label={t(
|
||||
"siteAutoUpdateLabel"
|
||||
)}
|
||||
checked={
|
||||
field.value
|
||||
}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
) => {
|
||||
field.onChange(
|
||||
{site &&
|
||||
site.type === "newt" &&
|
||||
!env.flags.disableEnterpriseFeatures && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="autoUpdateEnabled"
|
||||
render={({ field }) => {
|
||||
const isOverriding = form.watch(
|
||||
"autoUpdateOverrideOrg"
|
||||
);
|
||||
return (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<div className="">
|
||||
<SwitchInput
|
||||
id="auto-update-enabled"
|
||||
label={t(
|
||||
"siteAutoUpdateLabel"
|
||||
)}
|
||||
checked={
|
||||
field.value
|
||||
}
|
||||
onCheckedChange={(
|
||||
checked
|
||||
);
|
||||
form.setValue(
|
||||
"autoUpdateOverrideOrg",
|
||||
true
|
||||
);
|
||||
}}
|
||||
disabled={
|
||||
!hasAutoUpdateFeature
|
||||
}
|
||||
/>
|
||||
{isOverriding && (
|
||||
<ButtonUI
|
||||
type="button"
|
||||
variant="link"
|
||||
size="sm"
|
||||
className="text-sm text-muted-foreground px-0"
|
||||
onClick={() => {
|
||||
) => {
|
||||
field.onChange(
|
||||
checked
|
||||
);
|
||||
form.setValue(
|
||||
"autoUpdateOverrideOrg",
|
||||
false
|
||||
);
|
||||
form.setValue(
|
||||
"autoUpdateEnabled",
|
||||
orgAutoUpdate
|
||||
true
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
"siteAutoUpdateResetToOrg"
|
||||
)}
|
||||
</ButtonUI>
|
||||
)}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t(
|
||||
"siteAutoUpdateDescription"
|
||||
)}{" "}
|
||||
<a
|
||||
href="https://docs.pangolin.net/manage/sites/auto-update"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline inline-flex items-center gap-1"
|
||||
>
|
||||
{t("learnMore")}
|
||||
<ExternalLink className="size-3.5 shrink-0" />
|
||||
</a>
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
disabled={
|
||||
!hasAutoUpdateFeature
|
||||
}
|
||||
/>
|
||||
{isOverriding && (
|
||||
<ButtonUI
|
||||
type="button"
|
||||
variant="link"
|
||||
size="sm"
|
||||
className="text-sm text-muted-foreground px-0"
|
||||
onClick={() => {
|
||||
form.setValue(
|
||||
"autoUpdateOverrideOrg",
|
||||
false
|
||||
);
|
||||
form.setValue(
|
||||
"autoUpdateEnabled",
|
||||
orgAutoUpdate
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
"siteAutoUpdateResetToOrg"
|
||||
)}
|
||||
</ButtonUI>
|
||||
)}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
{t(
|
||||
"siteAutoUpdateDescription"
|
||||
)}{" "}
|
||||
<a
|
||||
href="https://docs.pangolin.net/manage/sites/auto-update"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline inline-flex items-center gap-1"
|
||||
>
|
||||
{t("learnMore")}
|
||||
<ExternalLink className="size-3.5 shrink-0" />
|
||||
</a>
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
</Form>
|
||||
</SettingsSectionForm>
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
} from "@app/components/ui/form";
|
||||
import HeaderTitle from "@app/components/SettingsSectionTitle";
|
||||
import { z } from "zod";
|
||||
import { createElement, useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Input } from "@app/components/ui/input";
|
||||
@@ -37,15 +37,6 @@ import {
|
||||
InfoSections,
|
||||
InfoSectionTitle
|
||||
} from "@app/components/InfoSection";
|
||||
import {
|
||||
FaApple,
|
||||
FaCubes,
|
||||
FaDocker,
|
||||
FaFreebsd,
|
||||
FaWindows
|
||||
} from "react-icons/fa";
|
||||
import { SiNixos, SiKubernetes } from "react-icons/si";
|
||||
import { Checkbox, CheckboxWithLabel } from "@app/components/ui/checkbox";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
|
||||
import { generateKeypair } from "../[niceId]/wireguardConfig";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
|
||||
@@ -156,10 +156,11 @@ export const orgNavSections = (
|
||||
]
|
||||
: []),
|
||||
// PaidFeaturesAlert
|
||||
...((build === "oss" && !env?.flags.disableEnterpriseFeatures) ||
|
||||
build === "saas" ||
|
||||
env?.app.identityProviderMode === "org" ||
|
||||
(env?.app.identityProviderMode === undefined && build !== "oss")
|
||||
...(!env?.flags.disableEnterpriseFeatures &&
|
||||
(build === "saas" ||
|
||||
env?.app.identityProviderMode === "org" ||
|
||||
(env?.app.identityProviderMode === undefined &&
|
||||
build !== "oss"))
|
||||
? [
|
||||
{
|
||||
title: "sidebarIdentityProviders",
|
||||
@@ -259,7 +260,7 @@ export const orgNavSections = (
|
||||
href: "/{orgId}/settings/api-keys",
|
||||
icon: <KeyRound className="size-4 flex-none" />
|
||||
},
|
||||
...(build !== "oss"
|
||||
...(!env?.flags.disableEnterpriseFeatures
|
||||
? [
|
||||
{
|
||||
title: "labels",
|
||||
|
||||
@@ -12,14 +12,7 @@ import { useNavigationContext } from "@app/hooks/useNavigationContext";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
import { type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
ArrowDown01Icon,
|
||||
ArrowUp10Icon,
|
||||
ChevronsUpDownIcon,
|
||||
MoreHorizontal,
|
||||
PencilIcon,
|
||||
PencilLineIcon
|
||||
} from "lucide-react";
|
||||
import { ArrowRight, MoreHorizontal } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useActionState, useMemo, useState, useTransition } from "react";
|
||||
@@ -109,7 +102,7 @@ export default function OrgLabelsTable({
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center gap-1.5 group">
|
||||
<div
|
||||
className="size-2.5 rounded-full bg-(--color) flex-none"
|
||||
className="size-2 rounded-full bg-(--color) flex-none"
|
||||
style={{
|
||||
// @ts-expect-error css color
|
||||
"--color": row.original.color
|
||||
@@ -125,34 +118,40 @@ export default function OrgLabelsTable({
|
||||
enableHiding: false,
|
||||
header: () => <span className="p-3"></span>,
|
||||
cell: ({ row }) => (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">{t("openMenu")}</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setSelectedLabel(row.original);
|
||||
setIsEditModalOpen(true);
|
||||
}}
|
||||
>
|
||||
{t("edit")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setSelectedLabel(row.original);
|
||||
setIsDeleteModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<span className="text-red-500">
|
||||
{t("delete")}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="flex items-center gap-2 justify-end">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">
|
||||
{t("openMenu")}
|
||||
</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setSelectedLabel(row.original);
|
||||
setIsDeleteModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<span className="text-red-500">
|
||||
{t("delete")}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setSelectedLabel(row.original);
|
||||
setIsEditModalOpen(true);
|
||||
}}
|
||||
>
|
||||
{t("edit")}
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
],
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from "react-icons/fa";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { SiKubernetes, SiNixos } from "react-icons/si";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
|
||||
export type CommandItem = string | { title: string; command: string };
|
||||
|
||||
@@ -50,9 +51,12 @@ export function NewtSiteInstallCommands({
|
||||
version = "latest"
|
||||
}: NewtSiteInstallCommandsProps) {
|
||||
const t = useTranslations();
|
||||
const { env } = useEnvContext();
|
||||
|
||||
const [acceptClients, setAcceptClients] = useState(true);
|
||||
const [allowPangolinSsh, setAllowPangolinSsh] = useState(true);
|
||||
const [allowPangolinSsh, setAllowPangolinSsh] = useState(
|
||||
!env.flags.disableEnterpriseFeatures
|
||||
);
|
||||
const [platform, setPlatform] = useState<Platform>("linux");
|
||||
const [architecture, setArchitecture] = useState(
|
||||
() => getArchitectures(platform)[0]
|
||||
@@ -71,7 +75,11 @@ export function NewtSiteInstallCommands({
|
||||
: "";
|
||||
|
||||
const disableSshFlag =
|
||||
supportsSshOption && !allowPangolinSsh ? " --disable-ssh" : "";
|
||||
supportsSshOption &&
|
||||
!allowPangolinSsh &&
|
||||
!env.flags.disableEnterpriseFeatures
|
||||
? " --disable-ssh"
|
||||
: "";
|
||||
const runAsRootPrefix =
|
||||
supportsSshOption && allowPangolinSsh ? "sudo " : "";
|
||||
|
||||
@@ -306,27 +314,29 @@ WantedBy=default.target`
|
||||
>
|
||||
{t("siteAcceptClientConnectionsDescription")}
|
||||
</p>
|
||||
{supportsSshOption && (
|
||||
<>
|
||||
<div className="flex items-center space-x-2 mb-2 mt-2">
|
||||
<CheckboxWithLabel
|
||||
id="allowPangolinSsh"
|
||||
checked={allowPangolinSsh}
|
||||
onCheckedChange={(checked) => {
|
||||
const value = checked as boolean;
|
||||
setAllowPangolinSsh(value);
|
||||
}}
|
||||
label="Allow Pangolin SSH"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
id="allowPangolinSsh-desc"
|
||||
className="text-sm text-muted-foreground"
|
||||
>
|
||||
{t("sitePangolinSshDescription")}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
{supportsSshOption &&
|
||||
!env.flags.disableEnterpriseFeatures && (
|
||||
<>
|
||||
<div className="flex items-center space-x-2 mb-2 mt-2">
|
||||
<CheckboxWithLabel
|
||||
id="allowPangolinSsh"
|
||||
checked={allowPangolinSsh}
|
||||
onCheckedChange={(checked) => {
|
||||
const value =
|
||||
checked as boolean;
|
||||
setAllowPangolinSsh(value);
|
||||
}}
|
||||
label="Allow Pangolin SSH"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
id="allowPangolinSsh-desc"
|
||||
className="text-sm text-muted-foreground"
|
||||
>
|
||||
{t("sitePangolinSshDescription")}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -73,7 +73,9 @@ export function EditPolicyForm({
|
||||
}
|
||||
|
||||
const policyTiers = tierMatrix[TierFeature.ResourcePolicies];
|
||||
const isDisabled = !isPaidUser(policyTiers);
|
||||
const isInlinePolicy = hidePolicyNameForm && resourceId === undefined;
|
||||
const showPaidAlert = !isInlinePolicy;
|
||||
const isDisabled = showPaidAlert && !isPaidUser(policyTiers);
|
||||
const effectiveReadonly = readonly || isDisabled;
|
||||
|
||||
const authSection = (
|
||||
@@ -100,7 +102,7 @@ export function EditPolicyForm({
|
||||
if (section === "general") {
|
||||
return (
|
||||
<>
|
||||
<PaidFeaturesAlert tiers={policyTiers} />
|
||||
{showPaidAlert && <PaidFeaturesAlert tiers={policyTiers} />}
|
||||
<div
|
||||
className={
|
||||
isDisabled
|
||||
@@ -117,7 +119,7 @@ export function EditPolicyForm({
|
||||
if (section === "authentication") {
|
||||
return (
|
||||
<>
|
||||
<PaidFeaturesAlert tiers={policyTiers} />
|
||||
{showPaidAlert && <PaidFeaturesAlert tiers={policyTiers} />}
|
||||
<div
|
||||
className={
|
||||
isDisabled
|
||||
@@ -134,7 +136,7 @@ export function EditPolicyForm({
|
||||
if (section === "rules") {
|
||||
return (
|
||||
<>
|
||||
<PaidFeaturesAlert tiers={policyTiers} />
|
||||
{showPaidAlert && <PaidFeaturesAlert tiers={policyTiers} />}
|
||||
<div
|
||||
className={
|
||||
isDisabled
|
||||
@@ -150,7 +152,7 @@ export function EditPolicyForm({
|
||||
|
||||
return (
|
||||
<>
|
||||
<PaidFeaturesAlert tiers={policyTiers} />
|
||||
{showPaidAlert && <PaidFeaturesAlert tiers={policyTiers} />}
|
||||
<div
|
||||
className={
|
||||
isDisabled ? "pointer-events-none opacity-50" : undefined
|
||||
|
||||
Reference in New Issue
Block a user