mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 22:00:51 +00:00
Format
This commit is contained in:
@@ -68,7 +68,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS36351",
|
code: "AS36351",
|
||||||
asn: 36351
|
asn: 36351
|
||||||
},
|
},
|
||||||
|
|
||||||
// CDNs
|
// CDNs
|
||||||
{
|
{
|
||||||
name: "Cloudflare",
|
name: "Cloudflare",
|
||||||
@@ -90,7 +90,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS16625",
|
code: "AS16625",
|
||||||
asn: 16625
|
asn: 16625
|
||||||
},
|
},
|
||||||
|
|
||||||
// Mobile Carriers - US
|
// Mobile Carriers - US
|
||||||
{
|
{
|
||||||
name: "T-Mobile USA",
|
name: "T-Mobile USA",
|
||||||
@@ -117,7 +117,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS6430",
|
code: "AS6430",
|
||||||
asn: 6430
|
asn: 6430
|
||||||
},
|
},
|
||||||
|
|
||||||
// Mobile Carriers - Europe
|
// Mobile Carriers - Europe
|
||||||
{
|
{
|
||||||
name: "Vodafone UK",
|
name: "Vodafone UK",
|
||||||
@@ -144,7 +144,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS12430",
|
code: "AS12430",
|
||||||
asn: 12430
|
asn: 12430
|
||||||
},
|
},
|
||||||
|
|
||||||
// Mobile Carriers - Asia
|
// Mobile Carriers - Asia
|
||||||
{
|
{
|
||||||
name: "NTT DoCoMo (Japan)",
|
name: "NTT DoCoMo (Japan)",
|
||||||
@@ -176,7 +176,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS9808",
|
code: "AS9808",
|
||||||
asn: 9808
|
asn: 9808
|
||||||
},
|
},
|
||||||
|
|
||||||
// Major US ISPs
|
// Major US ISPs
|
||||||
{
|
{
|
||||||
name: "AT&T Services",
|
name: "AT&T Services",
|
||||||
@@ -208,7 +208,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS209",
|
code: "AS209",
|
||||||
asn: 209
|
asn: 209
|
||||||
},
|
},
|
||||||
|
|
||||||
// Major European ISPs
|
// Major European ISPs
|
||||||
{
|
{
|
||||||
name: "Deutsche Telekom",
|
name: "Deutsche Telekom",
|
||||||
@@ -235,7 +235,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS12956",
|
code: "AS12956",
|
||||||
asn: 12956
|
asn: 12956
|
||||||
},
|
},
|
||||||
|
|
||||||
// Major Asian ISPs
|
// Major Asian ISPs
|
||||||
{
|
{
|
||||||
name: "China Telecom",
|
name: "China Telecom",
|
||||||
@@ -262,7 +262,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS55836",
|
code: "AS55836",
|
||||||
asn: 55836
|
asn: 55836
|
||||||
},
|
},
|
||||||
|
|
||||||
// VPN/Proxy Providers
|
// VPN/Proxy Providers
|
||||||
{
|
{
|
||||||
name: "Private Internet Access",
|
name: "Private Internet Access",
|
||||||
@@ -279,7 +279,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS213281",
|
code: "AS213281",
|
||||||
asn: 213281
|
asn: 213281
|
||||||
},
|
},
|
||||||
|
|
||||||
// Social Media / Major Tech
|
// Social Media / Major Tech
|
||||||
{
|
{
|
||||||
name: "Facebook/Meta",
|
name: "Facebook/Meta",
|
||||||
@@ -301,7 +301,7 @@ export const MAJOR_ASNS = [
|
|||||||
code: "AS2906",
|
code: "AS2906",
|
||||||
asn: 2906
|
asn: 2906
|
||||||
},
|
},
|
||||||
|
|
||||||
// Academic/Research
|
// Academic/Research
|
||||||
{
|
{
|
||||||
name: "MIT",
|
name: "MIT",
|
||||||
|
|||||||
@@ -134,13 +134,15 @@ export const resources = pgTable("resources", {
|
|||||||
proxyProtocol: boolean("proxyProtocol").notNull().default(false),
|
proxyProtocol: boolean("proxyProtocol").notNull().default(false),
|
||||||
proxyProtocolVersion: integer("proxyProtocolVersion").default(1),
|
proxyProtocolVersion: integer("proxyProtocolVersion").default(1),
|
||||||
|
|
||||||
maintenanceModeEnabled: boolean("maintenanceModeEnabled").notNull().default(false),
|
maintenanceModeEnabled: boolean("maintenanceModeEnabled")
|
||||||
|
.notNull()
|
||||||
|
.default(false),
|
||||||
maintenanceModeType: text("maintenanceModeType", {
|
maintenanceModeType: text("maintenanceModeType", {
|
||||||
enum: ["forced", "automatic"]
|
enum: ["forced", "automatic"]
|
||||||
}).default("forced"), // "forced" = always show, "automatic" = only when down
|
}).default("forced"), // "forced" = always show, "automatic" = only when down
|
||||||
maintenanceTitle: text("maintenanceTitle"),
|
maintenanceTitle: text("maintenanceTitle"),
|
||||||
maintenanceMessage: text("maintenanceMessage"),
|
maintenanceMessage: text("maintenanceMessage"),
|
||||||
maintenanceEstimatedTime: text("maintenanceEstimatedTime"),
|
maintenanceEstimatedTime: text("maintenanceEstimatedTime")
|
||||||
});
|
});
|
||||||
|
|
||||||
export const targets = pgTable("targets", {
|
export const targets = pgTable("targets", {
|
||||||
@@ -464,13 +466,22 @@ export const resourceHeaderAuth = pgTable("resourceHeaderAuth", {
|
|||||||
headerAuthHash: varchar("headerAuthHash").notNull()
|
headerAuthHash: varchar("headerAuthHash").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resourceHeaderAuthExtendedCompatibility = pgTable("resourceHeaderAuthExtendedCompatibility", {
|
export const resourceHeaderAuthExtendedCompatibility = pgTable(
|
||||||
headerAuthExtendedCompatibilityId: serial("headerAuthExtendedCompatibilityId").primaryKey(),
|
"resourceHeaderAuthExtendedCompatibility",
|
||||||
resourceId: integer("resourceId")
|
{
|
||||||
.notNull()
|
headerAuthExtendedCompatibilityId: serial(
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
"headerAuthExtendedCompatibilityId"
|
||||||
extendedCompatibilityIsActivated: boolean("extendedCompatibilityIsActivated").notNull().default(true),
|
).primaryKey(),
|
||||||
});
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
extendedCompatibilityIsActivated: boolean(
|
||||||
|
"extendedCompatibilityIsActivated"
|
||||||
|
)
|
||||||
|
.notNull()
|
||||||
|
.default(true)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const resourceAccessToken = pgTable("resourceAccessToken", {
|
export const resourceAccessToken = pgTable("resourceAccessToken", {
|
||||||
accessTokenId: varchar("accessTokenId").primaryKey(),
|
accessTokenId: varchar("accessTokenId").primaryKey(),
|
||||||
@@ -872,7 +883,9 @@ export type ResourceSession = InferSelectModel<typeof resourceSessions>;
|
|||||||
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
||||||
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
||||||
export type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
|
export type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
|
||||||
export type ResourceHeaderAuthExtendedCompatibility = InferSelectModel<typeof resourceHeaderAuthExtendedCompatibility>;
|
export type ResourceHeaderAuthExtendedCompatibility = InferSelectModel<
|
||||||
|
typeof resourceHeaderAuthExtendedCompatibility
|
||||||
|
>;
|
||||||
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
||||||
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
||||||
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import {
|
import { db, loginPage, LoginPage, loginPageOrg, Org, orgs } from "@server/db";
|
||||||
db, loginPage, LoginPage, loginPageOrg, Org, orgs,
|
|
||||||
} from "@server/db";
|
|
||||||
import {
|
import {
|
||||||
Resource,
|
Resource,
|
||||||
ResourcePassword,
|
ResourcePassword,
|
||||||
@@ -27,7 +25,7 @@ export type ResourceWithAuth = {
|
|||||||
pincode: ResourcePincode | null;
|
pincode: ResourcePincode | null;
|
||||||
password: ResourcePassword | null;
|
password: ResourcePassword | null;
|
||||||
headerAuth: ResourceHeaderAuth | null;
|
headerAuth: ResourceHeaderAuth | null;
|
||||||
headerAuthExtendedCompatibility: ResourceHeaderAuthExtendedCompatibility | null
|
headerAuthExtendedCompatibility: ResourceHeaderAuthExtendedCompatibility | null;
|
||||||
org: Org;
|
org: Org;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,12 +57,12 @@ export async function getResourceByDomain(
|
|||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuthExtendedCompatibility,
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
eq(resourceHeaderAuthExtendedCompatibility.resourceId, resources.resourceId)
|
eq(
|
||||||
)
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
.innerJoin(
|
resources.resourceId
|
||||||
orgs,
|
)
|
||||||
eq(orgs.orgId, resources.orgId)
|
|
||||||
)
|
)
|
||||||
|
.innerJoin(orgs, eq(orgs.orgId, resources.orgId))
|
||||||
.where(eq(resources.fullDomain, domain))
|
.where(eq(resources.fullDomain, domain))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
@@ -77,7 +75,8 @@ export async function getResourceByDomain(
|
|||||||
pincode: result.resourcePincode,
|
pincode: result.resourcePincode,
|
||||||
password: result.resourcePassword,
|
password: result.resourcePassword,
|
||||||
headerAuth: result.resourceHeaderAuth,
|
headerAuth: result.resourceHeaderAuth,
|
||||||
headerAuthExtendedCompatibility: result.resourceHeaderAuthExtendedCompatibility,
|
headerAuthExtendedCompatibility:
|
||||||
|
result.resourceHeaderAuthExtendedCompatibility,
|
||||||
org: result.orgs
|
org: result.orgs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,22 +12,22 @@ import { no } from "zod/v4/locales";
|
|||||||
export const domains = sqliteTable("domains", {
|
export const domains = sqliteTable("domains", {
|
||||||
domainId: text("domainId").primaryKey(),
|
domainId: text("domainId").primaryKey(),
|
||||||
baseDomain: text("baseDomain").notNull(),
|
baseDomain: text("baseDomain").notNull(),
|
||||||
configManaged: integer("configManaged", {mode: "boolean"})
|
configManaged: integer("configManaged", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
type: text("type"), // "ns", "cname", "wildcard"
|
type: text("type"), // "ns", "cname", "wildcard"
|
||||||
verified: integer("verified", {mode: "boolean"}).notNull().default(false),
|
verified: integer("verified", { mode: "boolean" }).notNull().default(false),
|
||||||
failed: integer("failed", {mode: "boolean"}).notNull().default(false),
|
failed: integer("failed", { mode: "boolean" }).notNull().default(false),
|
||||||
tries: integer("tries").notNull().default(0),
|
tries: integer("tries").notNull().default(0),
|
||||||
certResolver: text("certResolver"),
|
certResolver: text("certResolver"),
|
||||||
preferWildcardCert: integer("preferWildcardCert", {mode: "boolean"})
|
preferWildcardCert: integer("preferWildcardCert", { mode: "boolean" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const dnsRecords = sqliteTable("dnsRecords", {
|
export const dnsRecords = sqliteTable("dnsRecords", {
|
||||||
id: integer("id").primaryKey({autoIncrement: true}),
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
domainId: text("domainId")
|
domainId: text("domainId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => domains.domainId, {onDelete: "cascade"}),
|
.references(() => domains.domainId, { onDelete: "cascade" }),
|
||||||
|
|
||||||
recordType: text("recordType").notNull(), // "NS" | "CNAME" | "A" | "TXT"
|
recordType: text("recordType").notNull(), // "NS" | "CNAME" | "A" | "TXT"
|
||||||
baseDomain: text("baseDomain"),
|
baseDomain: text("baseDomain"),
|
||||||
@@ -41,7 +41,7 @@ export const orgs = sqliteTable("orgs", {
|
|||||||
subnet: text("subnet"),
|
subnet: text("subnet"),
|
||||||
utilitySubnet: text("utilitySubnet"), // this is the subnet for utility addresses
|
utilitySubnet: text("utilitySubnet"), // this is the subnet for utility addresses
|
||||||
createdAt: text("createdAt"),
|
createdAt: text("createdAt"),
|
||||||
requireTwoFactor: integer("requireTwoFactor", {mode: "boolean"}),
|
requireTwoFactor: integer("requireTwoFactor", { mode: "boolean" }),
|
||||||
maxSessionLengthHours: integer("maxSessionLengthHours"), // hours
|
maxSessionLengthHours: integer("maxSessionLengthHours"), // hours
|
||||||
passwordExpiryDays: integer("passwordExpiryDays"), // days
|
passwordExpiryDays: integer("passwordExpiryDays"), // days
|
||||||
settingsLogRetentionDaysRequest: integer("settingsLogRetentionDaysRequest") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
settingsLogRetentionDaysRequest: integer("settingsLogRetentionDaysRequest") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
||||||
@@ -58,23 +58,23 @@ export const orgs = sqliteTable("orgs", {
|
|||||||
export const userDomains = sqliteTable("userDomains", {
|
export const userDomains = sqliteTable("userDomains", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
domainId: text("domainId")
|
domainId: text("domainId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => domains.domainId, {onDelete: "cascade"})
|
.references(() => domains.domainId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orgDomains = sqliteTable("orgDomains", {
|
export const orgDomains = sqliteTable("orgDomains", {
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
domainId: text("domainId")
|
domainId: text("domainId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => domains.domainId, {onDelete: "cascade"})
|
.references(() => domains.domainId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sites = sqliteTable("sites", {
|
export const sites = sqliteTable("sites", {
|
||||||
siteId: integer("siteId").primaryKey({autoIncrement: true}),
|
siteId: integer("siteId").primaryKey({ autoIncrement: true }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -91,7 +91,7 @@ export const sites = sqliteTable("sites", {
|
|||||||
megabytesOut: integer("bytesOut").default(0),
|
megabytesOut: integer("bytesOut").default(0),
|
||||||
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
||||||
type: text("type").notNull(), // "newt" or "wireguard"
|
type: text("type").notNull(), // "newt" or "wireguard"
|
||||||
online: integer("online", {mode: "boolean"}).notNull().default(false),
|
online: integer("online", { mode: "boolean" }).notNull().default(false),
|
||||||
|
|
||||||
// exit node stuff that is how to connect to the site when it has a wg server
|
// exit node stuff that is how to connect to the site when it has a wg server
|
||||||
address: text("address"), // this is the address of the wireguard interface in newt
|
address: text("address"), // this is the address of the wireguard interface in newt
|
||||||
@@ -99,14 +99,14 @@ export const sites = sqliteTable("sites", {
|
|||||||
publicKey: text("publicKey"), // TODO: Fix typo in publicKey
|
publicKey: text("publicKey"), // TODO: Fix typo in publicKey
|
||||||
lastHolePunch: integer("lastHolePunch"),
|
lastHolePunch: integer("lastHolePunch"),
|
||||||
listenPort: integer("listenPort"),
|
listenPort: integer("listenPort"),
|
||||||
dockerSocketEnabled: integer("dockerSocketEnabled", {mode: "boolean"})
|
dockerSocketEnabled: integer("dockerSocketEnabled", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(true)
|
.default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resources = sqliteTable("resources", {
|
export const resources = sqliteTable("resources", {
|
||||||
resourceId: integer("resourceId").primaryKey({autoIncrement: true}),
|
resourceId: integer("resourceId").primaryKey({ autoIncrement: true }),
|
||||||
resourceGuid: text("resourceGuid", {length: 36})
|
resourceGuid: text("resourceGuid", { length: 36 })
|
||||||
.unique()
|
.unique()
|
||||||
.notNull()
|
.notNull()
|
||||||
.$defaultFn(() => randomUUID()),
|
.$defaultFn(() => randomUUID()),
|
||||||
@@ -122,35 +122,39 @@ export const resources = sqliteTable("resources", {
|
|||||||
domainId: text("domainId").references(() => domains.domainId, {
|
domainId: text("domainId").references(() => domains.domainId, {
|
||||||
onDelete: "set null"
|
onDelete: "set null"
|
||||||
}),
|
}),
|
||||||
ssl: integer("ssl", {mode: "boolean"}).notNull().default(false),
|
ssl: integer("ssl", { mode: "boolean" }).notNull().default(false),
|
||||||
blockAccess: integer("blockAccess", {mode: "boolean"})
|
blockAccess: integer("blockAccess", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
sso: integer("sso", {mode: "boolean"}).notNull().default(true),
|
sso: integer("sso", { mode: "boolean" }).notNull().default(true),
|
||||||
http: integer("http", {mode: "boolean"}).notNull().default(true),
|
http: integer("http", { mode: "boolean" }).notNull().default(true),
|
||||||
protocol: text("protocol").notNull(),
|
protocol: text("protocol").notNull(),
|
||||||
proxyPort: integer("proxyPort"),
|
proxyPort: integer("proxyPort"),
|
||||||
emailWhitelistEnabled: integer("emailWhitelistEnabled", {mode: "boolean"})
|
emailWhitelistEnabled: integer("emailWhitelistEnabled", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
applyRules: integer("applyRules", {mode: "boolean"})
|
applyRules: integer("applyRules", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
enabled: integer("enabled", {mode: "boolean"}).notNull().default(true),
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
||||||
stickySession: integer("stickySession", {mode: "boolean"})
|
stickySession: integer("stickySession", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
tlsServerName: text("tlsServerName"),
|
tlsServerName: text("tlsServerName"),
|
||||||
setHostHeader: text("setHostHeader"),
|
setHostHeader: text("setHostHeader"),
|
||||||
enableProxy: integer("enableProxy", {mode: "boolean"}).default(true),
|
enableProxy: integer("enableProxy", { mode: "boolean" }).default(true),
|
||||||
skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, {
|
skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, {
|
||||||
onDelete: "set null"
|
onDelete: "set null"
|
||||||
}),
|
}),
|
||||||
headers: text("headers"), // comma-separated list of headers to add to the request
|
headers: text("headers"), // comma-separated list of headers to add to the request
|
||||||
proxyProtocol: integer("proxyProtocol", { mode: "boolean" }).notNull().default(false),
|
proxyProtocol: integer("proxyProtocol", { mode: "boolean" })
|
||||||
|
.notNull()
|
||||||
|
.default(false),
|
||||||
proxyProtocolVersion: integer("proxyProtocolVersion").default(1),
|
proxyProtocolVersion: integer("proxyProtocolVersion").default(1),
|
||||||
|
|
||||||
maintenanceModeEnabled: integer("maintenanceModeEnabled", { mode: "boolean" })
|
maintenanceModeEnabled: integer("maintenanceModeEnabled", {
|
||||||
|
mode: "boolean"
|
||||||
|
})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
maintenanceModeType: text("maintenanceModeType", {
|
maintenanceModeType: text("maintenanceModeType", {
|
||||||
@@ -158,12 +162,11 @@ export const resources = sqliteTable("resources", {
|
|||||||
}).default("forced"), // "forced" = always show, "automatic" = only when down
|
}).default("forced"), // "forced" = always show, "automatic" = only when down
|
||||||
maintenanceTitle: text("maintenanceTitle"),
|
maintenanceTitle: text("maintenanceTitle"),
|
||||||
maintenanceMessage: text("maintenanceMessage"),
|
maintenanceMessage: text("maintenanceMessage"),
|
||||||
maintenanceEstimatedTime: text("maintenanceEstimatedTime"),
|
maintenanceEstimatedTime: text("maintenanceEstimatedTime")
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const targets = sqliteTable("targets", {
|
export const targets = sqliteTable("targets", {
|
||||||
targetId: integer("targetId").primaryKey({autoIncrement: true}),
|
targetId: integer("targetId").primaryKey({ autoIncrement: true }),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.references(() => resources.resourceId, {
|
.references(() => resources.resourceId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -178,7 +181,7 @@ export const targets = sqliteTable("targets", {
|
|||||||
method: text("method"),
|
method: text("method"),
|
||||||
port: integer("port").notNull(),
|
port: integer("port").notNull(),
|
||||||
internalPort: integer("internalPort"),
|
internalPort: integer("internalPort"),
|
||||||
enabled: integer("enabled", {mode: "boolean"}).notNull().default(true),
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
||||||
path: text("path"),
|
path: text("path"),
|
||||||
pathMatchType: text("pathMatchType"), // exact, prefix, regex
|
pathMatchType: text("pathMatchType"), // exact, prefix, regex
|
||||||
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
|
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
|
||||||
@@ -192,8 +195,8 @@ export const targetHealthCheck = sqliteTable("targetHealthCheck", {
|
|||||||
}),
|
}),
|
||||||
targetId: integer("targetId")
|
targetId: integer("targetId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => targets.targetId, {onDelete: "cascade"}),
|
.references(() => targets.targetId, { onDelete: "cascade" }),
|
||||||
hcEnabled: integer("hcEnabled", {mode: "boolean"})
|
hcEnabled: integer("hcEnabled", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
hcPath: text("hcPath"),
|
hcPath: text("hcPath"),
|
||||||
@@ -215,7 +218,7 @@ export const targetHealthCheck = sqliteTable("targetHealthCheck", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const exitNodes = sqliteTable("exitNodes", {
|
export const exitNodes = sqliteTable("exitNodes", {
|
||||||
exitNodeId: integer("exitNodeId").primaryKey({autoIncrement: true}),
|
exitNodeId: integer("exitNodeId").primaryKey({ autoIncrement: true }),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
address: text("address").notNull(), // this is the address of the wireguard interface in gerbil
|
address: text("address").notNull(), // this is the address of the wireguard interface in gerbil
|
||||||
endpoint: text("endpoint").notNull(), // this is how to reach gerbil externally - gets put into the wireguard config
|
endpoint: text("endpoint").notNull(), // this is how to reach gerbil externally - gets put into the wireguard config
|
||||||
@@ -223,7 +226,7 @@ export const exitNodes = sqliteTable("exitNodes", {
|
|||||||
listenPort: integer("listenPort").notNull(),
|
listenPort: integer("listenPort").notNull(),
|
||||||
reachableAt: text("reachableAt"), // this is the internal address of the gerbil http server for command control
|
reachableAt: text("reachableAt"), // this is the internal address of the gerbil http server for command control
|
||||||
maxConnections: integer("maxConnections"),
|
maxConnections: integer("maxConnections"),
|
||||||
online: integer("online", {mode: "boolean"}).notNull().default(false),
|
online: integer("online", { mode: "boolean" }).notNull().default(false),
|
||||||
lastPing: integer("lastPing"),
|
lastPing: integer("lastPing"),
|
||||||
type: text("type").default("gerbil"), // gerbil, remoteExitNode
|
type: text("type").default("gerbil"), // gerbil, remoteExitNode
|
||||||
region: text("region")
|
region: text("region")
|
||||||
@@ -236,10 +239,10 @@ export const siteResources = sqliteTable("siteResources", {
|
|||||||
}),
|
}),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sites.siteId, {onDelete: "cascade"}),
|
.references(() => sites.siteId, { onDelete: "cascade" }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
niceId: text("niceId").notNull(),
|
niceId: text("niceId").notNull(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
mode: text("mode").notNull(), // "host" | "cidr" | "port"
|
mode: text("mode").notNull(), // "host" | "cidr" | "port"
|
||||||
@@ -292,20 +295,20 @@ export const users = sqliteTable("user", {
|
|||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
}),
|
}),
|
||||||
passwordHash: text("passwordHash"),
|
passwordHash: text("passwordHash"),
|
||||||
twoFactorEnabled: integer("twoFactorEnabled", {mode: "boolean"})
|
twoFactorEnabled: integer("twoFactorEnabled", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
twoFactorSetupRequested: integer("twoFactorSetupRequested", {
|
twoFactorSetupRequested: integer("twoFactorSetupRequested", {
|
||||||
mode: "boolean"
|
mode: "boolean"
|
||||||
}).default(false),
|
}).default(false),
|
||||||
twoFactorSecret: text("twoFactorSecret"),
|
twoFactorSecret: text("twoFactorSecret"),
|
||||||
emailVerified: integer("emailVerified", {mode: "boolean"})
|
emailVerified: integer("emailVerified", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
dateCreated: text("dateCreated").notNull(),
|
dateCreated: text("dateCreated").notNull(),
|
||||||
termsAcceptedTimestamp: text("termsAcceptedTimestamp"),
|
termsAcceptedTimestamp: text("termsAcceptedTimestamp"),
|
||||||
termsVersion: text("termsVersion"),
|
termsVersion: text("termsVersion"),
|
||||||
serverAdmin: integer("serverAdmin", {mode: "boolean"})
|
serverAdmin: integer("serverAdmin", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
lastPasswordChange: integer("lastPasswordChange")
|
lastPasswordChange: integer("lastPasswordChange")
|
||||||
@@ -339,7 +342,7 @@ export const webauthnChallenge = sqliteTable("webauthnChallenge", {
|
|||||||
export const setupTokens = sqliteTable("setupTokens", {
|
export const setupTokens = sqliteTable("setupTokens", {
|
||||||
tokenId: text("tokenId").primaryKey(),
|
tokenId: text("tokenId").primaryKey(),
|
||||||
token: text("token").notNull(),
|
token: text("token").notNull(),
|
||||||
used: integer("used", {mode: "boolean"}).notNull().default(false),
|
used: integer("used", { mode: "boolean" }).notNull().default(false),
|
||||||
dateCreated: text("dateCreated").notNull(),
|
dateCreated: text("dateCreated").notNull(),
|
||||||
dateUsed: text("dateUsed")
|
dateUsed: text("dateUsed")
|
||||||
});
|
});
|
||||||
@@ -378,7 +381,7 @@ export const clients = sqliteTable("clients", {
|
|||||||
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
||||||
lastPing: integer("lastPing"),
|
lastPing: integer("lastPing"),
|
||||||
type: text("type").notNull(), // "olm"
|
type: text("type").notNull(), // "olm"
|
||||||
online: integer("online", {mode: "boolean"}).notNull().default(false),
|
online: integer("online", { mode: "boolean" }).notNull().default(false),
|
||||||
// endpoint: text("endpoint"),
|
// endpoint: text("endpoint"),
|
||||||
lastHolePunch: integer("lastHolePunch")
|
lastHolePunch: integer("lastHolePunch")
|
||||||
});
|
});
|
||||||
@@ -424,10 +427,10 @@ export const olms = sqliteTable("olms", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const twoFactorBackupCodes = sqliteTable("twoFactorBackupCodes", {
|
export const twoFactorBackupCodes = sqliteTable("twoFactorBackupCodes", {
|
||||||
codeId: integer("id").primaryKey({autoIncrement: true}),
|
codeId: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
codeHash: text("codeHash").notNull()
|
codeHash: text("codeHash").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -435,7 +438,7 @@ export const sessions = sqliteTable("session", {
|
|||||||
sessionId: text("id").primaryKey(),
|
sessionId: text("id").primaryKey(),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
expiresAt: integer("expiresAt").notNull(),
|
expiresAt: integer("expiresAt").notNull(),
|
||||||
issuedAt: integer("issuedAt"),
|
issuedAt: integer("issuedAt"),
|
||||||
deviceAuthUsed: integer("deviceAuthUsed", { mode: "boolean" })
|
deviceAuthUsed: integer("deviceAuthUsed", { mode: "boolean" })
|
||||||
@@ -447,7 +450,7 @@ export const newtSessions = sqliteTable("newtSession", {
|
|||||||
sessionId: text("id").primaryKey(),
|
sessionId: text("id").primaryKey(),
|
||||||
newtId: text("newtId")
|
newtId: text("newtId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => newts.newtId, {onDelete: "cascade"}),
|
.references(() => newts.newtId, { onDelete: "cascade" }),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -455,14 +458,14 @@ export const olmSessions = sqliteTable("clientSession", {
|
|||||||
sessionId: text("id").primaryKey(),
|
sessionId: text("id").primaryKey(),
|
||||||
olmId: text("olmId")
|
olmId: text("olmId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => olms.olmId, {onDelete: "cascade"}),
|
.references(() => olms.olmId, { onDelete: "cascade" }),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userOrgs = sqliteTable("userOrgs", {
|
export const userOrgs = sqliteTable("userOrgs", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -471,28 +474,28 @@ export const userOrgs = sqliteTable("userOrgs", {
|
|||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId),
|
.references(() => roles.roleId),
|
||||||
isOwner: integer("isOwner", {mode: "boolean"}).notNull().default(false),
|
isOwner: integer("isOwner", { mode: "boolean" }).notNull().default(false),
|
||||||
autoProvisioned: integer("autoProvisioned", {
|
autoProvisioned: integer("autoProvisioned", {
|
||||||
mode: "boolean"
|
mode: "boolean"
|
||||||
}).default(false)
|
}).default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const emailVerificationCodes = sqliteTable("emailVerificationCodes", {
|
export const emailVerificationCodes = sqliteTable("emailVerificationCodes", {
|
||||||
codeId: integer("id").primaryKey({autoIncrement: true}),
|
codeId: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
code: text("code").notNull(),
|
code: text("code").notNull(),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const passwordResetTokens = sqliteTable("passwordResetTokens", {
|
export const passwordResetTokens = sqliteTable("passwordResetTokens", {
|
||||||
tokenId: integer("id").primaryKey({autoIncrement: true}),
|
tokenId: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
tokenHash: text("tokenHash").notNull(),
|
tokenHash: text("tokenHash").notNull(),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
});
|
});
|
||||||
@@ -504,13 +507,13 @@ export const actions = sqliteTable("actions", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const roles = sqliteTable("roles", {
|
export const roles = sqliteTable("roles", {
|
||||||
roleId: integer("roleId").primaryKey({autoIncrement: true}),
|
roleId: integer("roleId").primaryKey({ autoIncrement: true }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
})
|
})
|
||||||
.notNull(),
|
.notNull(),
|
||||||
isAdmin: integer("isAdmin", {mode: "boolean"}),
|
isAdmin: integer("isAdmin", { mode: "boolean" }),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
description: text("description")
|
description: text("description")
|
||||||
});
|
});
|
||||||
@@ -518,92 +521,92 @@ export const roles = sqliteTable("roles", {
|
|||||||
export const roleActions = sqliteTable("roleActions", {
|
export const roleActions = sqliteTable("roleActions", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, {onDelete: "cascade"}),
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
actionId: text("actionId")
|
actionId: text("actionId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => actions.actionId, {onDelete: "cascade"}),
|
.references(() => actions.actionId, { onDelete: "cascade" }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, {onDelete: "cascade"})
|
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userActions = sqliteTable("userActions", {
|
export const userActions = sqliteTable("userActions", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
actionId: text("actionId")
|
actionId: text("actionId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => actions.actionId, {onDelete: "cascade"}),
|
.references(() => actions.actionId, { onDelete: "cascade" }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, {onDelete: "cascade"})
|
.references(() => orgs.orgId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleSites = sqliteTable("roleSites", {
|
export const roleSites = sqliteTable("roleSites", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, {onDelete: "cascade"}),
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sites.siteId, {onDelete: "cascade"})
|
.references(() => sites.siteId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userSites = sqliteTable("userSites", {
|
export const userSites = sqliteTable("userSites", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sites.siteId, {onDelete: "cascade"})
|
.references(() => sites.siteId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userClients = sqliteTable("userClients", {
|
export const userClients = sqliteTable("userClients", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
clientId: integer("clientId")
|
clientId: integer("clientId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => clients.clientId, {onDelete: "cascade"})
|
.references(() => clients.clientId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleClients = sqliteTable("roleClients", {
|
export const roleClients = sqliteTable("roleClients", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, {onDelete: "cascade"}),
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
clientId: integer("clientId")
|
clientId: integer("clientId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => clients.clientId, {onDelete: "cascade"})
|
.references(() => clients.clientId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleResources = sqliteTable("roleResources", {
|
export const roleResources = sqliteTable("roleResources", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, {onDelete: "cascade"}),
|
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"})
|
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userResources = sqliteTable("userResources", {
|
export const userResources = sqliteTable("userResources", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, {onDelete: "cascade"}),
|
.references(() => users.userId, { onDelete: "cascade" }),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"})
|
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userInvites = sqliteTable("userInvites", {
|
export const userInvites = sqliteTable("userInvites", {
|
||||||
inviteId: text("inviteId").primaryKey(),
|
inviteId: text("inviteId").primaryKey(),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
expiresAt: integer("expiresAt").notNull(),
|
expiresAt: integer("expiresAt").notNull(),
|
||||||
tokenHash: text("token").notNull(),
|
tokenHash: text("token").notNull(),
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, {onDelete: "cascade"})
|
.references(() => roles.roleId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resourcePincode = sqliteTable("resourcePincode", {
|
export const resourcePincode = sqliteTable("resourcePincode", {
|
||||||
@@ -612,7 +615,7 @@ export const resourcePincode = sqliteTable("resourcePincode", {
|
|||||||
}),
|
}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
pincodeHash: text("pincodeHash").notNull(),
|
pincodeHash: text("pincodeHash").notNull(),
|
||||||
digitLength: integer("digitLength").notNull()
|
digitLength: integer("digitLength").notNull()
|
||||||
});
|
});
|
||||||
@@ -623,7 +626,7 @@ export const resourcePassword = sqliteTable("resourcePassword", {
|
|||||||
}),
|
}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
passwordHash: text("passwordHash").notNull()
|
passwordHash: text("passwordHash").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -633,28 +636,38 @@ export const resourceHeaderAuth = sqliteTable("resourceHeaderAuth", {
|
|||||||
}),
|
}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
headerAuthHash: text("headerAuthHash").notNull()
|
headerAuthHash: text("headerAuthHash").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resourceHeaderAuthExtendedCompatibility = sqliteTable("resourceHeaderAuthExtendedCompatibility", {
|
export const resourceHeaderAuthExtendedCompatibility = sqliteTable(
|
||||||
headerAuthExtendedCompatibilityId: integer("headerAuthExtendedCompatibilityId").primaryKey({
|
"resourceHeaderAuthExtendedCompatibility",
|
||||||
autoIncrement: true
|
{
|
||||||
}),
|
headerAuthExtendedCompatibilityId: integer(
|
||||||
resourceId: integer("resourceId")
|
"headerAuthExtendedCompatibilityId"
|
||||||
.notNull()
|
).primaryKey({
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
autoIncrement: true
|
||||||
extendedCompatibilityIsActivated: integer("extendedCompatibilityIsActivated", {mode: "boolean"}).notNull().default(true)
|
}),
|
||||||
});
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
extendedCompatibilityIsActivated: integer(
|
||||||
|
"extendedCompatibilityIsActivated",
|
||||||
|
{ mode: "boolean" }
|
||||||
|
)
|
||||||
|
.notNull()
|
||||||
|
.default(true)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const resourceAccessToken = sqliteTable("resourceAccessToken", {
|
export const resourceAccessToken = sqliteTable("resourceAccessToken", {
|
||||||
accessTokenId: text("accessTokenId").primaryKey(),
|
accessTokenId: text("accessTokenId").primaryKey(),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
tokenHash: text("tokenHash").notNull(),
|
tokenHash: text("tokenHash").notNull(),
|
||||||
sessionLength: integer("sessionLength").notNull(),
|
sessionLength: integer("sessionLength").notNull(),
|
||||||
expiresAt: integer("expiresAt"),
|
expiresAt: integer("expiresAt"),
|
||||||
@@ -667,13 +680,13 @@ export const resourceSessions = sqliteTable("resourceSessions", {
|
|||||||
sessionId: text("id").primaryKey(),
|
sessionId: text("id").primaryKey(),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
expiresAt: integer("expiresAt").notNull(),
|
expiresAt: integer("expiresAt").notNull(),
|
||||||
sessionLength: integer("sessionLength").notNull(),
|
sessionLength: integer("sessionLength").notNull(),
|
||||||
doNotExtend: integer("doNotExtend", {mode: "boolean"})
|
doNotExtend: integer("doNotExtend", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
isRequestToken: integer("isRequestToken", {mode: "boolean"}),
|
isRequestToken: integer("isRequestToken", { mode: "boolean" }),
|
||||||
userSessionId: text("userSessionId").references(() => sessions.sessionId, {
|
userSessionId: text("userSessionId").references(() => sessions.sessionId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
}),
|
}),
|
||||||
@@ -705,11 +718,11 @@ export const resourceSessions = sqliteTable("resourceSessions", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const resourceWhitelist = sqliteTable("resourceWhitelist", {
|
export const resourceWhitelist = sqliteTable("resourceWhitelist", {
|
||||||
whitelistId: integer("id").primaryKey({autoIncrement: true}),
|
whitelistId: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"})
|
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resourceOtp = sqliteTable("resourceOtp", {
|
export const resourceOtp = sqliteTable("resourceOtp", {
|
||||||
@@ -718,7 +731,7 @@ export const resourceOtp = sqliteTable("resourceOtp", {
|
|||||||
}),
|
}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
otpHash: text("otpHash").notNull(),
|
otpHash: text("otpHash").notNull(),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
@@ -730,11 +743,11 @@ export const versionMigrations = sqliteTable("versionMigrations", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const resourceRules = sqliteTable("resourceRules", {
|
export const resourceRules = sqliteTable("resourceRules", {
|
||||||
ruleId: integer("ruleId").primaryKey({autoIncrement: true}),
|
ruleId: integer("ruleId").primaryKey({ autoIncrement: true }),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
enabled: integer("enabled", {mode: "boolean"}).notNull().default(true),
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
||||||
priority: integer("priority").notNull(),
|
priority: integer("priority").notNull(),
|
||||||
action: text("action").notNull(), // ACCEPT, DROP, PASS
|
action: text("action").notNull(), // ACCEPT, DROP, PASS
|
||||||
match: text("match").notNull(), // CIDR, PATH, IP
|
match: text("match").notNull(), // CIDR, PATH, IP
|
||||||
@@ -742,17 +755,17 @@ export const resourceRules = sqliteTable("resourceRules", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const supporterKey = sqliteTable("supporterKey", {
|
export const supporterKey = sqliteTable("supporterKey", {
|
||||||
keyId: integer("keyId").primaryKey({autoIncrement: true}),
|
keyId: integer("keyId").primaryKey({ autoIncrement: true }),
|
||||||
key: text("key").notNull(),
|
key: text("key").notNull(),
|
||||||
githubUsername: text("githubUsername").notNull(),
|
githubUsername: text("githubUsername").notNull(),
|
||||||
phrase: text("phrase"),
|
phrase: text("phrase"),
|
||||||
tier: text("tier"),
|
tier: text("tier"),
|
||||||
valid: integer("valid", {mode: "boolean"}).notNull().default(false)
|
valid: integer("valid", { mode: "boolean" }).notNull().default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Identity Providers
|
// Identity Providers
|
||||||
export const idp = sqliteTable("idp", {
|
export const idp = sqliteTable("idp", {
|
||||||
idpId: integer("idpId").primaryKey({autoIncrement: true}),
|
idpId: integer("idpId").primaryKey({ autoIncrement: true }),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
type: text("type").notNull(),
|
type: text("type").notNull(),
|
||||||
defaultRoleMapping: text("defaultRoleMapping"),
|
defaultRoleMapping: text("defaultRoleMapping"),
|
||||||
@@ -772,7 +785,7 @@ export const idpOidcConfig = sqliteTable("idpOidcConfig", {
|
|||||||
variant: text("variant").notNull().default("oidc"),
|
variant: text("variant").notNull().default("oidc"),
|
||||||
idpId: integer("idpId")
|
idpId: integer("idpId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => idp.idpId, {onDelete: "cascade"}),
|
.references(() => idp.idpId, { onDelete: "cascade" }),
|
||||||
clientId: text("clientId").notNull(),
|
clientId: text("clientId").notNull(),
|
||||||
clientSecret: text("clientSecret").notNull(),
|
clientSecret: text("clientSecret").notNull(),
|
||||||
authUrl: text("authUrl").notNull(),
|
authUrl: text("authUrl").notNull(),
|
||||||
@@ -800,22 +813,22 @@ export const apiKeys = sqliteTable("apiKeys", {
|
|||||||
apiKeyHash: text("apiKeyHash").notNull(),
|
apiKeyHash: text("apiKeyHash").notNull(),
|
||||||
lastChars: text("lastChars").notNull(),
|
lastChars: text("lastChars").notNull(),
|
||||||
createdAt: text("dateCreated").notNull(),
|
createdAt: text("dateCreated").notNull(),
|
||||||
isRoot: integer("isRoot", {mode: "boolean"}).notNull().default(false)
|
isRoot: integer("isRoot", { mode: "boolean" }).notNull().default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiKeyActions = sqliteTable("apiKeyActions", {
|
export const apiKeyActions = sqliteTable("apiKeyActions", {
|
||||||
apiKeyId: text("apiKeyId")
|
apiKeyId: text("apiKeyId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => apiKeys.apiKeyId, {onDelete: "cascade"}),
|
.references(() => apiKeys.apiKeyId, { onDelete: "cascade" }),
|
||||||
actionId: text("actionId")
|
actionId: text("actionId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => actions.actionId, {onDelete: "cascade"})
|
.references(() => actions.actionId, { onDelete: "cascade" })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiKeyOrg = sqliteTable("apiKeyOrg", {
|
export const apiKeyOrg = sqliteTable("apiKeyOrg", {
|
||||||
apiKeyId: text("apiKeyId")
|
apiKeyId: text("apiKeyId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => apiKeys.apiKeyId, {onDelete: "cascade"}),
|
.references(() => apiKeys.apiKeyId, { onDelete: "cascade" }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -826,10 +839,10 @@ export const apiKeyOrg = sqliteTable("apiKeyOrg", {
|
|||||||
export const idpOrg = sqliteTable("idpOrg", {
|
export const idpOrg = sqliteTable("idpOrg", {
|
||||||
idpId: integer("idpId")
|
idpId: integer("idpId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => idp.idpId, {onDelete: "cascade"}),
|
.references(() => idp.idpId, { onDelete: "cascade" }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
roleMapping: text("roleMapping"),
|
roleMapping: text("roleMapping"),
|
||||||
orgMapping: text("orgMapping")
|
orgMapping: text("orgMapping")
|
||||||
});
|
});
|
||||||
@@ -847,19 +860,19 @@ export const blueprints = sqliteTable("blueprints", {
|
|||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
source: text("source").notNull(),
|
source: text("source").notNull(),
|
||||||
createdAt: integer("createdAt").notNull(),
|
createdAt: integer("createdAt").notNull(),
|
||||||
succeeded: integer("succeeded", {mode: "boolean"}).notNull(),
|
succeeded: integer("succeeded", { mode: "boolean" }).notNull(),
|
||||||
contents: text("contents").notNull(),
|
contents: text("contents").notNull(),
|
||||||
message: text("message")
|
message: text("message")
|
||||||
});
|
});
|
||||||
export const requestAuditLog = sqliteTable(
|
export const requestAuditLog = sqliteTable(
|
||||||
"requestAuditLog",
|
"requestAuditLog",
|
||||||
{
|
{
|
||||||
id: integer("id").primaryKey({autoIncrement: true}),
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds
|
timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds
|
||||||
orgId: text("orgId").references(() => orgs.orgId, {
|
orgId: text("orgId").references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
}),
|
}),
|
||||||
action: integer("action", {mode: "boolean"}).notNull(),
|
action: integer("action", { mode: "boolean" }).notNull(),
|
||||||
reason: integer("reason").notNull(),
|
reason: integer("reason").notNull(),
|
||||||
actorType: text("actorType"),
|
actorType: text("actorType"),
|
||||||
actor: text("actor"),
|
actor: text("actor"),
|
||||||
@@ -876,7 +889,7 @@ export const requestAuditLog = sqliteTable(
|
|||||||
host: text("host"),
|
host: text("host"),
|
||||||
path: text("path"),
|
path: text("path"),
|
||||||
method: text("method"),
|
method: text("method"),
|
||||||
tls: integer("tls", {mode: "boolean"})
|
tls: integer("tls", { mode: "boolean" })
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
index("idx_requestAuditLog_timestamp").on(table.timestamp),
|
index("idx_requestAuditLog_timestamp").on(table.timestamp),
|
||||||
@@ -932,7 +945,9 @@ export type ResourceSession = InferSelectModel<typeof resourceSessions>;
|
|||||||
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
||||||
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
||||||
export type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
|
export type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
|
||||||
export type ResourceHeaderAuthExtendedCompatibility = InferSelectModel<typeof resourceHeaderAuthExtendedCompatibility>;
|
export type ResourceHeaderAuthExtendedCompatibility = InferSelectModel<
|
||||||
|
typeof resourceHeaderAuthExtendedCompatibility
|
||||||
|
>;
|
||||||
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
||||||
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
||||||
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const MaintenanceSchema = z.object({
|
export const MaintenanceSchema = z.object({});
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
import { db, newts, blueprints, Blueprint, Site, siteResources, roleSiteResources, userSiteResources, clientSiteResources } from "@server/db";
|
import {
|
||||||
|
db,
|
||||||
|
newts,
|
||||||
|
blueprints,
|
||||||
|
Blueprint,
|
||||||
|
Site,
|
||||||
|
siteResources,
|
||||||
|
roleSiteResources,
|
||||||
|
userSiteResources,
|
||||||
|
clientSiteResources
|
||||||
|
} from "@server/db";
|
||||||
import { Config, ConfigSchema } from "./types";
|
import { Config, ConfigSchema } from "./types";
|
||||||
import { ProxyResourcesResults, updateProxyResources } from "./proxyResources";
|
import { ProxyResourcesResults, updateProxyResources } from "./proxyResources";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
@@ -126,7 +136,7 @@ export async function applyBlueprint({
|
|||||||
)
|
)
|
||||||
.then((rows) => rows.map((row) => row.roleId));
|
.then((rows) => rows.map((row) => row.roleId));
|
||||||
|
|
||||||
const existingUserIds= await trx
|
const existingUserIds = await trx
|
||||||
.select()
|
.select()
|
||||||
.from(userSiteResources)
|
.from(userSiteResources)
|
||||||
.where(
|
.where(
|
||||||
@@ -134,7 +144,8 @@ export async function applyBlueprint({
|
|||||||
userSiteResources.siteResourceId,
|
userSiteResources.siteResourceId,
|
||||||
result.oldSiteResource.siteResourceId
|
result.oldSiteResource.siteResourceId
|
||||||
)
|
)
|
||||||
).then((rows) => rows.map((row) => row.userId));
|
)
|
||||||
|
.then((rows) => rows.map((row) => row.userId));
|
||||||
|
|
||||||
const existingClientIds = await trx
|
const existingClientIds = await trx
|
||||||
.select()
|
.select()
|
||||||
@@ -144,13 +155,19 @@ export async function applyBlueprint({
|
|||||||
clientSiteResources.siteResourceId,
|
clientSiteResources.siteResourceId,
|
||||||
result.oldSiteResource.siteResourceId
|
result.oldSiteResource.siteResourceId
|
||||||
)
|
)
|
||||||
).then((rows) => rows.map((row) => row.clientId));
|
)
|
||||||
|
.then((rows) => rows.map((row) => row.clientId));
|
||||||
|
|
||||||
// delete the existing site resource
|
// delete the existing site resource
|
||||||
await trx
|
await trx
|
||||||
.delete(siteResources)
|
.delete(siteResources)
|
||||||
.where(
|
.where(
|
||||||
and(eq(siteResources.siteResourceId, result.oldSiteResource.siteResourceId))
|
and(
|
||||||
|
eq(
|
||||||
|
siteResources.siteResourceId,
|
||||||
|
result.oldSiteResource.siteResourceId
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
await rebuildClientAssociationsFromSiteResource(
|
await rebuildClientAssociationsFromSiteResource(
|
||||||
@@ -161,7 +178,7 @@ export async function applyBlueprint({
|
|||||||
const [insertedSiteResource] = await trx
|
const [insertedSiteResource] = await trx
|
||||||
.insert(siteResources)
|
.insert(siteResources)
|
||||||
.values({
|
.values({
|
||||||
...result.newSiteResource,
|
...result.newSiteResource
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
@@ -172,18 +189,20 @@ export async function applyBlueprint({
|
|||||||
|
|
||||||
if (existingRoleIds.length > 0) {
|
if (existingRoleIds.length > 0) {
|
||||||
await trx.insert(roleSiteResources).values(
|
await trx.insert(roleSiteResources).values(
|
||||||
existingRoleIds.map((roleId) => ({
|
existingRoleIds.map((roleId) => ({
|
||||||
roleId,
|
roleId,
|
||||||
siteResourceId: insertedSiteResource!.siteResourceId
|
siteResourceId:
|
||||||
|
insertedSiteResource!.siteResourceId
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingUserIds.length > 0) {
|
if (existingUserIds.length > 0) {
|
||||||
await trx.insert(userSiteResources).values(
|
await trx.insert(userSiteResources).values(
|
||||||
existingUserIds.map((userId) => ({
|
existingUserIds.map((userId) => ({
|
||||||
userId,
|
userId,
|
||||||
siteResourceId: insertedSiteResource!.siteResourceId
|
siteResourceId:
|
||||||
|
insertedSiteResource!.siteResourceId
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -192,7 +211,8 @@ export async function applyBlueprint({
|
|||||||
await trx.insert(clientSiteResources).values(
|
await trx.insert(clientSiteResources).values(
|
||||||
existingClientIds.map((clientId) => ({
|
existingClientIds.map((clientId) => ({
|
||||||
clientId,
|
clientId,
|
||||||
siteResourceId: insertedSiteResource!.siteResourceId
|
siteResourceId:
|
||||||
|
insertedSiteResource!.siteResourceId
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -201,7 +221,6 @@ export async function applyBlueprint({
|
|||||||
insertedSiteResource,
|
insertedSiteResource,
|
||||||
trx
|
trx
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const [newSite] = await trx
|
const [newSite] = await trx
|
||||||
.select()
|
.select()
|
||||||
|
|||||||
@@ -54,11 +54,13 @@ export const AuthSchema = z.object({
|
|||||||
// pincode has to have 6 digits
|
// pincode has to have 6 digits
|
||||||
pincode: z.number().min(100000).max(999999).optional(),
|
pincode: z.number().min(100000).max(999999).optional(),
|
||||||
password: z.string().min(1).optional(),
|
password: z.string().min(1).optional(),
|
||||||
"basic-auth": z.object({
|
"basic-auth": z
|
||||||
user: z.string().min(1),
|
.object({
|
||||||
password: z.string().min(1),
|
user: z.string().min(1),
|
||||||
extendedCompatibility: z.boolean().default(true)
|
password: z.string().min(1),
|
||||||
}).optional(),
|
extendedCompatibility: z.boolean().default(true)
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
"sso-enabled": z.boolean().optional().default(false),
|
"sso-enabled": z.boolean().optional().default(false),
|
||||||
"sso-roles": z
|
"sso-roles": z
|
||||||
.array(z.string())
|
.array(z.string())
|
||||||
|
|||||||
@@ -318,10 +318,7 @@ export function doCidrsOverlap(cidr1: string, cidr2: string): boolean {
|
|||||||
const range2 = cidrToRange(cidr2);
|
const range2 = cidrToRange(cidr2);
|
||||||
|
|
||||||
// Overlap if the ranges intersect
|
// Overlap if the ranges intersect
|
||||||
return (
|
return range1.start <= range2.end && range2.start <= range1.end;
|
||||||
range1.start <= range2.end &&
|
|
||||||
range2.start <= range1.end
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNextAvailableClientSubnet(
|
export async function getNextAvailableClientSubnet(
|
||||||
|
|||||||
@@ -216,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()
|
||||||
.prefault({}),
|
.prefault({}),
|
||||||
|
|||||||
@@ -294,12 +294,12 @@ export async function getTraefikConfig(
|
|||||||
certResolver: resolverName,
|
certResolver: resolverName,
|
||||||
...(preferWildcard
|
...(preferWildcard
|
||||||
? {
|
? {
|
||||||
domains: [
|
domains: [
|
||||||
{
|
{
|
||||||
main: wildCard
|
main: wildCard
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -475,9 +475,9 @@ export async function getTraefikConfig(
|
|||||||
// RECEIVE BANDWIDTH ENDPOINT.
|
// RECEIVE BANDWIDTH ENDPOINT.
|
||||||
|
|
||||||
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
||||||
const anySitesOnline = (
|
const anySitesOnline = targets.some(
|
||||||
targets
|
(target) => target.site.online
|
||||||
).some((target) => target.site.online);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
targets
|
targets
|
||||||
@@ -544,14 +544,14 @@ export async function getTraefikConfig(
|
|||||||
})(),
|
})(),
|
||||||
...(resource.stickySession
|
...(resource.stickySession
|
||||||
? {
|
? {
|
||||||
sticky: {
|
sticky: {
|
||||||
cookie: {
|
cookie: {
|
||||||
name: "p_sticky", // TODO: make this configurable via config.yml like other cookies
|
name: "p_sticky", // TODO: make this configurable via config.yml like other cookies
|
||||||
secure: resource.ssl,
|
secure: resource.ssl,
|
||||||
httpOnly: true
|
httpOnly: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -603,9 +603,9 @@ export async function getTraefikConfig(
|
|||||||
loadBalancer: {
|
loadBalancer: {
|
||||||
servers: (() => {
|
servers: (() => {
|
||||||
// Check if any sites are online
|
// Check if any sites are online
|
||||||
const anySitesOnline = (
|
const anySitesOnline = targets.some(
|
||||||
targets
|
(target) => target.site.online
|
||||||
).some((target) => target.site.online);
|
);
|
||||||
|
|
||||||
return targets
|
return targets
|
||||||
.filter((target) => {
|
.filter((target) => {
|
||||||
@@ -654,18 +654,18 @@ export async function getTraefikConfig(
|
|||||||
})(),
|
})(),
|
||||||
...(resource.proxyProtocol && protocol == "tcp"
|
...(resource.proxyProtocol && protocol == "tcp"
|
||||||
? {
|
? {
|
||||||
serversTransport: `${ppPrefix}${resource.proxyProtocolVersion || 1}@file` // TODO: does @file here cause issues?
|
serversTransport: `${ppPrefix}${resource.proxyProtocolVersion || 1}@file` // TODO: does @file here cause issues?
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(resource.stickySession
|
...(resource.stickySession
|
||||||
? {
|
? {
|
||||||
sticky: {
|
sticky: {
|
||||||
ipStrategy: {
|
ipStrategy: {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
sourcePort: true
|
sourcePort: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ export async function getTraefikConfig(
|
|||||||
generateLoginPageRouters = false,
|
generateLoginPageRouters = false,
|
||||||
allowRawResources = true
|
allowRawResources = true
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
|
|
||||||
// Get resources with their targets and sites in a single optimized query
|
// Get resources with their targets and sites in a single optimized query
|
||||||
// Start from sites on this exit node, then join to targets and resources
|
// Start from sites on this exit node, then join to targets and resources
|
||||||
const resourcesWithTargetsAndSites = await db
|
const resourcesWithTargetsAndSites = await db
|
||||||
@@ -435,17 +434,15 @@ export async function getTraefikConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const availableServers = targets.filter(
|
const availableServers = targets.filter((target) => {
|
||||||
(target) => {
|
if (!target.enabled) return false;
|
||||||
if (!target.enabled) return false;
|
|
||||||
|
|
||||||
if (!target.site.online) return false;
|
if (!target.site.online) return false;
|
||||||
|
|
||||||
if (target.health == "unhealthy") return false;
|
if (target.health == "unhealthy") return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const hasHealthyServers = availableServers.length > 0;
|
const hasHealthyServers = availableServers.length > 0;
|
||||||
|
|
||||||
@@ -794,9 +791,9 @@ export async function getTraefikConfig(
|
|||||||
loadBalancer: {
|
loadBalancer: {
|
||||||
servers: (() => {
|
servers: (() => {
|
||||||
// Check if any sites are online
|
// Check if any sites are online
|
||||||
const anySitesOnline = (
|
const anySitesOnline = targets.some(
|
||||||
targets
|
(target) => target.site.online
|
||||||
).some((target) => target.site.online);
|
);
|
||||||
|
|
||||||
return targets
|
return targets
|
||||||
.filter((target) => {
|
.filter((target) => {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import {
|
|||||||
resourceHeaderAuthExtendedCompatibility,
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
ResourceHeaderAuthExtendedCompatibility,
|
ResourceHeaderAuthExtendedCompatibility,
|
||||||
orgs,
|
orgs,
|
||||||
requestAuditLog,
|
requestAuditLog
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import {
|
import {
|
||||||
resources,
|
resources,
|
||||||
@@ -503,7 +503,10 @@ hybridRouter.get(
|
|||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuthExtendedCompatibility,
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
eq(resourceHeaderAuthExtendedCompatibility.resourceId, resources.resourceId)
|
eq(
|
||||||
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
|
resources.resourceId
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.where(eq(resources.fullDomain, domain))
|
.where(eq(resources.fullDomain, domain))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
@@ -538,7 +541,8 @@ hybridRouter.get(
|
|||||||
pincode: result.resourcePincode,
|
pincode: result.resourcePincode,
|
||||||
password: result.resourcePassword,
|
password: result.resourcePassword,
|
||||||
headerAuth: result.resourceHeaderAuth,
|
headerAuth: result.resourceHeaderAuth,
|
||||||
headerAuthExtendedCompatibility: result.resourceHeaderAuthExtendedCompatibility
|
headerAuthExtendedCompatibility:
|
||||||
|
result.resourceHeaderAuthExtendedCompatibility
|
||||||
};
|
};
|
||||||
|
|
||||||
return response<ResourceWithAuth>(res, {
|
return response<ResourceWithAuth>(res, {
|
||||||
|
|||||||
@@ -39,4 +39,4 @@ internalRouter.post(
|
|||||||
|
|
||||||
internalRouter.get(`/license/status`, license.getLicenseStatus);
|
internalRouter.get(`/license/status`, license.getLicenseStatus);
|
||||||
|
|
||||||
internalRouter.get("/maintenance/info", resource.getMaintenanceInfo);
|
internalRouter.get("/maintenance/info", resource.getMaintenanceInfo);
|
||||||
|
|||||||
@@ -11,4 +11,4 @@
|
|||||||
* This file is not licensed under the AGPLv3.
|
* This file is not licensed under the AGPLv3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./getMaintenanceInfo";
|
export * from "./getMaintenanceInfo";
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ async function query(query: Q) {
|
|||||||
.where(and(baseConditions, not(isNull(requestAuditLog.location))))
|
.where(and(baseConditions, not(isNull(requestAuditLog.location))))
|
||||||
.groupBy(requestAuditLog.location)
|
.groupBy(requestAuditLog.location)
|
||||||
.orderBy(desc(totalQ))
|
.orderBy(desc(totalQ))
|
||||||
.limit(DISTINCT_LIMIT+1);
|
.limit(DISTINCT_LIMIT + 1);
|
||||||
|
|
||||||
if (requestsPerCountry.length > DISTINCT_LIMIT) {
|
if (requestsPerCountry.length > DISTINCT_LIMIT) {
|
||||||
// throw an error
|
// throw an error
|
||||||
|
|||||||
@@ -189,22 +189,22 @@ async function queryUniqueFilterAttributes(
|
|||||||
.selectDistinct({ actor: requestAuditLog.actor })
|
.selectDistinct({ actor: requestAuditLog.actor })
|
||||||
.from(requestAuditLog)
|
.from(requestAuditLog)
|
||||||
.where(baseConditions)
|
.where(baseConditions)
|
||||||
.limit(DISTINCT_LIMIT+1),
|
.limit(DISTINCT_LIMIT + 1),
|
||||||
primaryDb
|
primaryDb
|
||||||
.selectDistinct({ locations: requestAuditLog.location })
|
.selectDistinct({ locations: requestAuditLog.location })
|
||||||
.from(requestAuditLog)
|
.from(requestAuditLog)
|
||||||
.where(baseConditions)
|
.where(baseConditions)
|
||||||
.limit(DISTINCT_LIMIT+1),
|
.limit(DISTINCT_LIMIT + 1),
|
||||||
primaryDb
|
primaryDb
|
||||||
.selectDistinct({ hosts: requestAuditLog.host })
|
.selectDistinct({ hosts: requestAuditLog.host })
|
||||||
.from(requestAuditLog)
|
.from(requestAuditLog)
|
||||||
.where(baseConditions)
|
.where(baseConditions)
|
||||||
.limit(DISTINCT_LIMIT+1),
|
.limit(DISTINCT_LIMIT + 1),
|
||||||
primaryDb
|
primaryDb
|
||||||
.selectDistinct({ paths: requestAuditLog.path })
|
.selectDistinct({ paths: requestAuditLog.path })
|
||||||
.from(requestAuditLog)
|
.from(requestAuditLog)
|
||||||
.where(baseConditions)
|
.where(baseConditions)
|
||||||
.limit(DISTINCT_LIMIT+1),
|
.limit(DISTINCT_LIMIT + 1),
|
||||||
primaryDb
|
primaryDb
|
||||||
.selectDistinct({
|
.selectDistinct({
|
||||||
id: requestAuditLog.resourceId,
|
id: requestAuditLog.resourceId,
|
||||||
@@ -216,7 +216,7 @@ async function queryUniqueFilterAttributes(
|
|||||||
eq(requestAuditLog.resourceId, resources.resourceId)
|
eq(requestAuditLog.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
.where(baseConditions)
|
.where(baseConditions)
|
||||||
.limit(DISTINCT_LIMIT+1)
|
.limit(DISTINCT_LIMIT + 1)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// TODO: for stuff like the paths this is too restrictive so lets just show some of the paths and the user needs to
|
// TODO: for stuff like the paths this is too restrictive so lets just show some of the paths and the user needs to
|
||||||
@@ -309,10 +309,12 @@ export async function queryRequestAuditLogs(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
// if the message is "Too many distinct filter attributes to retrieve. Please refine your time range.", return a 400 and the message
|
// if the message is "Too many distinct filter attributes to retrieve. Please refine your time range.", return a 400 and the message
|
||||||
if (error instanceof Error && error.message === "Too many distinct filter attributes to retrieve. Please refine your time range.") {
|
if (
|
||||||
return next(
|
error instanceof Error &&
|
||||||
createHttpError(HttpCode.BAD_REQUEST, error.message)
|
error.message ===
|
||||||
);
|
"Too many distinct filter attributes to retrieve. Please refine your time range."
|
||||||
|
) {
|
||||||
|
return next(createHttpError(HttpCode.BAD_REQUEST, error.message));
|
||||||
}
|
}
|
||||||
return next(
|
return next(
|
||||||
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export async function getConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clean up the public key - keep only valid base64 characters (A-Z, a-z, 0-9, +, /, =)
|
// clean up the public key - keep only valid base64 characters (A-Z, a-z, 0-9, +, /, =)
|
||||||
const cleanedPublicKey = publicKey.replace(/[^A-Za-z0-9+/=]/g, '');
|
const cleanedPublicKey = publicKey.replace(/[^A-Za-z0-9+/=]/g, "");
|
||||||
|
|
||||||
const exitNode = await createExitNode(cleanedPublicKey, reachableAt);
|
const exitNode = await createExitNode(cleanedPublicKey, reachableAt);
|
||||||
|
|
||||||
|
|||||||
@@ -858,7 +858,6 @@ authenticated.put(
|
|||||||
blueprints.applyJSONBlueprint
|
blueprints.applyJSONBlueprint
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
authenticated.get(
|
authenticated.get(
|
||||||
"/org/:orgId/blueprint/:blueprintId",
|
"/org/:orgId/blueprint/:blueprintId",
|
||||||
verifyApiKeyOrgAccess,
|
verifyApiKeyOrgAccess,
|
||||||
@@ -866,7 +865,6 @@ authenticated.get(
|
|||||||
blueprints.getBlueprint
|
blueprints.getBlueprint
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
authenticated.get(
|
authenticated.get(
|
||||||
"/org/:orgId/blueprints",
|
"/org/:orgId/blueprints",
|
||||||
verifyApiKeyOrgAccess,
|
verifyApiKeyOrgAccess,
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ export async function pickOrgDefaults(
|
|||||||
// const subnet = await getNextAvailableOrgSubnet();
|
// const subnet = await getNextAvailableOrgSubnet();
|
||||||
// Just hard code the subnet for now for everyone
|
// Just hard code the subnet for now for everyone
|
||||||
const subnet = config.getRawConfig().orgs.subnet_group;
|
const subnet = config.getRawConfig().orgs.subnet_group;
|
||||||
const utilitySubnet =
|
const utilitySubnet = config.getRawConfig().orgs.utility_subnet_group;
|
||||||
config.getRawConfig().orgs.utility_subnet_group;
|
|
||||||
|
|
||||||
return response<PickOrgDefaultsResponse>(res, {
|
return response<PickOrgDefaultsResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -154,4 +154,4 @@ export async function updateOrg(
|
|||||||
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import {Request, Response, NextFunction} from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import {z} from "zod";
|
import { z } from "zod";
|
||||||
import {
|
import {
|
||||||
db,
|
db,
|
||||||
resourceHeaderAuth, resourceHeaderAuthExtendedCompatibility,
|
resourceHeaderAuth,
|
||||||
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
resourcePassword,
|
resourcePassword,
|
||||||
resourcePincode,
|
resourcePincode,
|
||||||
resources
|
resources
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import {eq} from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import {fromError} from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import {build} from "@server/build";
|
import { build } from "@server/build";
|
||||||
|
|
||||||
const getResourceAuthInfoSchema = z.strictObject({
|
const getResourceAuthInfoSchema = z.strictObject({
|
||||||
resourceGuid: z.string()
|
resourceGuid: z.string()
|
||||||
@@ -52,68 +53,68 @@ export async function getResourceAuthInfo(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {resourceGuid} = parsedParams.data;
|
const { resourceGuid } = parsedParams.data;
|
||||||
|
|
||||||
const isGuidInteger = /^\d+$/.test(resourceGuid);
|
const isGuidInteger = /^\d+$/.test(resourceGuid);
|
||||||
|
|
||||||
const [result] =
|
const [result] =
|
||||||
isGuidInteger && build === "saas"
|
isGuidInteger && build === "saas"
|
||||||
? await db
|
? await db
|
||||||
.select()
|
.select()
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourcePincode,
|
resourcePincode,
|
||||||
eq(resourcePincode.resourceId, resources.resourceId)
|
eq(resourcePincode.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourcePassword,
|
resourcePassword,
|
||||||
eq(resourcePassword.resourceId, resources.resourceId)
|
eq(resourcePassword.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(
|
eq(
|
||||||
resourceHeaderAuth.resourceId,
|
resourceHeaderAuth.resourceId,
|
||||||
resources.resourceId
|
resources.resourceId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuthExtendedCompatibility,
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
eq(
|
eq(
|
||||||
resourceHeaderAuthExtendedCompatibility.resourceId,
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
resources.resourceId
|
resources.resourceId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.where(eq(resources.resourceId, Number(resourceGuid)))
|
.where(eq(resources.resourceId, Number(resourceGuid)))
|
||||||
.limit(1)
|
.limit(1)
|
||||||
: await db
|
: await db
|
||||||
.select()
|
.select()
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourcePincode,
|
resourcePincode,
|
||||||
eq(resourcePincode.resourceId, resources.resourceId)
|
eq(resourcePincode.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourcePassword,
|
resourcePassword,
|
||||||
eq(resourcePassword.resourceId, resources.resourceId)
|
eq(resourcePassword.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(
|
eq(
|
||||||
resourceHeaderAuth.resourceId,
|
resourceHeaderAuth.resourceId,
|
||||||
resources.resourceId
|
resources.resourceId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuthExtendedCompatibility,
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
eq(
|
eq(
|
||||||
resourceHeaderAuthExtendedCompatibility.resourceId,
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
resources.resourceId
|
resources.resourceId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.where(eq(resources.resourceGuid, resourceGuid))
|
.where(eq(resources.resourceGuid, resourceGuid))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
const resource = result?.resources;
|
const resource = result?.resources;
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
@@ -125,7 +126,8 @@ export async function getResourceAuthInfo(
|
|||||||
const pincode = result?.resourcePincode;
|
const pincode = result?.resourcePincode;
|
||||||
const password = result?.resourcePassword;
|
const password = result?.resourcePassword;
|
||||||
const headerAuth = result?.resourceHeaderAuth;
|
const headerAuth = result?.resourceHeaderAuth;
|
||||||
const headerAuthExtendedCompatibility = result?.resourceHeaderAuthExtendedCompatibility;
|
const headerAuthExtendedCompatibility =
|
||||||
|
result?.resourceHeaderAuthExtendedCompatibility;
|
||||||
|
|
||||||
const url = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`;
|
const url = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`;
|
||||||
|
|
||||||
@@ -138,7 +140,8 @@ export async function getResourceAuthInfo(
|
|||||||
password: password !== null,
|
password: password !== null,
|
||||||
pincode: pincode !== null,
|
pincode: pincode !== null,
|
||||||
headerAuth: headerAuth !== null,
|
headerAuth: headerAuth !== null,
|
||||||
headerAuthExtendedCompatibility: headerAuthExtendedCompatibility !== null,
|
headerAuthExtendedCompatibility:
|
||||||
|
headerAuthExtendedCompatibility !== null,
|
||||||
sso: resource.sso,
|
sso: resource.sso,
|
||||||
blockAccess: resource.blockAccess,
|
blockAccess: resource.blockAccess,
|
||||||
url,
|
url,
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import {db, resourceHeaderAuth, resourceHeaderAuthExtendedCompatibility} from "@server/db";
|
import {
|
||||||
|
db,
|
||||||
|
resourceHeaderAuth,
|
||||||
|
resourceHeaderAuthExtendedCompatibility
|
||||||
|
} from "@server/db";
|
||||||
import {
|
import {
|
||||||
resources,
|
resources,
|
||||||
userResources,
|
userResources,
|
||||||
@@ -109,7 +113,8 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
|||||||
domainId: resources.domainId,
|
domainId: resources.domainId,
|
||||||
niceId: resources.niceId,
|
niceId: resources.niceId,
|
||||||
headerAuthId: resourceHeaderAuth.headerAuthId,
|
headerAuthId: resourceHeaderAuth.headerAuthId,
|
||||||
headerAuthExtendedCompatibilityId: resourceHeaderAuthExtendedCompatibility.headerAuthExtendedCompatibilityId,
|
headerAuthExtendedCompatibilityId:
|
||||||
|
resourceHeaderAuthExtendedCompatibility.headerAuthExtendedCompatibilityId,
|
||||||
targetId: targets.targetId,
|
targetId: targets.targetId,
|
||||||
targetIp: targets.ip,
|
targetIp: targets.ip,
|
||||||
targetPort: targets.port,
|
targetPort: targets.port,
|
||||||
@@ -133,7 +138,10 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
|||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuthExtendedCompatibility,
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
eq(resourceHeaderAuthExtendedCompatibility.resourceId, resources.resourceId)
|
eq(
|
||||||
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
|
resources.resourceId
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.leftJoin(targets, eq(targets.resourceId, resources.resourceId))
|
.leftJoin(targets, eq(targets.resourceId, resources.resourceId))
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import {Request, Response, NextFunction} from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import {z} from "zod";
|
import { z } from "zod";
|
||||||
import {db, resourceHeaderAuth, resourceHeaderAuthExtendedCompatibility} from "@server/db";
|
import {
|
||||||
import {eq} from "drizzle-orm";
|
db,
|
||||||
|
resourceHeaderAuth,
|
||||||
|
resourceHeaderAuthExtendedCompatibility
|
||||||
|
} from "@server/db";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import {fromError} from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import {response} from "@server/lib/response";
|
import { response } from "@server/lib/response";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import {hashPassword} from "@server/auth/password";
|
import { hashPassword } from "@server/auth/password";
|
||||||
import {OpenAPITags, registry} from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const setResourceAuthMethodsParamsSchema = z.object({
|
const setResourceAuthMethodsParamsSchema = z.object({
|
||||||
resourceId: z.string().transform(Number).pipe(z.int().positive())
|
resourceId: z.string().transform(Number).pipe(z.int().positive())
|
||||||
@@ -67,29 +71,40 @@ export async function setResourceHeaderAuth(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {resourceId} = parsedParams.data;
|
const { resourceId } = parsedParams.data;
|
||||||
const {user, password, extendedCompatibility} = parsedBody.data;
|
const { user, password, extendedCompatibility } = parsedBody.data;
|
||||||
|
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
await trx
|
await trx
|
||||||
.delete(resourceHeaderAuth)
|
.delete(resourceHeaderAuth)
|
||||||
.where(eq(resourceHeaderAuth.resourceId, resourceId));
|
.where(eq(resourceHeaderAuth.resourceId, resourceId));
|
||||||
await trx.delete(resourceHeaderAuthExtendedCompatibility).where(eq(resourceHeaderAuthExtendedCompatibility.resourceId, resourceId));
|
await trx
|
||||||
|
.delete(resourceHeaderAuthExtendedCompatibility)
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
|
resourceId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if (user && password && extendedCompatibility !== null) {
|
if (user && password && extendedCompatibility !== null) {
|
||||||
const headerAuthHash = await hashPassword(Buffer.from(`${user}:${password}`).toString("base64"));
|
const headerAuthHash = await hashPassword(
|
||||||
|
Buffer.from(`${user}:${password}`).toString("base64")
|
||||||
|
);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
trx
|
trx
|
||||||
.insert(resourceHeaderAuth)
|
.insert(resourceHeaderAuth)
|
||||||
.values({resourceId, headerAuthHash}),
|
.values({ resourceId, headerAuthHash }),
|
||||||
trx
|
trx
|
||||||
.insert(resourceHeaderAuthExtendedCompatibility)
|
.insert(resourceHeaderAuthExtendedCompatibility)
|
||||||
.values({resourceId, extendedCompatibilityIsActivated: extendedCompatibility})
|
.values({
|
||||||
|
resourceId,
|
||||||
|
extendedCompatibilityIsActivated:
|
||||||
|
extendedCompatibility
|
||||||
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ export type GetMaintenanceInfoResponse = {
|
|||||||
maintenanceTitle: string | null;
|
maintenanceTitle: string | null;
|
||||||
maintenanceMessage: string | null;
|
maintenanceMessage: string | null;
|
||||||
maintenanceEstimatedTime: string | null;
|
maintenanceEstimatedTime: string | null;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -343,7 +343,9 @@ async function updateHttpResource(
|
|||||||
|
|
||||||
const isLicensed = await isLicensedOrSubscribed(resource.orgId);
|
const isLicensed = await isLicensedOrSubscribed(resource.orgId);
|
||||||
if (build == "enterprise" && !isLicensed) {
|
if (build == "enterprise" && !isLicensed) {
|
||||||
logger.warn("Server is not licensed! Clearing set maintenance screen values");
|
logger.warn(
|
||||||
|
"Server is not licensed! Clearing set maintenance screen values"
|
||||||
|
);
|
||||||
// null the maintenance mode fields if not licensed
|
// null the maintenance mode fields if not licensed
|
||||||
updateData.maintenanceModeEnabled = undefined;
|
updateData.maintenanceModeEnabled = undefined;
|
||||||
updateData.maintenanceModeType = undefined;
|
updateData.maintenanceModeType = undefined;
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import {
|
|||||||
userSiteResources
|
userSiteResources
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import { getUniqueSiteResourceName } from "@server/db/names";
|
import { getUniqueSiteResourceName } from "@server/db/names";
|
||||||
import { getNextAvailableAliasAddress, isIpInCidr, portRangeStringSchema } from "@server/lib/ip";
|
import {
|
||||||
|
getNextAvailableAliasAddress,
|
||||||
|
isIpInCidr,
|
||||||
|
portRangeStringSchema
|
||||||
|
} from "@server/lib/ip";
|
||||||
import { rebuildClientAssociationsFromSiteResource } from "@server/lib/rebuildClientAssociations";
|
import { rebuildClientAssociationsFromSiteResource } from "@server/lib/rebuildClientAssociations";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
@@ -69,7 +73,10 @@ const createSiteResourceSchema = z
|
|||||||
const domainRegex =
|
const domainRegex =
|
||||||
/^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
|
/^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
|
||||||
const isValidDomain = domainRegex.test(data.destination);
|
const isValidDomain = domainRegex.test(data.destination);
|
||||||
const isValidAlias = data.alias !== undefined && data.alias !== null && data.alias.trim() !== "";
|
const isValidAlias =
|
||||||
|
data.alias !== undefined &&
|
||||||
|
data.alias !== null &&
|
||||||
|
data.alias.trim() !== "";
|
||||||
|
|
||||||
return isValidDomain && isValidAlias; // require the alias to be set in the case of domain
|
return isValidDomain && isValidAlias; // require the alias to be set in the case of domain
|
||||||
}
|
}
|
||||||
@@ -182,7 +189,9 @@ export async function createSiteResource(
|
|||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (!org) {
|
if (!org) {
|
||||||
return next(createHttpError(HttpCode.NOT_FOUND, "Organization not found"));
|
return next(
|
||||||
|
createHttpError(HttpCode.NOT_FOUND, "Organization not found")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!org.subnet || !org.utilitySubnet) {
|
if (!org.subnet || !org.utilitySubnet) {
|
||||||
@@ -195,10 +204,13 @@ export async function createSiteResource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only check if destination is an IP address
|
// Only check if destination is an IP address
|
||||||
const isIp = z.union([z.ipv4(), z.ipv6()]).safeParse(destination).success;
|
const isIp = z
|
||||||
|
.union([z.ipv4(), z.ipv6()])
|
||||||
|
.safeParse(destination).success;
|
||||||
if (
|
if (
|
||||||
isIp &&
|
isIp &&
|
||||||
(isIpInCidr(destination, org.subnet) || isIpInCidr(destination, org.utilitySubnet))
|
(isIpInCidr(destination, org.subnet) ||
|
||||||
|
isIpInCidr(destination, org.utilitySubnet))
|
||||||
) {
|
) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
|
|||||||
@@ -88,9 +88,7 @@ export async function deleteSiteResource(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.info(`Deleted site resource ${siteResourceId}`);
|
||||||
`Deleted site resource ${siteResourceId}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: { message: "Site resource deleted successfully" },
|
data: { message: "Site resource deleted successfully" },
|
||||||
|
|||||||
@@ -204,7 +204,9 @@ export async function updateSiteResource(
|
|||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (!org) {
|
if (!org) {
|
||||||
return next(createHttpError(HttpCode.NOT_FOUND, "Organization not found"));
|
return next(
|
||||||
|
createHttpError(HttpCode.NOT_FOUND, "Organization not found")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!org.subnet || !org.utilitySubnet) {
|
if (!org.subnet || !org.utilitySubnet) {
|
||||||
@@ -217,10 +219,13 @@ export async function updateSiteResource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only check if destination is an IP address
|
// Only check if destination is an IP address
|
||||||
const isIp = z.union([z.ipv4(), z.ipv6()]).safeParse(destination).success;
|
const isIp = z
|
||||||
|
.union([z.ipv4(), z.ipv6()])
|
||||||
|
.safeParse(destination).success;
|
||||||
if (
|
if (
|
||||||
isIp &&
|
isIp &&
|
||||||
(isIpInCidr(destination!, org.subnet) || isIpInCidr(destination!, org.utilitySubnet))
|
(isIpInCidr(destination!, org.subnet) ||
|
||||||
|
isIpInCidr(destination!, org.utilitySubnet))
|
||||||
) {
|
) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
@@ -295,7 +300,7 @@ export async function updateSiteResource(
|
|||||||
const [insertedSiteResource] = await trx
|
const [insertedSiteResource] = await trx
|
||||||
.insert(siteResources)
|
.insert(siteResources)
|
||||||
.values({
|
.values({
|
||||||
...existingSiteResource,
|
...existingSiteResource
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
@@ -517,9 +522,14 @@ export async function handleMessagingForUpdatedSiteResource(
|
|||||||
site: { siteId: number; orgId: string },
|
site: { siteId: number; orgId: string },
|
||||||
trx: Transaction
|
trx: Transaction
|
||||||
) {
|
) {
|
||||||
|
logger.debug(
|
||||||
logger.debug("handleMessagingForUpdatedSiteResource: existingSiteResource is: ", existingSiteResource);
|
"handleMessagingForUpdatedSiteResource: existingSiteResource is: ",
|
||||||
logger.debug("handleMessagingForUpdatedSiteResource: updatedSiteResource is: ", updatedSiteResource);
|
existingSiteResource
|
||||||
|
);
|
||||||
|
logger.debug(
|
||||||
|
"handleMessagingForUpdatedSiteResource: updatedSiteResource is: ",
|
||||||
|
updatedSiteResource
|
||||||
|
);
|
||||||
|
|
||||||
const { mergedAllClients } =
|
const { mergedAllClients } =
|
||||||
await rebuildClientAssociationsFromSiteResource(
|
await rebuildClientAssociationsFromSiteResource(
|
||||||
|
|||||||
@@ -71,7 +71,9 @@ export default async function GeneralSettingsPage({
|
|||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<OrgInfoCard />
|
<OrgInfoCard />
|
||||||
<HorizontalTabs items={navItems}>{children}</HorizontalTabs>
|
<HorizontalTabs items={navItems}>
|
||||||
|
{children}
|
||||||
|
</HorizontalTabs>
|
||||||
</div>
|
</div>
|
||||||
</OrgUserProvider>
|
</OrgUserProvider>
|
||||||
</OrgProvider>
|
</OrgProvider>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default async function ClientResourcesPage(
|
|||||||
niceId: siteResource.niceId,
|
niceId: siteResource.niceId,
|
||||||
tcpPortRangeString: siteResource.tcpPortRangeString || null,
|
tcpPortRangeString: siteResource.tcpPortRangeString || null,
|
||||||
udpPortRangeString: siteResource.udpPortRangeString || null,
|
udpPortRangeString: siteResource.udpPortRangeString || null,
|
||||||
disableIcmp: siteResource.disableIcmp || false,
|
disableIcmp: siteResource.disableIcmp || false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -240,13 +240,7 @@ export default function ResourceAuthenticationPage() {
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
hasInitializedRef.current = true;
|
hasInitializedRef.current = true;
|
||||||
}, [
|
}, [pageLoading, resourceRoles, resourceUsers, whitelist, orgIdps]);
|
||||||
pageLoading,
|
|
||||||
resourceRoles,
|
|
||||||
resourceUsers,
|
|
||||||
whitelist,
|
|
||||||
orgIdps
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [, submitUserRolesForm, loadingSaveUsersRoles] = useActionState(
|
const [, submitUserRolesForm, loadingSaveUsersRoles] = useActionState(
|
||||||
onSubmitUsersRoles,
|
onSubmitUsersRoles,
|
||||||
@@ -602,9 +596,7 @@ export default function ResourceAuthenticationPage() {
|
|||||||
{ssoEnabled && allIdps.length > 0 && (
|
{ssoEnabled && allIdps.length > 0 && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">
|
<label className="text-sm font-medium">
|
||||||
{t(
|
{t("defaultIdentityProvider")}
|
||||||
"defaultIdentityProvider"
|
|
||||||
)}
|
|
||||||
</label>
|
</label>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
|
|||||||
@@ -118,8 +118,7 @@ export default function ResourceRules(props: {
|
|||||||
const [countrySelectValue, setCountrySelectValue] = useState("");
|
const [countrySelectValue, setCountrySelectValue] = useState("");
|
||||||
const [openAddRuleCountrySelect, setOpenAddRuleCountrySelect] =
|
const [openAddRuleCountrySelect, setOpenAddRuleCountrySelect] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [openAddRuleAsnSelect, setOpenAddRuleAsnSelect] =
|
const [openAddRuleAsnSelect, setOpenAddRuleAsnSelect] = useState(false);
|
||||||
useState(false);
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const { env } = useEnvContext();
|
const { env } = useEnvContext();
|
||||||
@@ -181,7 +180,7 @@ export default function ResourceRules(props: {
|
|||||||
// Normalize ASN value
|
// Normalize ASN value
|
||||||
if (data.match === "ASN") {
|
if (data.match === "ASN") {
|
||||||
const originalValue = data.value.toUpperCase();
|
const originalValue = data.value.toUpperCase();
|
||||||
|
|
||||||
// Handle special "ALL" case
|
// Handle special "ALL" case
|
||||||
if (originalValue === "ALL" || originalValue === "AS0") {
|
if (originalValue === "ALL" || originalValue === "AS0") {
|
||||||
data.value = "ALL";
|
data.value = "ALL";
|
||||||
@@ -542,7 +541,11 @@ export default function ResourceRules(props: {
|
|||||||
updateRule(row.original.ruleId, {
|
updateRule(row.original.ruleId, {
|
||||||
match: value,
|
match: value,
|
||||||
value:
|
value:
|
||||||
value === "COUNTRY" ? "US" : value === "ASN" ? "AS15169" : row.original.value
|
value === "COUNTRY"
|
||||||
|
? "US"
|
||||||
|
: value === "ASN"
|
||||||
|
? "AS15169"
|
||||||
|
: row.original.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -559,9 +562,7 @@ export default function ResourceRules(props: {
|
|||||||
</SelectItem>
|
</SelectItem>
|
||||||
)}
|
)}
|
||||||
{isMaxmindAsnAvailable && (
|
{isMaxmindAsnAvailable && (
|
||||||
<SelectItem value="ASN">
|
<SelectItem value="ASN">{RuleMatch.ASN}</SelectItem>
|
||||||
{RuleMatch.ASN}
|
|
||||||
</SelectItem>
|
|
||||||
)}
|
)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
@@ -654,9 +655,7 @@ export default function ResourceRules(props: {
|
|||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="min-w-[200px] p-0">
|
<PopoverContent className="min-w-[200px] p-0">
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput
|
<CommandInput placeholder="Search ASNs or enter custom..." />
|
||||||
placeholder="Search ASNs or enter custom..."
|
|
||||||
/>
|
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>
|
<CommandEmpty>
|
||||||
No ASN found. Enter a custom ASN below.
|
No ASN found. Enter a custom ASN below.
|
||||||
@@ -665,7 +664,9 @@ export default function ResourceRules(props: {
|
|||||||
{MAJOR_ASNS.map((asn) => (
|
{MAJOR_ASNS.map((asn) => (
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={asn.code}
|
key={asn.code}
|
||||||
value={asn.name + " " + asn.code}
|
value={
|
||||||
|
asn.name + " " + asn.code
|
||||||
|
}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
updateRule(
|
updateRule(
|
||||||
row.original.ruleId,
|
row.original.ruleId,
|
||||||
@@ -1056,8 +1057,8 @@ export default function ResourceRules(props: {
|
|||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
) : addRuleForm.watch(
|
) : addRuleForm.watch(
|
||||||
"match"
|
"match"
|
||||||
) === "ASN" ? (
|
) === "ASN" ? (
|
||||||
<Popover
|
<Popover
|
||||||
open={
|
open={
|
||||||
openAddRuleAsnSelect
|
openAddRuleAsnSelect
|
||||||
@@ -1086,21 +1087,27 @@ export default function ResourceRules(props: {
|
|||||||
field.value
|
field.value
|
||||||
)
|
)
|
||||||
?.name +
|
?.name +
|
||||||
" (" +
|
" (" +
|
||||||
field.value +
|
field.value +
|
||||||
")" || field.value
|
")" ||
|
||||||
|
field.value
|
||||||
: "Select ASN"}
|
: "Select ASN"}
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-full p-0">
|
<PopoverContent className="w-full p-0">
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput
|
<CommandInput placeholder="Search ASNs or enter custom..." />
|
||||||
placeholder="Search ASNs or enter custom..."
|
|
||||||
/>
|
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>
|
<CommandEmpty>
|
||||||
No ASN found. Use the custom input below.
|
No
|
||||||
|
ASN
|
||||||
|
found.
|
||||||
|
Use
|
||||||
|
the
|
||||||
|
custom
|
||||||
|
input
|
||||||
|
below.
|
||||||
</CommandEmpty>
|
</CommandEmpty>
|
||||||
<CommandGroup>
|
<CommandGroup>
|
||||||
{MAJOR_ASNS.map(
|
{MAJOR_ASNS.map(
|
||||||
@@ -1112,7 +1119,9 @@ export default function ResourceRules(props: {
|
|||||||
asn.code
|
asn.code
|
||||||
}
|
}
|
||||||
value={
|
value={
|
||||||
asn.name + " " + asn.code
|
asn.name +
|
||||||
|
" " +
|
||||||
|
asn.code
|
||||||
}
|
}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
field.onChange(
|
field.onChange(
|
||||||
@@ -1138,6 +1147,7 @@ export default function ResourceRules(props: {
|
|||||||
{
|
{
|
||||||
asn.code
|
asn.code
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
)
|
)
|
||||||
@@ -1148,14 +1158,32 @@ export default function ResourceRules(props: {
|
|||||||
<div className="border-t p-2">
|
<div className="border-t p-2">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Enter custom ASN (e.g., AS15169)"
|
placeholder="Enter custom ASN (e.g., AS15169)"
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(
|
||||||
if (e.key === "Enter") {
|
e
|
||||||
const value = e.currentTarget.value
|
) => {
|
||||||
.toUpperCase()
|
if (
|
||||||
.replace(/^AS/, "");
|
e.key ===
|
||||||
if (/^\d+$/.test(value)) {
|
"Enter"
|
||||||
field.onChange("AS" + value);
|
) {
|
||||||
setOpenAddRuleAsnSelect(false);
|
const value =
|
||||||
|
e.currentTarget.value
|
||||||
|
.toUpperCase()
|
||||||
|
.replace(
|
||||||
|
/^AS/,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
/^\d+$/.test(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
field.onChange(
|
||||||
|
"AS" +
|
||||||
|
value
|
||||||
|
);
|
||||||
|
setOpenAddRuleAsnSelect(
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -756,7 +756,9 @@ WantedBy=default.target`
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="md:col-start-1 md:col-span-2">
|
<FormItem className="md:col-start-1 md:col-span-2">
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{t("siteAddress")}
|
{t(
|
||||||
|
"siteAddress"
|
||||||
|
)}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -66,4 +66,3 @@ export const ClientDownloadBanner = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default ClientDownloadBanner;
|
export default ClientDownloadBanner;
|
||||||
|
|
||||||
|
|||||||
@@ -99,14 +99,12 @@ export default function ClientResourcesTable({
|
|||||||
siteId: number
|
siteId: number
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
await api
|
await api.delete(`/site-resource/${resourceId}`).then(() => {
|
||||||
.delete(`/site-resource/${resourceId}`)
|
startTransition(() => {
|
||||||
.then(() => {
|
router.refresh();
|
||||||
startTransition(() => {
|
setIsDeleteModalOpen(false);
|
||||||
router.refresh();
|
|
||||||
setIsDeleteModalOpen(false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(t("resourceErrorDelete"), e);
|
console.error(t("resourceErrorDelete"), e);
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -87,7 +87,12 @@ const isValidPortRangeString = (val: string | undefined | null): boolean => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startPort < 1 || startPort > 65535 || endPort < 1 || endPort > 65535) {
|
if (
|
||||||
|
startPort < 1 ||
|
||||||
|
startPort > 65535 ||
|
||||||
|
endPort < 1 ||
|
||||||
|
endPort > 65535
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +136,10 @@ const getPortModeFromString = (val: string | undefined | null): PortMode => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Helper to get the port string for API from mode and custom value
|
// Helper to get the port string for API from mode and custom value
|
||||||
const getPortStringFromMode = (mode: PortMode, customValue: string): string | undefined => {
|
const getPortStringFromMode = (
|
||||||
|
mode: PortMode,
|
||||||
|
customValue: string
|
||||||
|
): string | undefined => {
|
||||||
if (mode === "all") return "*";
|
if (mode === "all") return "*";
|
||||||
if (mode === "blocked") return "";
|
if (mode === "blocked") return "";
|
||||||
return customValue;
|
return customValue;
|
||||||
@@ -1097,8 +1105,7 @@ export default function CreateInternalResourceDialog({
|
|||||||
size="sm"
|
size="sm"
|
||||||
tags={
|
tags={
|
||||||
form.getValues()
|
form.getValues()
|
||||||
.roles ||
|
.roles || []
|
||||||
[]
|
|
||||||
}
|
}
|
||||||
setTags={(
|
setTags={(
|
||||||
newRoles
|
newRoles
|
||||||
@@ -1154,8 +1161,7 @@ export default function CreateInternalResourceDialog({
|
|||||||
)}
|
)}
|
||||||
tags={
|
tags={
|
||||||
form.getValues()
|
form.getValues()
|
||||||
.users ||
|
.users || []
|
||||||
[]
|
|
||||||
}
|
}
|
||||||
size="sm"
|
size="sm"
|
||||||
setTags={(
|
setTags={(
|
||||||
@@ -1245,9 +1251,7 @@ export default function CreateInternalResourceDialog({
|
|||||||
restrictTagsToAutocompleteOptions={
|
restrictTagsToAutocompleteOptions={
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
sortTags={
|
sortTags={true}
|
||||||
true
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|||||||
@@ -95,4 +95,3 @@ export const DismissableBanner = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default DismissableBanner;
|
export default DismissableBanner;
|
||||||
|
|
||||||
|
|||||||
@@ -501,13 +501,19 @@ export default function EditInternalResourceDialog({
|
|||||||
// ]);
|
// ]);
|
||||||
|
|
||||||
await queryClient.invalidateQueries(
|
await queryClient.invalidateQueries(
|
||||||
resourceQueries.siteResourceRoles({ siteResourceId: resource.id })
|
resourceQueries.siteResourceRoles({
|
||||||
|
siteResourceId: resource.id
|
||||||
|
})
|
||||||
);
|
);
|
||||||
await queryClient.invalidateQueries(
|
await queryClient.invalidateQueries(
|
||||||
resourceQueries.siteResourceUsers({ siteResourceId: resource.id })
|
resourceQueries.siteResourceUsers({
|
||||||
|
siteResourceId: resource.id
|
||||||
|
})
|
||||||
);
|
);
|
||||||
await queryClient.invalidateQueries(
|
await queryClient.invalidateQueries(
|
||||||
resourceQueries.siteResourceClients({ siteResourceId: resource.id })
|
resourceQueries.siteResourceClients({
|
||||||
|
siteResourceId: resource.id
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ export default function ExitNodesTable({
|
|||||||
isRefreshing={isRefreshing}
|
isRefreshing={isRefreshing}
|
||||||
columnVisibility={{
|
columnVisibility={{
|
||||||
type: false,
|
type: false,
|
||||||
address: false,
|
address: false
|
||||||
}}
|
}}
|
||||||
enableColumnVisibility={true}
|
enableColumnVisibility={true}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -116,10 +116,12 @@ export function LayoutSidebar({
|
|||||||
isCollapsed={isSidebarCollapsed}
|
isCollapsed={isSidebarCollapsed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={cn(
|
<div
|
||||||
"w-full border-b border-border",
|
className={cn(
|
||||||
isSidebarCollapsed && "mb-2"
|
"w-full border-b border-border",
|
||||||
)} />
|
isSidebarCollapsed && "mb-2"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<div className="flex-1 overflow-y-auto relative">
|
<div className="flex-1 overflow-y-auto relative">
|
||||||
<div className="px-2 pt-1">
|
<div className="px-2 pt-1">
|
||||||
{!isAdminPage && user.serverAdmin && (
|
{!isAdminPage && user.serverAdmin && (
|
||||||
|
|||||||
@@ -120,7 +120,9 @@ export default function LoginForm({
|
|||||||
const focusInput = () => {
|
const focusInput = () => {
|
||||||
// Try using the ref first
|
// Try using the ref first
|
||||||
if (otpContainerRef.current) {
|
if (otpContainerRef.current) {
|
||||||
const hiddenInput = otpContainerRef.current.querySelector('input') as HTMLInputElement;
|
const hiddenInput = otpContainerRef.current.querySelector(
|
||||||
|
"input"
|
||||||
|
) as HTMLInputElement;
|
||||||
if (hiddenInput) {
|
if (hiddenInput) {
|
||||||
hiddenInput.focus();
|
hiddenInput.focus();
|
||||||
return;
|
return;
|
||||||
@@ -128,17 +130,23 @@ export default function LoginForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: query the DOM
|
// Fallback: query the DOM
|
||||||
const otpContainer = document.querySelector('[data-slot="input-otp"]');
|
const otpContainer = document.querySelector(
|
||||||
|
'[data-slot="input-otp"]'
|
||||||
|
);
|
||||||
if (!otpContainer) return;
|
if (!otpContainer) return;
|
||||||
|
|
||||||
const hiddenInput = otpContainer.querySelector('input') as HTMLInputElement;
|
const hiddenInput = otpContainer.querySelector(
|
||||||
|
"input"
|
||||||
|
) as HTMLInputElement;
|
||||||
if (hiddenInput) {
|
if (hiddenInput) {
|
||||||
hiddenInput.focus();
|
hiddenInput.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last resort: click the first slot
|
// Last resort: click the first slot
|
||||||
const firstSlot = otpContainer.querySelector('[data-slot="input-otp-slot"]') as HTMLElement;
|
const firstSlot = otpContainer.querySelector(
|
||||||
|
'[data-slot="input-otp-slot"]'
|
||||||
|
) as HTMLElement;
|
||||||
if (firstSlot) {
|
if (firstSlot) {
|
||||||
firstSlot.click();
|
firstSlot.click();
|
||||||
}
|
}
|
||||||
@@ -508,7 +516,10 @@ export default function LoginForm({
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div ref={otpContainerRef} className="flex justify-center">
|
<div
|
||||||
|
ref={otpContainerRef}
|
||||||
|
className="flex justify-center"
|
||||||
|
>
|
||||||
<InputOTP
|
<InputOTP
|
||||||
maxLength={6}
|
maxLength={6}
|
||||||
{...field}
|
{...field}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ type MachineClientsBannerProps = {
|
|||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MachineClientsBanner = ({
|
export const MachineClientsBanner = ({ orgId }: MachineClientsBannerProps) => {
|
||||||
orgId
|
|
||||||
}: MachineClientsBannerProps) => {
|
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -57,4 +55,3 @@ export const MachineClientsBanner = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default MachineClientsBanner;
|
export default MachineClientsBanner;
|
||||||
|
|
||||||
|
|||||||
@@ -39,4 +39,3 @@ export default function OrgInfoCard({}: OrgInfoCardProps) {
|
|||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,4 +119,3 @@ export default async function OrgLoginPage({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,4 +51,3 @@ export const PrivateResourcesBanner = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default PrivateResourcesBanner;
|
export default PrivateResourcesBanner;
|
||||||
|
|
||||||
|
|||||||
@@ -81,10 +81,10 @@ export default function ProductUpdates({
|
|||||||
|
|
||||||
const showNewVersionPopup = Boolean(
|
const showNewVersionPopup = Boolean(
|
||||||
latestVersion &&
|
latestVersion &&
|
||||||
valid(latestVersion) &&
|
valid(latestVersion) &&
|
||||||
valid(currentVersion) &&
|
valid(currentVersion) &&
|
||||||
ignoredVersionUpdate !== latestVersion &&
|
ignoredVersionUpdate !== latestVersion &&
|
||||||
gt(latestVersion, currentVersion)
|
gt(latestVersion, currentVersion)
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredUpdates = data.updates.filter(
|
const filteredUpdates = data.updates.filter(
|
||||||
|
|||||||
@@ -20,4 +20,3 @@ export const ProxyResourcesBanner = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default ProxyResourcesBanner;
|
export default ProxyResourcesBanner;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {Button} from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -9,12 +9,12 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage
|
FormMessage
|
||||||
} from "@app/components/ui/form";
|
} from "@app/components/ui/form";
|
||||||
import {Input} from "@app/components/ui/input";
|
import { Input } from "@app/components/ui/input";
|
||||||
import {toast} from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import {zodResolver} from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {useForm} from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import {z} from "zod";
|
import { z } from "zod";
|
||||||
import {
|
import {
|
||||||
Credenza,
|
Credenza,
|
||||||
CredenzaBody,
|
CredenzaBody,
|
||||||
@@ -25,14 +25,14 @@ import {
|
|||||||
CredenzaHeader,
|
CredenzaHeader,
|
||||||
CredenzaTitle
|
CredenzaTitle
|
||||||
} from "@app/components/Credenza";
|
} from "@app/components/Credenza";
|
||||||
import {formatAxiosError} from "@app/lib/api";
|
import { formatAxiosError } from "@app/lib/api";
|
||||||
import {AxiosResponse} from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import {Resource} from "@server/db";
|
import { Resource } from "@server/db";
|
||||||
import {createApiClient} from "@app/lib/api";
|
import { createApiClient } from "@app/lib/api";
|
||||||
import {useEnvContext} from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import {useTranslations} from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import {SwitchInput} from "@/components/SwitchInput";
|
import { SwitchInput } from "@/components/SwitchInput";
|
||||||
import {InfoPopup} from "@/components/ui/info-popup";
|
import { InfoPopup } from "@/components/ui/info-popup";
|
||||||
|
|
||||||
const setHeaderAuthFormSchema = z.object({
|
const setHeaderAuthFormSchema = z.object({
|
||||||
user: z.string().min(4).max(100),
|
user: z.string().min(4).max(100),
|
||||||
@@ -56,11 +56,11 @@ type SetHeaderAuthFormProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function SetResourceHeaderAuthForm({
|
export default function SetResourceHeaderAuthForm({
|
||||||
open,
|
open,
|
||||||
setOpen,
|
setOpen,
|
||||||
resourceId,
|
resourceId,
|
||||||
onSetHeaderAuth
|
onSetHeaderAuth
|
||||||
}: SetHeaderAuthFormProps) {
|
}: SetHeaderAuthFormProps) {
|
||||||
const api = createApiClient(useEnvContext());
|
const api = createApiClient(useEnvContext());
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
@@ -82,11 +82,14 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
async function onSubmit(data: SetHeaderAuthFormValues) {
|
async function onSubmit(data: SetHeaderAuthFormValues) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/header-auth`, {
|
api.post<AxiosResponse<Resource>>(
|
||||||
user: data.user,
|
`/resource/${resourceId}/header-auth`,
|
||||||
password: data.password,
|
{
|
||||||
extendedCompatibility: data.extendedCompatibility
|
user: data.user,
|
||||||
})
|
password: data.password,
|
||||||
|
extendedCompatibility: data.extendedCompatibility
|
||||||
|
}
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast({
|
toast({
|
||||||
title: t("resourceHeaderAuthSetup"),
|
title: t("resourceHeaderAuthSetup"),
|
||||||
@@ -100,10 +103,10 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: t('resourceErrorHeaderAuthSetup'),
|
title: t("resourceErrorHeaderAuthSetup"),
|
||||||
description: formatAxiosError(
|
description: formatAxiosError(
|
||||||
e,
|
e,
|
||||||
t('resourceErrorHeaderAuthSetupDescription')
|
t("resourceErrorHeaderAuthSetupDescription")
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -139,7 +142,7 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="user"
|
name="user"
|
||||||
render={({field}) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t("user")}</FormLabel>
|
<FormLabel>{t("user")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
@@ -149,14 +152,14 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage/>
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="password"
|
name="password"
|
||||||
render={({field}) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
{t("password")}
|
{t("password")}
|
||||||
@@ -168,25 +171,31 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage/>
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="extendedCompatibility"
|
name="extendedCompatibility"
|
||||||
render={({field}) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SwitchInput
|
<SwitchInput
|
||||||
id="header-auth-compatibility-toggle"
|
id="header-auth-compatibility-toggle"
|
||||||
label={t("headerAuthCompatibility")}
|
label={t(
|
||||||
info={t('headerAuthCompatibilityInfo')}
|
"headerAuthCompatibility"
|
||||||
|
)}
|
||||||
|
info={t(
|
||||||
|
"headerAuthCompatibilityInfo"
|
||||||
|
)}
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={
|
||||||
|
field.onChange
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage/>
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -91,10 +91,10 @@ export default function SetResourcePasswordForm({
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: t('resourceErrorPasswordSetup'),
|
title: t("resourceErrorPasswordSetup"),
|
||||||
description: formatAxiosError(
|
description: formatAxiosError(
|
||||||
e,
|
e,
|
||||||
t('resourceErrorPasswordSetupDescription')
|
t("resourceErrorPasswordSetupDescription")
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -97,10 +97,10 @@ export default function SetResourcePincodeForm({
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: t('resourceErrorPincodeSetup'),
|
title: t("resourceErrorPincodeSetup"),
|
||||||
description: formatAxiosError(
|
description: formatAxiosError(
|
||||||
e,
|
e,
|
||||||
t('resourceErrorPincodeSetupDescription')
|
t("resourceErrorPincodeSetupDescription")
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -37,4 +37,3 @@ export const SitesBanner = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default SitesBanner;
|
export default SitesBanner;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {Switch} from "./ui/switch";
|
import { Switch } from "./ui/switch";
|
||||||
import {Label} from "./ui/label";
|
import { Label } from "./ui/label";
|
||||||
import {Button} from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {Info} from "lucide-react";
|
import { Info } from "lucide-react";
|
||||||
import {info} from "winston";
|
import { info } from "winston";
|
||||||
import {Popover, PopoverContent, PopoverTrigger} from "@/components/ui/popover";
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
|
||||||
interface SwitchComponentProps {
|
interface SwitchComponentProps {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -18,22 +22,22 @@ interface SwitchComponentProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SwitchInput({
|
export function SwitchInput({
|
||||||
id,
|
id,
|
||||||
label,
|
label,
|
||||||
description,
|
description,
|
||||||
info,
|
info,
|
||||||
disabled,
|
disabled,
|
||||||
checked,
|
checked,
|
||||||
defaultChecked = false,
|
defaultChecked = false,
|
||||||
onCheckedChange
|
onCheckedChange
|
||||||
}: SwitchComponentProps) {
|
}: SwitchComponentProps) {
|
||||||
const defaultTrigger = (
|
const defaultTrigger = (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="h-6 w-6 rounded-full p-0"
|
className="h-6 w-6 rounded-full p-0"
|
||||||
>
|
>
|
||||||
<Info className="h-4 w-4"/>
|
<Info className="h-4 w-4" />
|
||||||
<span className="sr-only">Show info</span>
|
<span className="sr-only">Show info</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@@ -49,18 +53,20 @@ export function SwitchInput({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
{label && <Label htmlFor={id}>{label}</Label>}
|
{label && <Label htmlFor={id}>{label}</Label>}
|
||||||
{info && <Popover>
|
{info && (
|
||||||
<PopoverTrigger asChild>
|
<Popover>
|
||||||
{defaultTrigger}
|
<PopoverTrigger asChild>
|
||||||
</PopoverTrigger>
|
{defaultTrigger}
|
||||||
<PopoverContent className="w-80">
|
</PopoverTrigger>
|
||||||
{info && (
|
<PopoverContent className="w-80">
|
||||||
<p className="text-sm text-muted-foreground">
|
{info && (
|
||||||
{info}
|
<p className="text-sm text-muted-foreground">
|
||||||
</p>
|
{info}
|
||||||
)}
|
</p>
|
||||||
</PopoverContent>
|
)}
|
||||||
</Popover>}
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{description && (
|
{description && (
|
||||||
<span className="text-muted-foreground text-sm">
|
<span className="text-muted-foreground text-sm">
|
||||||
|
|||||||
@@ -276,14 +276,15 @@ function AuthPageSettings({
|
|||||||
<>
|
<>
|
||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
<SettingsSectionHeader>
|
<SettingsSectionHeader>
|
||||||
<SettingsSectionTitle>{t("customDomain")}</SettingsSectionTitle>
|
<SettingsSectionTitle>
|
||||||
|
{t("customDomain")}
|
||||||
|
</SettingsSectionTitle>
|
||||||
<SettingsSectionDescription>
|
<SettingsSectionDescription>
|
||||||
{t("authPageDescription")}
|
{t("authPageDescription")}
|
||||||
</SettingsSectionDescription>
|
</SettingsSectionDescription>
|
||||||
</SettingsSectionHeader>
|
</SettingsSectionHeader>
|
||||||
<SettingsSectionBody>
|
<SettingsSectionBody>
|
||||||
<SettingsSectionForm>
|
<SettingsSectionForm>
|
||||||
|
|
||||||
<PaidFeaturesAlert />
|
<PaidFeaturesAlert />
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
|
|||||||
@@ -58,12 +58,7 @@ export default function IdpLoginButtons({
|
|||||||
|
|
||||||
let redirectToUrl: string | undefined;
|
let redirectToUrl: string | undefined;
|
||||||
try {
|
try {
|
||||||
console.log(
|
console.log("generating", idpId, redirect || "/", orgId);
|
||||||
"generating",
|
|
||||||
idpId,
|
|
||||||
redirect || "/",
|
|
||||||
orgId
|
|
||||||
);
|
|
||||||
const safeRedirect = cleanRedirect(redirect || "/");
|
const safeRedirect = cleanRedirect(redirect || "/");
|
||||||
const response = await generateOidcUrlProxy(
|
const response = await generateOidcUrlProxy(
|
||||||
idpId,
|
idpId,
|
||||||
|
|||||||
@@ -288,7 +288,10 @@ export function DataTable<TData, TValue>({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (persistPageSize && pagination.pageSize !== pageSize) {
|
if (persistPageSize && pagination.pageSize !== pageSize) {
|
||||||
// Only store if user has actually changed it from initial value
|
// Only store if user has actually changed it from initial value
|
||||||
if (hasUserChangedPageSize.current && pagination.pageSize !== initialPageSize.current) {
|
if (
|
||||||
|
hasUserChangedPageSize.current &&
|
||||||
|
pagination.pageSize !== initialPageSize.current
|
||||||
|
) {
|
||||||
setStoredPageSize(pagination.pageSize, tableId);
|
setStoredPageSize(pagination.pageSize, tableId);
|
||||||
}
|
}
|
||||||
setPageSize(pagination.pageSize);
|
setPageSize(pagination.pageSize);
|
||||||
@@ -298,7 +301,9 @@ export function DataTable<TData, TValue>({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Persist column visibility to localStorage when it changes (but not on initial mount)
|
// Persist column visibility to localStorage when it changes (but not on initial mount)
|
||||||
if (shouldPersistColumnVisibility) {
|
if (shouldPersistColumnVisibility) {
|
||||||
const hasChanged = JSON.stringify(columnVisibility) !== JSON.stringify(initialColumnVisibilityState.current);
|
const hasChanged =
|
||||||
|
JSON.stringify(columnVisibility) !==
|
||||||
|
JSON.stringify(initialColumnVisibilityState.current);
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
// Mark as user-initiated change and persist
|
// Mark as user-initiated change and persist
|
||||||
hasUserChangedColumnVisibility.current = true;
|
hasUserChangedColumnVisibility.current = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user