Crud working

This commit is contained in:
Owen
2026-04-11 21:09:12 -07:00
parent fc4633db91
commit 5803da4893
13 changed files with 258 additions and 70 deletions

View File

@@ -1,6 +1,8 @@
import { import {
clients, clients,
clientSiteResources, clientSiteResources,
domains,
orgDomains,
roles, roles,
roleSiteResources, roleSiteResources,
SiteResource, SiteResource,
@@ -11,10 +13,83 @@ import {
userSiteResources userSiteResources
} from "@server/db"; } from "@server/db";
import { sites } from "@server/db"; import { sites } from "@server/db";
import { eq, and, ne, inArray, or } from "drizzle-orm"; import { eq, and, ne, inArray, or, isNotNull } from "drizzle-orm";
import { Config } from "./types"; import { Config } from "./types";
import logger from "@server/logger"; import logger from "@server/logger";
import { getNextAvailableAliasAddress } from "../ip"; import { getNextAvailableAliasAddress } from "../ip";
import { createCertificate } from "#dynamic/routers/certificates/createCertificate";
async function getDomainForSiteResource(
siteResourceId: number | undefined,
fullDomain: string,
orgId: string,
trx: Transaction
): Promise<{ subdomain: string | null; domainId: string }> {
const [fullDomainExists] = await trx
.select({ siteResourceId: siteResources.siteResourceId })
.from(siteResources)
.where(
and(
eq(siteResources.fullDomain, fullDomain),
eq(siteResources.orgId, orgId),
siteResourceId
? ne(siteResources.siteResourceId, siteResourceId)
: isNotNull(siteResources.siteResourceId)
)
)
.limit(1);
if (fullDomainExists) {
throw new Error(
`Site resource already exists with domain: ${fullDomain} in org ${orgId}`
);
}
const possibleDomains = await trx
.select()
.from(domains)
.innerJoin(orgDomains, eq(domains.domainId, orgDomains.domainId))
.where(and(eq(orgDomains.orgId, orgId), eq(domains.verified, true)))
.execute();
if (possibleDomains.length === 0) {
throw new Error(
`Domain not found for full-domain: ${fullDomain} in org ${orgId}`
);
}
const validDomains = possibleDomains.filter((domain) => {
if (domain.domains.type == "ns" || domain.domains.type == "wildcard") {
return (
fullDomain === domain.domains.baseDomain ||
fullDomain.endsWith(`.${domain.domains.baseDomain}`)
);
} else if (domain.domains.type == "cname") {
return fullDomain === domain.domains.baseDomain;
}
});
if (validDomains.length === 0) {
throw new Error(
`Domain not found for full-domain: ${fullDomain} in org ${orgId}`
);
}
const domainSelection = validDomains[0].domains;
const baseDomain = domainSelection.baseDomain;
let subdomain: string | null = null;
if (fullDomain !== baseDomain) {
subdomain = fullDomain.replace(`.${baseDomain}`, "");
}
await createCertificate(domainSelection.domainId, fullDomain, trx);
return {
subdomain,
domainId: domainSelection.domainId
};
}
function siteResourceModeForDb(mode: "host" | "cidr" | "http" | "https"): { function siteResourceModeForDb(mode: "host" | "cidr" | "http" | "https"): {
mode: "host" | "cidr" | "http"; mode: "host" | "cidr" | "http";
@@ -91,6 +166,19 @@ export async function updateClientResources(
if (existingResource) { if (existingResource) {
const mappedMode = siteResourceModeForDb(resourceData.mode); const mappedMode = siteResourceModeForDb(resourceData.mode);
let domainInfo:
| { subdomain: string | null; domainId: string }
| undefined;
if (resourceData["full-domain"] && mappedMode.mode === "http") {
domainInfo = await getDomainForSiteResource(
existingResource.siteResourceId,
resourceData["full-domain"],
orgId,
trx
);
}
// Update existing resource // Update existing resource
const [updatedResource] = await trx const [updatedResource] = await trx
.update(siteResources) .update(siteResources)
@@ -107,7 +195,10 @@ export async function updateClientResources(
alias: resourceData.alias || null, alias: resourceData.alias || null,
disableIcmp: resourceData["disable-icmp"], disableIcmp: resourceData["disable-icmp"],
tcpPortRangeString: resourceData["tcp-ports"], tcpPortRangeString: resourceData["tcp-ports"],
udpPortRangeString: resourceData["udp-ports"] udpPortRangeString: resourceData["udp-ports"],
fullDomain: resourceData["full-domain"] || null,
subdomain: domainInfo ? domainInfo.subdomain : null,
domainId: domainInfo ? domainInfo.domainId : null
}) })
.where( .where(
eq( eq(
@@ -118,7 +209,6 @@ export async function updateClientResources(
.returning(); .returning();
const siteResourceId = existingResource.siteResourceId; const siteResourceId = existingResource.siteResourceId;
const orgId = existingResource.orgId;
await trx await trx
.delete(clientSiteResources) .delete(clientSiteResources)
@@ -231,6 +321,18 @@ export async function updateClientResources(
aliasAddress = await getNextAvailableAliasAddress(orgId); aliasAddress = await getNextAvailableAliasAddress(orgId);
} }
let domainInfo:
| { subdomain: string | null; domainId: string }
| undefined;
if (resourceData["full-domain"] && mappedMode.mode === "http") {
domainInfo = await getDomainForSiteResource(
undefined,
resourceData["full-domain"],
orgId,
trx
);
}
// Create new resource // Create new resource
const [newResource] = await trx const [newResource] = await trx
.insert(siteResources) .insert(siteResources)
@@ -250,7 +352,10 @@ export async function updateClientResources(
aliasAddress: aliasAddress, aliasAddress: aliasAddress,
disableIcmp: resourceData["disable-icmp"], disableIcmp: resourceData["disable-icmp"],
tcpPortRangeString: resourceData["tcp-ports"], tcpPortRangeString: resourceData["tcp-ports"],
udpPortRangeString: resourceData["udp-ports"] udpPortRangeString: resourceData["udp-ports"],
fullDomain: resourceData["full-domain"] || null,
subdomain: domainInfo ? domainInfo.subdomain : null,
domainId: domainInfo ? domainInfo.domainId : null
}) })
.returning(); .returning();

View File

@@ -1100,7 +1100,7 @@ function checkIfTargetChanged(
return false; return false;
} }
async function getDomain( export async function getDomain(
resourceId: number | undefined, resourceId: number | undefined,
fullDomain: string, fullDomain: string,
orgId: string, orgId: string,

View File

@@ -325,7 +325,7 @@ export function isTargetsOnlyResource(resource: any): boolean {
export const ClientResourceSchema = z export const ClientResourceSchema = z
.object({ .object({
name: z.string().min(1).max(255), name: z.string().min(1).max(255),
mode: z.enum(["host", "cidr", "http", "https"]), mode: z.enum(["host", "cidr", "http"]),
site: z.string(), site: z.string(),
// protocol: z.enum(["tcp", "udp"]).optional(), // protocol: z.enum(["tcp", "udp"]).optional(),
// proxyPort: z.int().positive().optional(), // proxyPort: z.int().positive().optional(),
@@ -335,6 +335,8 @@ export const ClientResourceSchema = z
"tcp-ports": portRangeStringSchema.optional().default("*"), "tcp-ports": portRangeStringSchema.optional().default("*"),
"udp-ports": portRangeStringSchema.optional().default("*"), "udp-ports": portRangeStringSchema.optional().default("*"),
"disable-icmp": z.boolean().optional().default(false), "disable-icmp": z.boolean().optional().default(false),
"full-domain": z.string().optional(),
ssl: z.boolean().optional(),
alias: z alias: z
.string() .string()
.regex( .regex(
@@ -477,6 +479,39 @@ export const ConfigSchema = z
}); });
} }
// Enforce the full-domain uniqueness across client-resources in the same stack
const clientFullDomainMap = new Map<string, string[]>();
Object.entries(config["client-resources"]).forEach(
([resourceKey, resource]) => {
const fullDomain = resource["full-domain"];
if (fullDomain) {
if (!clientFullDomainMap.has(fullDomain)) {
clientFullDomainMap.set(fullDomain, []);
}
clientFullDomainMap.get(fullDomain)!.push(resourceKey);
}
}
);
const clientFullDomainDuplicates = Array.from(
clientFullDomainMap.entries()
)
.filter(([_, resourceKeys]) => resourceKeys.length > 1)
.map(
([fullDomain, resourceKeys]) =>
`'${fullDomain}' used by resources: ${resourceKeys.join(", ")}`
)
.join("; ");
if (clientFullDomainDuplicates.length !== 0) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["client-resources"],
message: `Duplicate 'full-domain' values found: ${clientFullDomainDuplicates}`
});
}
// Enforce proxy-port uniqueness within proxy-resources per protocol // Enforce proxy-port uniqueness within proxy-resources per protocol
const protocolPortMap = new Map<string, string[]>(); const protocolPortMap = new Map<string, string[]>();

View File

@@ -478,9 +478,9 @@ export type Alias = { alias: string | null; aliasAddress: string | null };
export function generateAliasConfig(allSiteResources: SiteResource[]): Alias[] { export function generateAliasConfig(allSiteResources: SiteResource[]): Alias[] {
return allSiteResources return allSiteResources
.filter((sr) => sr.alias && sr.aliasAddress && sr.mode == "host") .filter((sr) => sr.aliasAddress && ((sr.alias && sr.mode == "host") || (sr.fullDomain && sr.mode == "http")))
.map((sr) => ({ .map((sr) => ({
alias: sr.alias, alias: sr.alias || sr.fullDomain,
aliasAddress: sr.aliasAddress aliasAddress: sr.aliasAddress
})); }));
} }
@@ -672,7 +672,6 @@ export async function generateSubnetProxyTargetV2(
); );
return; return;
} }
const publicProtocol = siteResource.ssl ? "https" : "http";
// also push a match for the alias address // also push a match for the alias address
let tlsCert: string | undefined; let tlsCert: string | undefined;
let tlsKey: string | undefined; let tlsKey: string | undefined;

View File

@@ -17,7 +17,6 @@ import { getUserDeviceName } from "@server/db/names";
import { buildSiteConfigurationForOlmClient } from "./buildConfiguration"; import { buildSiteConfigurationForOlmClient } from "./buildConfiguration";
import { OlmErrorCodes, sendOlmError } from "./error"; import { OlmErrorCodes, sendOlmError } from "./error";
import { handleFingerprintInsertion } from "./fingerprintingUtils"; import { handleFingerprintInsertion } from "./fingerprintingUtils";
import { Alias } from "@server/lib/ip";
import { build } from "@server/build"; import { build } from "@server/build";
import { canCompress } from "@server/lib/clientVersionChecks"; import { canCompress } from "@server/lib/clientVersionChecks";
import config from "@server/lib/config"; import config from "@server/lib/config";

View File

@@ -66,7 +66,7 @@ const createSiteResourceSchema = z
.strict() .strict()
.refine( .refine(
(data) => { (data) => {
if (data.mode === "host" || data.mode == "http") { if (data.mode === "host") {
if (data.mode == "host") { if (data.mode == "host") {
// Check if it's a valid IP address using zod (v4 or v6) // Check if it's a valid IP address using zod (v4 or v6)
const isValidIP = z const isValidIP = z
@@ -262,7 +262,6 @@ export async function createSiteResource(
let fullDomain: string | null = null; let fullDomain: string | null = null;
let finalSubdomain: string | null = null; let finalSubdomain: string | null = null;
let finalAlias = alias ? alias.trim() : null;
if (domainId && subdomain) { if (domainId && subdomain) {
// Validate domain and construct full domain // Validate domain and construct full domain
const domainResult = await validateAndConstructDomain( const domainResult = await validateAndConstructDomain(
@@ -279,18 +278,32 @@ export async function createSiteResource(
fullDomain = domainResult.fullDomain; fullDomain = domainResult.fullDomain;
finalSubdomain = domainResult.subdomain; finalSubdomain = domainResult.subdomain;
finalAlias = fullDomain; // we will use the full domain as the alias for uniqueness checks and routing
// make sure the full domain is unique
const existingResource = await db
.select()
.from(siteResources)
.where(eq(siteResources.fullDomain, fullDomain));
if (existingResource.length > 0) {
return next(
createHttpError(
HttpCode.CONFLICT,
"Resource with that domain already exists"
)
);
}
} }
// make sure the alias is unique within the org if provided // make sure the alias is unique within the org if provided
if (finalAlias) { if (alias) {
const [conflict] = await db const [conflict] = await db
.select() .select()
.from(siteResources) .from(siteResources)
.where( .where(
and( and(
eq(siteResources.orgId, orgId), eq(siteResources.orgId, orgId),
eq(siteResources.alias, finalAlias.trim()) eq(siteResources.alias, alias.trim())
) )
) )
.limit(1); .limit(1);
@@ -330,13 +343,14 @@ export async function createSiteResource(
scheme, scheme,
destinationPort, destinationPort,
enabled, enabled,
alias: finalAlias, alias: alias ? alias.trim() : null,
aliasAddress, aliasAddress,
tcpPortRangeString, tcpPortRangeString,
udpPortRangeString, udpPortRangeString,
disableIcmp, disableIcmp,
domainId, domainId,
subdomain: finalSubdomain subdomain: finalSubdomain,
fullDomain
}; };
if (isLicensedSshPam) { if (isLicensedSshPam) {
if (authDaemonPort !== undefined) if (authDaemonPort !== undefined)

View File

@@ -101,6 +101,9 @@ function querySiteResourcesBase() {
disableIcmp: siteResources.disableIcmp, disableIcmp: siteResources.disableIcmp,
authDaemonMode: siteResources.authDaemonMode, authDaemonMode: siteResources.authDaemonMode,
authDaemonPort: siteResources.authDaemonPort, authDaemonPort: siteResources.authDaemonPort,
subdomain: siteResources.subdomain,
domainId: siteResources.domainId,
fullDomain: siteResources.fullDomain,
siteName: sites.name, siteName: sites.name,
siteNiceId: sites.niceId, siteNiceId: sites.niceId,
siteAddress: sites.address siteAddress: sites.address

View File

@@ -81,11 +81,9 @@ const updateSiteResourceSchema = z
.refine( .refine(
(data) => { (data) => {
if ( if (
(data.mode === "host" || data.mode === "host" &&
data.mode == "http") &&
data.destination data.destination
) { ) {
if (data.mode == "host") {
const isValidIP = z const isValidIP = z
// .union([z.ipv4(), z.ipv6()]) // .union([z.ipv4(), z.ipv6()])
.union([z.ipv4()]) // for now lets just do ipv4 until we verify ipv6 works everywhere .union([z.ipv4()]) // for now lets just do ipv4 until we verify ipv6 works everywhere
@@ -94,7 +92,6 @@ const updateSiteResourceSchema = z
if (isValidIP) { if (isValidIP) {
return true; return true;
} }
}
// Check if it's a valid domain (hostname pattern, TLD not required) // Check if it's a valid domain (hostname pattern, TLD not required)
const domainRegex = const domainRegex =
@@ -309,7 +306,6 @@ export async function updateSiteResource(
let fullDomain: string | null = null; let fullDomain: string | null = null;
let finalSubdomain: string | null = null; let finalSubdomain: string | null = null;
let finalAlias = alias ? alias.trim() : null;
if (domainId && subdomain) { if (domainId && subdomain) {
// Validate domain and construct full domain // Validate domain and construct full domain
const domainResult = await validateAndConstructDomain( const domainResult = await validateAndConstructDomain(
@@ -326,18 +322,32 @@ export async function updateSiteResource(
fullDomain = domainResult.fullDomain; fullDomain = domainResult.fullDomain;
finalSubdomain = domainResult.subdomain; finalSubdomain = domainResult.subdomain;
finalAlias = fullDomain; // we will use the full domain as the alias for uniqueness checks and routing
// make sure the full domain is unique
const existingResource = await db
.select()
.from(siteResources)
.where(eq(siteResources.fullDomain, fullDomain));
if (existingResource.length > 0) {
return next(
createHttpError(
HttpCode.CONFLICT,
"Resource with that domain already exists"
)
);
}
} }
// make sure the alias is unique within the org if provided // make sure the alias is unique within the org if provided
if (finalAlias) { if (alias) {
const [conflict] = await db const [conflict] = await db
.select() .select()
.from(siteResources) .from(siteResources)
.where( .where(
and( and(
eq(siteResources.orgId, existingSiteResource.orgId), eq(siteResources.orgId, existingSiteResource.orgId),
eq(siteResources.alias, finalAlias.trim()), eq(siteResources.alias, alias.trim()),
ne(siteResources.siteResourceId, siteResourceId) // exclude self ne(siteResources.siteResourceId, siteResourceId) // exclude self
) )
) )
@@ -405,12 +415,13 @@ export async function updateSiteResource(
destination, destination,
destinationPort, destinationPort,
enabled, enabled,
alias: finalAlias, alias: alias ? alias.trim() : null,
tcpPortRangeString, tcpPortRangeString,
udpPortRangeString, udpPortRangeString,
disableIcmp, disableIcmp,
domainId, domainId,
subdomain: finalSubdomain, subdomain: finalSubdomain,
fullDomain,
...sshPamSet ...sshPamSet
}) })
.where( .where(
@@ -507,18 +518,20 @@ export async function updateSiteResource(
.set({ .set({
name: name, name: name,
siteId: siteId, siteId: siteId,
niceId: niceId,
mode: mode, mode: mode,
scheme, scheme,
ssl, ssl,
destination: destination, destination: destination,
destinationPort: destinationPort, destinationPort: destinationPort,
enabled: enabled, enabled: enabled,
alias: finalAlias, alias: alias ? alias.trim() : null,
tcpPortRangeString: tcpPortRangeString, tcpPortRangeString: tcpPortRangeString,
udpPortRangeString: udpPortRangeString, udpPortRangeString: udpPortRangeString,
disableIcmp: disableIcmp, disableIcmp: disableIcmp,
domainId, domainId,
subdomain: finalSubdomain, subdomain: finalSubdomain,
fullDomain,
...sshPamSet ...sshPamSet
}) })
.where( .where(

View File

@@ -88,7 +88,10 @@ export default async function ClientResourcesPage(
udpPortRangeString: siteResource.udpPortRangeString || null, udpPortRangeString: siteResource.udpPortRangeString || null,
disableIcmp: siteResource.disableIcmp || false, disableIcmp: siteResource.disableIcmp || false,
authDaemonMode: siteResource.authDaemonMode ?? null, authDaemonMode: siteResource.authDaemonMode ?? null,
authDaemonPort: siteResource.authDaemonPort ?? null authDaemonPort: siteResource.authDaemonPort ?? null,
subdomain: siteResource.subdomain ?? null,
domainId: siteResource.domainId ?? null,
fullDomain: siteResource.fullDomain ?? null
}; };
} }
); );

View File

@@ -63,6 +63,9 @@ export type InternalResourceRow = {
disableIcmp: boolean; disableIcmp: boolean;
authDaemonMode?: "site" | "remote" | null; authDaemonMode?: "site" | "remote" | null;
authDaemonPort?: number | null; authDaemonPort?: number | null;
subdomain?: string | null;
domainId?: string | null;
fullDomain?: string | null;
}; };
function resolveHttpHttpsDisplayPort( function resolveHttpHttpsDisplayPort(
@@ -313,8 +316,8 @@ export default function ClientResourcesTable({
/> />
); );
} }
if (resourceRow.mode === "http" && resourceRow.alias) { if (resourceRow.mode === "http") {
const url = `${resourceRow.ssl ? "https" : "http"}://${resourceRow.alias}`; const url = `${resourceRow.ssl ? "https" : "http"}://${resourceRow.fullDomain}`;
return ( return (
<CopyToClipboard <CopyToClipboard
text={url} text={url}

View File

@@ -75,24 +75,34 @@ export default function CreateInternalResourceDialog({
...(data.mode === "http" && { ...(data.mode === "http" && {
scheme: data.scheme, scheme: data.scheme,
ssl: data.ssl ?? false, ssl: data.ssl ?? false,
destinationPort: data.httpHttpsPort ?? undefined destinationPort: data.httpHttpsPort ?? undefined,
}), domainId: data.httpConfigDomainId
alias: ? data.httpConfigDomainId
data.alias &&
typeof data.alias === "string" &&
data.alias.trim()
? data.alias
: undefined, : undefined,
tcpPortRangeString: data.tcpPortRangeString, subdomain: data.httpConfigSubdomain
udpPortRangeString: data.udpPortRangeString, ? data.httpConfigSubdomain
disableIcmp: data.disableIcmp ?? false, : undefined
...(data.authDaemonMode != null && {
authDaemonMode: data.authDaemonMode
}), }),
...(data.authDaemonMode === "remote" && ...(data.mode === "host" && {
data.authDaemonPort != null && { alias:
authDaemonPort: data.authDaemonPort data.alias &&
typeof data.alias === "string" &&
data.alias.trim()
? data.alias
: undefined,
...(data.authDaemonMode != null && {
authDaemonMode: data.authDaemonMode
}), }),
...(data.authDaemonMode === "remote" &&
data.authDaemonPort != null && {
authDaemonPort: data.authDaemonPort
})
}),
...((data.mode === "host" || data.mode == "cidr") && {
tcpPortRangeString: data.tcpPortRangeString,
udpPortRangeString: data.udpPortRangeString,
disableIcmp: data.disableIcmp ?? false
}),
roleIds: data.roles roleIds: data.roles
? data.roles.map((r) => parseInt(r.id)) ? data.roles.map((r) => parseInt(r.id))
: [], : [],

View File

@@ -77,22 +77,28 @@ export default function EditInternalResourceDialog({
...(data.mode === "http" && { ...(data.mode === "http" && {
scheme: data.scheme, scheme: data.scheme,
ssl: data.ssl ?? false, ssl: data.ssl ?? false,
destinationPort: data.httpHttpsPort ?? null destinationPort: data.httpHttpsPort ?? null,
domainId: data.httpConfigDomainId ? data.httpConfigDomainId : undefined,
subdomain: data.httpConfigSubdomain ? data.httpConfigSubdomain : undefined
}), }),
alias: ...(data.mode === "host" && {
data.alias && alias:
typeof data.alias === "string" && data.alias &&
data.alias.trim() typeof data.alias === "string" &&
? data.alias data.alias.trim()
: null, ? data.alias
tcpPortRangeString: data.tcpPortRangeString, : null,
udpPortRangeString: data.udpPortRangeString, ...(data.authDaemonMode != null && {
disableIcmp: data.disableIcmp ?? false, authDaemonMode: data.authDaemonMode
...(data.authDaemonMode != null && { }),
authDaemonMode: data.authDaemonMode ...(data.authDaemonMode === "remote" && {
authDaemonPort: data.authDaemonPort || null
})
}), }),
...(data.authDaemonMode === "remote" && { ...((data.mode === "host" || data.mode === "cidr") && {
authDaemonPort: data.authDaemonPort || null tcpPortRangeString: data.tcpPortRangeString,
udpPortRangeString: data.udpPortRangeString,
disableIcmp: data.disableIcmp ?? false
}), }),
roleIds: (data.roles || []).map((r) => parseInt(r.id)), roleIds: (data.roles || []).map((r) => parseInt(r.id)),
userIds: (data.users || []).map((u) => u.id), userIds: (data.users || []).map((u) => u.id),

View File

@@ -146,9 +146,9 @@ export type InternalResourceData = {
httpHttpsPort?: number | null; httpHttpsPort?: number | null;
scheme?: "http" | "https" | null; scheme?: "http" | "https" | null;
ssl?: boolean; ssl?: boolean;
httpConfigSubdomain?: string | null; subdomain?: string | null;
httpConfigDomainId?: string | null; domainId?: string | null;
httpConfigFullDomain?: string | null; fullDomain?: string | null;
}; };
const tagSchema = z.object({ id: z.string(), text: z.string() }); const tagSchema = z.object({ id: z.string(), text: z.string() });
@@ -479,9 +479,9 @@ export function InternalResourceForm({
httpHttpsPort: resource.httpHttpsPort ?? null, httpHttpsPort: resource.httpHttpsPort ?? null,
scheme: resource.scheme ?? "http", scheme: resource.scheme ?? "http",
ssl: resource.ssl ?? false, ssl: resource.ssl ?? false,
httpConfigSubdomain: resource.httpConfigSubdomain ?? null, httpConfigSubdomain: resource.subdomain ?? null,
httpConfigDomainId: resource.httpConfigDomainId ?? null, httpConfigDomainId: resource.domainId ?? null,
httpConfigFullDomain: resource.httpConfigFullDomain ?? null, httpConfigFullDomain: resource.fullDomain ?? null,
niceId: resource.niceId, niceId: resource.niceId,
roles: [], roles: [],
users: [], users: [],
@@ -582,9 +582,9 @@ export function InternalResourceForm({
httpHttpsPort: resource.httpHttpsPort ?? null, httpHttpsPort: resource.httpHttpsPort ?? null,
scheme: resource.scheme ?? "http", scheme: resource.scheme ?? "http",
ssl: resource.ssl ?? false, ssl: resource.ssl ?? false,
httpConfigSubdomain: resource.httpConfigSubdomain ?? null, httpConfigSubdomain: resource.subdomain ?? null,
httpConfigDomainId: resource.httpConfigDomainId ?? null, httpConfigDomainId: resource.domainId ?? null,
httpConfigFullDomain: resource.httpConfigFullDomain ?? null, httpConfigFullDomain: resource.fullDomain ?? null,
tcpPortRangeString: resource.tcpPortRangeString ?? "*", tcpPortRangeString: resource.tcpPortRangeString ?? "*",
udpPortRangeString: resource.udpPortRangeString ?? "*", udpPortRangeString: resource.udpPortRangeString ?? "*",
disableIcmp: resource.disableIcmp ?? false, disableIcmp: resource.disableIcmp ?? false,
@@ -1023,7 +1023,6 @@ export function InternalResourceForm({
"httpConfigFullDomain", "httpConfigFullDomain",
null null
); );
form.setValue("alias", null);
return; return;
} }
form.setValue( form.setValue(
@@ -1038,7 +1037,6 @@ export function InternalResourceForm({
"httpConfigFullDomain", "httpConfigFullDomain",
res.fullDomain res.fullDomain
); );
form.setValue("alias", res.fullDomain);
}} }}
/> />
<FormField <FormField