mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-07 03:06:40 +00:00
hide path match and rewrite in raw resource
This commit is contained in:
@@ -63,7 +63,7 @@ const createTargetSchema = z
|
|||||||
.enum(["exact", "prefix", "regex", "stripPrefix"])
|
.enum(["exact", "prefix", "regex", "stripPrefix"])
|
||||||
.optional()
|
.optional()
|
||||||
.nullable(),
|
.nullable(),
|
||||||
priority: z.number().int().min(1).max(1000)
|
priority: z.number().int().min(1).max(1000).optional().nullable()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
@@ -224,7 +224,7 @@ export async function createTarget(
|
|||||||
pathMatchType: targetData.pathMatchType,
|
pathMatchType: targetData.pathMatchType,
|
||||||
rewritePath: targetData.rewritePath,
|
rewritePath: targetData.rewritePath,
|
||||||
rewritePathType: targetData.rewritePathType,
|
rewritePathType: targetData.rewritePathType,
|
||||||
priority: targetData.priority
|
priority: targetData.priority || 100
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ const addTargetSchema = z
|
|||||||
.enum(["exact", "prefix", "regex", "stripPrefix"])
|
.enum(["exact", "prefix", "regex", "stripPrefix"])
|
||||||
.optional()
|
.optional()
|
||||||
.nullable(),
|
.nullable(),
|
||||||
priority: z.number().int().min(1).max(1000)
|
priority: z.number().int().min(1).max(1000).optional()
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
@@ -429,17 +429,19 @@ export default function ReverseProxyTargets(props: {
|
|||||||
}, [isAdvancedMode]);
|
}, [isAdvancedMode]);
|
||||||
|
|
||||||
function addNewTarget() {
|
function addNewTarget() {
|
||||||
|
const isHttp = resource.http;
|
||||||
|
|
||||||
const newTarget: LocalTarget = {
|
const newTarget: LocalTarget = {
|
||||||
targetId: -Date.now(), // Use negative timestamp as temporary ID
|
targetId: -Date.now(), // Use negative timestamp as temporary ID
|
||||||
ip: "",
|
ip: "",
|
||||||
method: resource.http ? "http" : null,
|
method: isHttp ? "http" : null,
|
||||||
port: 0,
|
port: 0,
|
||||||
siteId: sites.length > 0 ? sites[0].siteId : 0,
|
siteId: sites.length > 0 ? sites[0].siteId : 0,
|
||||||
path: null,
|
path: isHttp ? null : null,
|
||||||
pathMatchType: null,
|
pathMatchType: isHttp ? null : null,
|
||||||
rewritePath: null,
|
rewritePath: isHttp ? null : null,
|
||||||
rewritePathType: null,
|
rewritePathType: isHttp ? null : null,
|
||||||
priority: 100,
|
priority: isHttp ? 100 : 100,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
resourceId: resource.resourceId,
|
resourceId: resource.resourceId,
|
||||||
hcEnabled: false,
|
hcEnabled: false,
|
||||||
@@ -515,25 +517,31 @@ export default function ReverseProxyTargets(props: {
|
|||||||
try {
|
try {
|
||||||
setTargetsLoading(true);
|
setTargetsLoading(true);
|
||||||
|
|
||||||
const response = await api.post<
|
const data: any = {
|
||||||
AxiosResponse<CreateTargetResponse>
|
|
||||||
>(`/target`, {
|
|
||||||
resourceId: resource.resourceId,
|
resourceId: resource.resourceId,
|
||||||
siteId: target.siteId,
|
siteId: target.siteId,
|
||||||
ip: target.ip,
|
ip: target.ip,
|
||||||
method: target.method,
|
method: target.method,
|
||||||
port: target.port,
|
port: target.port,
|
||||||
path: target.path,
|
|
||||||
pathMatchType: target.pathMatchType,
|
|
||||||
rewritePath: target.rewritePath,
|
|
||||||
rewritePathType: target.rewritePathType,
|
|
||||||
priority: target.priority,
|
|
||||||
enabled: target.enabled,
|
enabled: target.enabled,
|
||||||
hcEnabled: target.hcEnabled,
|
hcEnabled: target.hcEnabled,
|
||||||
hcPath: target.hcPath,
|
hcPath: target.hcPath,
|
||||||
hcInterval: target.hcInterval,
|
hcInterval: target.hcInterval,
|
||||||
hcTimeout: target.hcTimeout
|
hcTimeout: target.hcTimeout
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Only include path-related fields for HTTP resources
|
||||||
|
if (resource.http) {
|
||||||
|
data.path = target.path;
|
||||||
|
data.pathMatchType = target.pathMatchType;
|
||||||
|
data.rewritePath = target.rewritePath;
|
||||||
|
data.rewritePathType = target.rewritePathType;
|
||||||
|
data.priority = target.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await api.post<
|
||||||
|
AxiosResponse<CreateTargetResponse>
|
||||||
|
>(`/target`, data);
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
// Update the target with the new ID and remove the new flag
|
// Update the target with the new ID and remove the new flag
|
||||||
@@ -616,19 +624,20 @@ export default function ReverseProxyTargets(props: {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const site = sites.find((site) => site.siteId === data.siteId);
|
const site = sites.find((site) => site.siteId === data.siteId);
|
||||||
|
const isHttp = resource.http;
|
||||||
|
|
||||||
const newTarget: LocalTarget = {
|
const newTarget: LocalTarget = {
|
||||||
...data,
|
...data,
|
||||||
path: data.path || null,
|
path: isHttp ? (data.path || null) : null,
|
||||||
pathMatchType: data.pathMatchType || null,
|
pathMatchType: isHttp ? (data.pathMatchType || null) : null,
|
||||||
rewritePath: data.rewritePath || null,
|
rewritePath: isHttp ? (data.rewritePath || null) : null,
|
||||||
rewritePathType: data.rewritePathType || null,
|
rewritePathType: isHttp ? (data.rewritePathType || null) : null,
|
||||||
siteType: site?.type || null,
|
siteType: site?.type || null,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
targetId: new Date().getTime(),
|
targetId: new Date().getTime(),
|
||||||
new: true,
|
new: true,
|
||||||
resourceId: resource.resourceId,
|
resourceId: resource.resourceId,
|
||||||
priority: 100,
|
priority: isHttp ? (data.priority || 100) : 100,
|
||||||
hcEnabled: false,
|
hcEnabled: false,
|
||||||
hcPath: null,
|
hcPath: null,
|
||||||
hcMethod: null,
|
hcMethod: null,
|
||||||
@@ -720,7 +729,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
// Save targets
|
// Save targets
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
const data = {
|
const data: any = {
|
||||||
ip: target.ip,
|
ip: target.ip,
|
||||||
port: target.port,
|
port: target.port,
|
||||||
method: target.method,
|
method: target.method,
|
||||||
@@ -736,14 +745,18 @@ export default function ReverseProxyTargets(props: {
|
|||||||
hcHeaders: target.hcHeaders || null,
|
hcHeaders: target.hcHeaders || null,
|
||||||
hcFollowRedirects: target.hcFollowRedirects || null,
|
hcFollowRedirects: target.hcFollowRedirects || null,
|
||||||
hcMethod: target.hcMethod || null,
|
hcMethod: target.hcMethod || null,
|
||||||
hcStatus: target.hcStatus || null,
|
hcStatus: target.hcStatus || null
|
||||||
path: target.path,
|
|
||||||
pathMatchType: target.pathMatchType,
|
|
||||||
rewritePath: target.rewritePath,
|
|
||||||
rewritePathType: target.rewritePathType,
|
|
||||||
priority: target.priority
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only include path-related fields for HTTP resources
|
||||||
|
if (resource.http) {
|
||||||
|
data.path = target.path;
|
||||||
|
data.pathMatchType = target.pathMatchType;
|
||||||
|
data.rewritePath = target.rewritePath;
|
||||||
|
data.rewritePathType = target.rewritePathType;
|
||||||
|
data.priority = target.priority;
|
||||||
|
}
|
||||||
|
|
||||||
if (target.new) {
|
if (target.new) {
|
||||||
const res = await api.put<
|
const res = await api.put<
|
||||||
AxiosResponse<CreateTargetResponse>
|
AxiosResponse<CreateTargetResponse>
|
||||||
@@ -815,6 +828,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
const getColumns = (): ColumnDef<LocalTarget>[] => {
|
const getColumns = (): ColumnDef<LocalTarget>[] => {
|
||||||
const baseColumns: ColumnDef<LocalTarget>[] = [];
|
const baseColumns: ColumnDef<LocalTarget>[] = [];
|
||||||
|
const isHttp = resource.http;
|
||||||
|
|
||||||
const priorityColumn: ColumnDef<LocalTarget> = {
|
const priorityColumn: ColumnDef<LocalTarget> = {
|
||||||
id: "priority",
|
id: "priority",
|
||||||
@@ -1047,7 +1061,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
role="combobox"
|
role="combobox"
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[180px] justify-between text-sm font-medium border-r pr-4 rounded-none h-8 hover:bg-transparent",
|
"w-[180px] justify-between text-sm border-r pr-4 rounded-none h-8 hover:bg-transparent",
|
||||||
!row.original.siteId &&
|
!row.original.siteId &&
|
||||||
"text-muted-foreground"
|
"text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
@@ -1129,7 +1143,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
<Input
|
<Input
|
||||||
defaultValue={row.original.ip}
|
defaultValue={row.original.ip}
|
||||||
placeholder="IP / Hostname"
|
placeholder="IP / Hostname"
|
||||||
className="flex-1 min-w-[120px] border-none placeholder-gray-400"
|
className="flex-1 min-w-[120px] pl-0 border-none placeholder-gray-400"
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
const input = e.target.value.trim();
|
const input = e.target.value.trim();
|
||||||
const hasProtocol =
|
const hasProtocol =
|
||||||
@@ -1313,15 +1327,20 @@ export default function ReverseProxyTargets(props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (isAdvancedMode) {
|
if (isAdvancedMode) {
|
||||||
return [
|
const columns = [
|
||||||
matchPathColumn,
|
|
||||||
addressColumn,
|
addressColumn,
|
||||||
rewritePathColumn,
|
|
||||||
priorityColumn,
|
|
||||||
healthCheckColumn,
|
healthCheckColumn,
|
||||||
enabledColumn,
|
enabledColumn,
|
||||||
actionsColumn
|
actionsColumn
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Only include path-related columns for HTTP resources
|
||||||
|
if (isHttp) {
|
||||||
|
columns.unshift(matchPathColumn);
|
||||||
|
columns.splice(3, 0, rewritePathColumn, priorityColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
addressColumn,
|
addressColumn,
|
||||||
@@ -1450,7 +1469,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="advanced-mode-toggle"
|
htmlFor="advanced-mode-toggle"
|
||||||
className="text-sm font-medium"
|
className="text-sm"
|
||||||
>
|
>
|
||||||
{t("advancedMode")}
|
{t("advancedMode")}
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const addTargetSchema = z
|
|||||||
.enum(["exact", "prefix", "regex", "stripPrefix"])
|
.enum(["exact", "prefix", "regex", "stripPrefix"])
|
||||||
.optional()
|
.optional()
|
||||||
.nullable(),
|
.nullable(),
|
||||||
priority: z.number().int().min(1).max(1000)
|
priority: z.number().int().min(1).max(1000).optional()
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
@@ -268,17 +268,19 @@ export default function Page() {
|
|||||||
}, [isAdvancedMode]);
|
}, [isAdvancedMode]);
|
||||||
|
|
||||||
function addNewTarget() {
|
function addNewTarget() {
|
||||||
|
const isHttp = baseForm.watch("http");
|
||||||
|
|
||||||
const newTarget: LocalTarget = {
|
const newTarget: LocalTarget = {
|
||||||
targetId: -Date.now(), // Use negative timestamp as temporary ID
|
targetId: -Date.now(), // Use negative timestamp as temporary ID
|
||||||
ip: "",
|
ip: "",
|
||||||
method: baseForm.watch("http") ? "http" : null,
|
method: isHttp ? "http" : null,
|
||||||
port: 0,
|
port: 0,
|
||||||
siteId: sites.length > 0 ? sites[0].siteId : 0,
|
siteId: sites.length > 0 ? sites[0].siteId : 0,
|
||||||
path: null,
|
path: isHttp ? null : null,
|
||||||
pathMatchType: null,
|
pathMatchType: isHttp ? null : null,
|
||||||
rewritePath: null,
|
rewritePath: isHttp ? null : null,
|
||||||
rewritePathType: null,
|
rewritePathType: isHttp ? null : null,
|
||||||
priority: 100,
|
priority: isHttp ? 100 : 100,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
resourceId: 0,
|
resourceId: 0,
|
||||||
hcEnabled: false,
|
hcEnabled: false,
|
||||||
@@ -352,7 +354,7 @@ export default function Page() {
|
|||||||
pathMatchType: null,
|
pathMatchType: null,
|
||||||
rewritePath: null,
|
rewritePath: null,
|
||||||
rewritePathType: null,
|
rewritePathType: null,
|
||||||
priority: 100
|
priority: baseForm.watch("http") ? 100 : undefined
|
||||||
} as z.infer<typeof addTargetSchema>
|
} as z.infer<typeof addTargetSchema>
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -362,7 +364,8 @@ export default function Page() {
|
|||||||
|
|
||||||
return targets.every((target) => {
|
return targets.every((target) => {
|
||||||
try {
|
try {
|
||||||
addTargetSchema.parse({
|
const isHttp = baseForm.watch("http");
|
||||||
|
const targetData: any = {
|
||||||
ip: target.ip,
|
ip: target.ip,
|
||||||
method: target.method,
|
method: target.method,
|
||||||
port: target.port,
|
port: target.port,
|
||||||
@@ -370,9 +373,15 @@ export default function Page() {
|
|||||||
path: target.path,
|
path: target.path,
|
||||||
pathMatchType: target.pathMatchType,
|
pathMatchType: target.pathMatchType,
|
||||||
rewritePath: target.rewritePath,
|
rewritePath: target.rewritePath,
|
||||||
rewritePathType: target.rewritePathType,
|
rewritePathType: target.rewritePathType
|
||||||
priority: target.priority
|
};
|
||||||
});
|
|
||||||
|
// Only include priority for HTTP resources
|
||||||
|
if (isHttp) {
|
||||||
|
targetData.priority = target.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
addTargetSchema.parse(targetData);
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
@@ -436,18 +445,20 @@ export default function Page() {
|
|||||||
|
|
||||||
const site = sites.find((site) => site.siteId === data.siteId);
|
const site = sites.find((site) => site.siteId === data.siteId);
|
||||||
|
|
||||||
|
const isHttp = baseForm.watch("http");
|
||||||
|
|
||||||
const newTarget: LocalTarget = {
|
const newTarget: LocalTarget = {
|
||||||
...data,
|
...data,
|
||||||
path: data.path || null,
|
path: isHttp ? (data.path || null) : null,
|
||||||
pathMatchType: data.pathMatchType || null,
|
pathMatchType: isHttp ? (data.pathMatchType || null) : null,
|
||||||
rewritePath: data.rewritePath || null,
|
rewritePath: isHttp ? (data.rewritePath || null) : null,
|
||||||
rewritePathType: data.rewritePathType || null,
|
rewritePathType: isHttp ? (data.rewritePathType || null) : null,
|
||||||
siteType: site?.type || null,
|
siteType: site?.type || null,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
targetId: new Date().getTime(),
|
targetId: new Date().getTime(),
|
||||||
new: true,
|
new: true,
|
||||||
resourceId: 0, // Will be set when resource is created
|
resourceId: 0, // Will be set when resource is created
|
||||||
priority: 100, // Default priority
|
priority: isHttp ? (data.priority || 100) : 100, // Default priority
|
||||||
hcEnabled: false,
|
hcEnabled: false,
|
||||||
hcPath: null,
|
hcPath: null,
|
||||||
hcMethod: null,
|
hcMethod: null,
|
||||||
@@ -473,7 +484,7 @@ export default function Page() {
|
|||||||
pathMatchType: null,
|
pathMatchType: null,
|
||||||
rewritePath: null,
|
rewritePath: null,
|
||||||
rewritePathType: null,
|
rewritePathType: null,
|
||||||
priority: 100
|
priority: isHttp ? 100 : undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,7 +575,7 @@ export default function Page() {
|
|||||||
if (targets.length > 0) {
|
if (targets.length > 0) {
|
||||||
try {
|
try {
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
const data = {
|
const data: any = {
|
||||||
ip: target.ip,
|
ip: target.ip,
|
||||||
port: target.port,
|
port: target.port,
|
||||||
method: target.method,
|
method: target.method,
|
||||||
@@ -581,14 +592,18 @@ export default function Page() {
|
|||||||
hcPort: target.hcPort || null,
|
hcPort: target.hcPort || null,
|
||||||
hcFollowRedirects:
|
hcFollowRedirects:
|
||||||
target.hcFollowRedirects || null,
|
target.hcFollowRedirects || null,
|
||||||
hcStatus: target.hcStatus || null,
|
hcStatus: target.hcStatus || null
|
||||||
path: target.path,
|
|
||||||
pathMatchType: target.pathMatchType,
|
|
||||||
rewritePath: target.rewritePath,
|
|
||||||
rewritePathType: target.rewritePathType,
|
|
||||||
priority: target.priority
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only include path-related fields for HTTP resources
|
||||||
|
if (isHttp) {
|
||||||
|
data.path = target.path;
|
||||||
|
data.pathMatchType = target.pathMatchType;
|
||||||
|
data.rewritePath = target.rewritePath;
|
||||||
|
data.rewritePathType = target.rewritePathType;
|
||||||
|
data.priority = target.priority;
|
||||||
|
}
|
||||||
|
|
||||||
await api.put(`/resource/${id}/target`, data);
|
await api.put(`/resource/${id}/target`, data);
|
||||||
}
|
}
|
||||||
} catch (targetError) {
|
} catch (targetError) {
|
||||||
@@ -730,6 +745,7 @@ export default function Page() {
|
|||||||
|
|
||||||
const getColumns = (): ColumnDef<LocalTarget>[] => {
|
const getColumns = (): ColumnDef<LocalTarget>[] => {
|
||||||
const baseColumns: ColumnDef<LocalTarget>[] = [];
|
const baseColumns: ColumnDef<LocalTarget>[] = [];
|
||||||
|
const isHttp = baseForm.watch("http");
|
||||||
|
|
||||||
const priorityColumn: ColumnDef<LocalTarget> = {
|
const priorityColumn: ColumnDef<LocalTarget> = {
|
||||||
id: "priority",
|
id: "priority",
|
||||||
@@ -962,7 +978,7 @@ export default function Page() {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
role="combobox"
|
role="combobox"
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[180px] justify-between text-sm font-medium border-r pr-4 rounded-none h-8 hover:bg-transparent",
|
"w-[180px] justify-between text-sm border-r pr-4 rounded-none h-8 hover:bg-transparent",
|
||||||
!row.original.siteId &&
|
!row.original.siteId &&
|
||||||
"text-muted-foreground"
|
"text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
@@ -1027,7 +1043,7 @@ export default function Page() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="h-8 px-2 w-[70px] text-sm font-normal border-none bg-transparent shadow-none focus:ring-0 focus:outline-none focus-visible:ring-0 data-[state=open]:bg-transparent">
|
<SelectTrigger className="h-8 px-2 w-[70px] border-none bg-transparent shadow-none focus:ring-0 focus:outline-none focus-visible:ring-0 data-[state=open]:bg-transparent">
|
||||||
{row.original.method || "http"}
|
{row.original.method || "http"}
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -1044,7 +1060,7 @@ export default function Page() {
|
|||||||
<Input
|
<Input
|
||||||
defaultValue={row.original.ip}
|
defaultValue={row.original.ip}
|
||||||
placeholder="IP / Hostname"
|
placeholder="IP / Hostname"
|
||||||
className="flex-1 min-w-[120px] border-none placeholder-gray-400"
|
className="flex-1 min-w-[120px] pl-0 border-none placeholder-gray-400"
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
const input = e.target.value.trim();
|
const input = e.target.value.trim();
|
||||||
const hasProtocol =
|
const hasProtocol =
|
||||||
@@ -1228,15 +1244,20 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (isAdvancedMode) {
|
if (isAdvancedMode) {
|
||||||
return [
|
const columns = [
|
||||||
matchPathColumn,
|
|
||||||
addressColumn,
|
addressColumn,
|
||||||
rewritePathColumn,
|
|
||||||
priorityColumn,
|
|
||||||
healthCheckColumn,
|
healthCheckColumn,
|
||||||
enabledColumn,
|
enabledColumn,
|
||||||
actionsColumn
|
actionsColumn
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Only include path-related columns for HTTP resources
|
||||||
|
if (isHttp) {
|
||||||
|
columns.unshift(matchPathColumn);
|
||||||
|
columns.splice(3, 0, rewritePathColumn, priorityColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
addressColumn,
|
addressColumn,
|
||||||
@@ -1681,7 +1702,7 @@ export default function Page() {
|
|||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="advanced-mode-toggle"
|
htmlFor="advanced-mode-toggle"
|
||||||
className="text-sm font-medium"
|
className="text-sm"
|
||||||
>
|
>
|
||||||
{t("advancedMode")}
|
{t("advancedMode")}
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user