mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-21 01:56:38 +00:00
Compare commits
1 Commits
private-si
...
1.16.2-s.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aed86ce4ba |
@@ -216,18 +216,12 @@ export const exitNodes = pgTable("exitNodes", {
|
|||||||
export const siteResources = pgTable("siteResources", {
|
export const siteResources = pgTable("siteResources", {
|
||||||
// this is for the clients
|
// this is for the clients
|
||||||
siteResourceId: serial("siteResourceId").primaryKey(),
|
siteResourceId: serial("siteResourceId").primaryKey(),
|
||||||
|
siteId: integer("siteId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => sites.siteId, { onDelete: "cascade" }),
|
||||||
orgId: varchar("orgId")
|
orgId: varchar("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
networkId: integer("networkId").references(() => networks.networkId, {
|
|
||||||
onDelete: "set null"
|
|
||||||
}),
|
|
||||||
defaultNetworkId: integer("defaultNetworkId").references(
|
|
||||||
() => networks.networkId,
|
|
||||||
{
|
|
||||||
onDelete: "restrict"
|
|
||||||
}
|
|
||||||
),
|
|
||||||
niceId: varchar("niceId").notNull(),
|
niceId: varchar("niceId").notNull(),
|
||||||
name: varchar("name").notNull(),
|
name: varchar("name").notNull(),
|
||||||
mode: varchar("mode").$type<"host" | "cidr">().notNull(), // "host" | "cidr" | "port"
|
mode: varchar("mode").$type<"host" | "cidr">().notNull(), // "host" | "cidr" | "port"
|
||||||
@@ -247,32 +241,6 @@ export const siteResources = pgTable("siteResources", {
|
|||||||
.default("site")
|
.default("site")
|
||||||
});
|
});
|
||||||
|
|
||||||
export const networks = pgTable("networks", {
|
|
||||||
networkId: serial("networkId").primaryKey(),
|
|
||||||
niceId: text("niceId"),
|
|
||||||
name: text("name"),
|
|
||||||
scope: varchar("scope")
|
|
||||||
.$type<"global" | "resource">()
|
|
||||||
.notNull()
|
|
||||||
.default("global"),
|
|
||||||
orgId: varchar("orgId")
|
|
||||||
.references(() => orgs.orgId, {
|
|
||||||
onDelete: "cascade"
|
|
||||||
})
|
|
||||||
.notNull()
|
|
||||||
});
|
|
||||||
|
|
||||||
export const siteNetworks = pgTable("siteNetworks", {
|
|
||||||
siteId: integer("siteId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => sites.siteId, {
|
|
||||||
onDelete: "cascade"
|
|
||||||
}),
|
|
||||||
networkId: integer("networkId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => networks.networkId, { onDelete: "cascade" })
|
|
||||||
});
|
|
||||||
|
|
||||||
export const clientSiteResources = pgTable("clientSiteResources", {
|
export const clientSiteResources = pgTable("clientSiteResources", {
|
||||||
clientId: integer("clientId")
|
clientId: integer("clientId")
|
||||||
.notNull()
|
.notNull()
|
||||||
@@ -1106,4 +1074,3 @@ export type RequestAuditLog = InferSelectModel<typeof requestAuditLog>;
|
|||||||
export type RoundTripMessageTracker = InferSelectModel<
|
export type RoundTripMessageTracker = InferSelectModel<
|
||||||
typeof roundTripMessageTracker
|
typeof roundTripMessageTracker
|
||||||
>;
|
>;
|
||||||
export type Network = InferSelectModel<typeof networks>;
|
|
||||||
|
|||||||
@@ -82,9 +82,6 @@ export const sites = sqliteTable("sites", {
|
|||||||
exitNodeId: integer("exitNode").references(() => exitNodes.exitNodeId, {
|
exitNodeId: integer("exitNode").references(() => exitNodes.exitNodeId, {
|
||||||
onDelete: "set null"
|
onDelete: "set null"
|
||||||
}),
|
}),
|
||||||
networkId: integer("networkId").references(() => networks.networkId, {
|
|
||||||
onDelete: "set null"
|
|
||||||
}),
|
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
pubKey: text("pubKey"),
|
pubKey: text("pubKey"),
|
||||||
subnet: text("subnet"),
|
subnet: text("subnet"),
|
||||||
@@ -242,16 +239,12 @@ export const siteResources = sqliteTable("siteResources", {
|
|||||||
siteResourceId: integer("siteResourceId").primaryKey({
|
siteResourceId: integer("siteResourceId").primaryKey({
|
||||||
autoIncrement: true
|
autoIncrement: true
|
||||||
}),
|
}),
|
||||||
|
siteId: integer("siteId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => sites.siteId, { onDelete: "cascade" }),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
||||||
networkId: integer("networkId").references(() => networks.networkId, {
|
|
||||||
onDelete: "set null"
|
|
||||||
}),
|
|
||||||
defaultNetworkId: integer("defaultNetworkId").references(
|
|
||||||
() => networks.networkId,
|
|
||||||
{ onDelete: "restrict" }
|
|
||||||
),
|
|
||||||
niceId: text("niceId").notNull(),
|
niceId: text("niceId").notNull(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
mode: text("mode").$type<"host" | "cidr">().notNull(), // "host" | "cidr" | "port"
|
mode: text("mode").$type<"host" | "cidr">().notNull(), // "host" | "cidr" | "port"
|
||||||
@@ -273,30 +266,6 @@ export const siteResources = sqliteTable("siteResources", {
|
|||||||
.default("site")
|
.default("site")
|
||||||
});
|
});
|
||||||
|
|
||||||
export const networks = sqliteTable("networks", {
|
|
||||||
networkId: integer("networkId").primaryKey({ autoIncrement: true }),
|
|
||||||
niceId: text("niceId"),
|
|
||||||
name: text("name"),
|
|
||||||
scope: text("scope")
|
|
||||||
.$type<"global" | "resource">()
|
|
||||||
.notNull()
|
|
||||||
.default("global"),
|
|
||||||
orgId: text("orgId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" })
|
|
||||||
});
|
|
||||||
|
|
||||||
export const siteNetworks = sqliteTable("siteNetworks", {
|
|
||||||
siteId: integer("siteId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => sites.siteId, {
|
|
||||||
onDelete: "cascade"
|
|
||||||
}),
|
|
||||||
networkId: integer("networkId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => networks.networkId, { onDelete: "cascade" })
|
|
||||||
});
|
|
||||||
|
|
||||||
export const clientSiteResources = sqliteTable("clientSiteResources", {
|
export const clientSiteResources = sqliteTable("clientSiteResources", {
|
||||||
clientId: integer("clientId")
|
clientId: integer("clientId")
|
||||||
.notNull()
|
.notNull()
|
||||||
@@ -1189,7 +1158,6 @@ export type ApiKey = InferSelectModel<typeof apiKeys>;
|
|||||||
export type ApiKeyAction = InferSelectModel<typeof apiKeyActions>;
|
export type ApiKeyAction = InferSelectModel<typeof apiKeyActions>;
|
||||||
export type ApiKeyOrg = InferSelectModel<typeof apiKeyOrg>;
|
export type ApiKeyOrg = InferSelectModel<typeof apiKeyOrg>;
|
||||||
export type SiteResource = InferSelectModel<typeof siteResources>;
|
export type SiteResource = InferSelectModel<typeof siteResources>;
|
||||||
export type Network = InferSelectModel<typeof networks>;
|
|
||||||
export type OrgDomains = InferSelectModel<typeof orgDomains>;
|
export type OrgDomains = InferSelectModel<typeof orgDomains>;
|
||||||
export type SetupToken = InferSelectModel<typeof setupTokens>;
|
export type SetupToken = InferSelectModel<typeof setupTokens>;
|
||||||
export type HostMeta = InferSelectModel<typeof hostMeta>;
|
export type HostMeta = InferSelectModel<typeof hostMeta>;
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ export async function applyBlueprint({
|
|||||||
for (const result of clientResourcesResults) {
|
for (const result of clientResourcesResults) {
|
||||||
if (
|
if (
|
||||||
result.oldSiteResource &&
|
result.oldSiteResource &&
|
||||||
JSON.stringify(result.newSites?.sort()) !==
|
result.oldSiteResource.siteId !=
|
||||||
JSON.stringify(result.oldSites?.sort())
|
result.newSiteResource.siteId
|
||||||
) {
|
) {
|
||||||
// query existing associations
|
// query existing associations
|
||||||
const existingRoleIds = await trx
|
const existingRoleIds = await trx
|
||||||
@@ -222,46 +222,38 @@ export async function applyBlueprint({
|
|||||||
trx
|
trx
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let good = true;
|
const [newSite] = await trx
|
||||||
for (const newSite of result.newSites) {
|
.select()
|
||||||
const [site] = await trx
|
.from(sites)
|
||||||
.select()
|
.innerJoin(newts, eq(sites.siteId, newts.siteId))
|
||||||
.from(sites)
|
.where(
|
||||||
.innerJoin(newts, eq(sites.siteId, newts.siteId))
|
and(
|
||||||
.where(
|
eq(sites.siteId, result.newSiteResource.siteId),
|
||||||
and(
|
eq(sites.orgId, orgId),
|
||||||
eq(sites.siteId, newSite.siteId),
|
eq(sites.type, "newt"),
|
||||||
eq(sites.orgId, orgId),
|
isNotNull(sites.pubKey)
|
||||||
eq(sites.type, "newt"),
|
|
||||||
isNotNull(sites.pubKey)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.limit(1);
|
)
|
||||||
|
.limit(1);
|
||||||
if (!site) {
|
|
||||||
logger.debug(
|
|
||||||
`No newt sites found for client resource ${result.newSiteResource.siteResourceId}, skipping target update`
|
|
||||||
);
|
|
||||||
good = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!newSite) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Updating client resource ${result.newSiteResource.siteResourceId} on site ${newSite.siteId}`
|
`No newt site found for client resource ${result.newSiteResource.siteResourceId}, skipping target update`
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (!good) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`Updating client resource ${result.newSiteResource.siteResourceId} on site ${newSite.sites.siteId}`
|
||||||
|
);
|
||||||
|
|
||||||
await handleMessagingForUpdatedSiteResource(
|
await handleMessagingForUpdatedSiteResource(
|
||||||
result.oldSiteResource,
|
result.oldSiteResource,
|
||||||
result.newSiteResource,
|
result.newSiteResource,
|
||||||
result.newSites.map((site) => ({
|
{
|
||||||
siteId: site.siteId,
|
siteId: newSite.sites.siteId,
|
||||||
orgId: result.newSiteResource.orgId
|
orgId: newSite.sites.orgId
|
||||||
})),
|
},
|
||||||
trx
|
trx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,12 @@ import {
|
|||||||
clientSiteResources,
|
clientSiteResources,
|
||||||
roles,
|
roles,
|
||||||
roleSiteResources,
|
roleSiteResources,
|
||||||
Site,
|
|
||||||
SiteResource,
|
SiteResource,
|
||||||
siteNetworks,
|
|
||||||
siteResources,
|
siteResources,
|
||||||
Transaction,
|
Transaction,
|
||||||
userOrgs,
|
userOrgs,
|
||||||
users,
|
users,
|
||||||
userSiteResources,
|
userSiteResources
|
||||||
networks
|
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import { sites } from "@server/db";
|
import { sites } from "@server/db";
|
||||||
import { eq, and, ne, inArray, or } from "drizzle-orm";
|
import { eq, and, ne, inArray, or } from "drizzle-orm";
|
||||||
@@ -22,8 +19,6 @@ import { getNextAvailableAliasAddress } from "../ip";
|
|||||||
export type ClientResourcesResults = {
|
export type ClientResourcesResults = {
|
||||||
newSiteResource: SiteResource;
|
newSiteResource: SiteResource;
|
||||||
oldSiteResource?: SiteResource;
|
oldSiteResource?: SiteResource;
|
||||||
newSites: { siteId: number }[];
|
|
||||||
oldSites: { siteId: number }[];
|
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
export async function updateClientResources(
|
export async function updateClientResources(
|
||||||
@@ -48,70 +43,36 @@ export async function updateClientResources(
|
|||||||
)
|
)
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
const existingSiteIds = existingResource?.networkId
|
const resourceSiteId = resourceData.site;
|
||||||
? await trx
|
let site;
|
||||||
.select({ siteId: sites.siteId })
|
|
||||||
.from(siteNetworks)
|
|
||||||
.where(eq(siteNetworks.networkId, existingResource.networkId))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
let allSites: { siteId: number }[] = [];
|
if (resourceSiteId) {
|
||||||
if (resourceData.site) {
|
// Look up site by niceId
|
||||||
let siteSingle;
|
[site] = await trx
|
||||||
const resourceSiteId = resourceData.site;
|
.select({ siteId: sites.siteId })
|
||||||
|
.from(sites)
|
||||||
if (resourceSiteId) {
|
.where(
|
||||||
// Look up site by niceId
|
and(
|
||||||
[siteSingle] = await trx
|
eq(sites.niceId, resourceSiteId),
|
||||||
.select({ siteId: sites.siteId })
|
eq(sites.orgId, orgId)
|
||||||
.from(sites)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(sites.niceId, resourceSiteId),
|
|
||||||
eq(sites.orgId, orgId)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.limit(1);
|
)
|
||||||
} else if (siteId) {
|
.limit(1);
|
||||||
// Use the provided siteId directly, but verify it belongs to the org
|
} else if (siteId) {
|
||||||
[siteSingle] = await trx
|
// Use the provided siteId directly, but verify it belongs to the org
|
||||||
.select({ siteId: sites.siteId })
|
[site] = await trx
|
||||||
.from(sites)
|
.select({ siteId: sites.siteId })
|
||||||
.where(
|
.from(sites)
|
||||||
and(eq(sites.siteId, siteId), eq(sites.orgId, orgId))
|
.where(and(eq(sites.siteId, siteId), eq(sites.orgId, orgId)))
|
||||||
)
|
.limit(1);
|
||||||
.limit(1);
|
} else {
|
||||||
} else {
|
throw new Error(`Target site is required`);
|
||||||
throw new Error(`Target site is required`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!siteSingle) {
|
|
||||||
throw new Error(
|
|
||||||
`Site not found: ${resourceSiteId} in org ${orgId}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
allSites.push(siteSingle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resourceData.sites) {
|
if (!site) {
|
||||||
for (const siteNiceId of resourceData.sites) {
|
throw new Error(
|
||||||
const [site] = await trx
|
`Site not found: ${resourceSiteId} in org ${orgId}`
|
||||||
.select({ siteId: sites.siteId })
|
);
|
||||||
.from(sites)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(sites.niceId, siteNiceId),
|
|
||||||
eq(sites.orgId, orgId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.limit(1);
|
|
||||||
if (!site) {
|
|
||||||
throw new Error(
|
|
||||||
`Site not found: ${siteId} in org ${orgId}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
allSites.push(site);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingResource) {
|
if (existingResource) {
|
||||||
@@ -120,6 +81,7 @@ export async function updateClientResources(
|
|||||||
.update(siteResources)
|
.update(siteResources)
|
||||||
.set({
|
.set({
|
||||||
name: resourceData.name || resourceNiceId,
|
name: resourceData.name || resourceNiceId,
|
||||||
|
siteId: site.siteId,
|
||||||
mode: resourceData.mode,
|
mode: resourceData.mode,
|
||||||
destination: resourceData.destination,
|
destination: resourceData.destination,
|
||||||
enabled: true, // hardcoded for now
|
enabled: true, // hardcoded for now
|
||||||
@@ -140,21 +102,6 @@ export async function updateClientResources(
|
|||||||
const siteResourceId = existingResource.siteResourceId;
|
const siteResourceId = existingResource.siteResourceId;
|
||||||
const orgId = existingResource.orgId;
|
const orgId = existingResource.orgId;
|
||||||
|
|
||||||
if (updatedResource.networkId) {
|
|
||||||
await trx
|
|
||||||
.delete(siteNetworks)
|
|
||||||
.where(
|
|
||||||
eq(siteNetworks.networkId, updatedResource.networkId)
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const site of allSites) {
|
|
||||||
await trx.insert(siteNetworks).values({
|
|
||||||
siteId: site.siteId,
|
|
||||||
networkId: updatedResource.networkId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await trx
|
await trx
|
||||||
.delete(clientSiteResources)
|
.delete(clientSiteResources)
|
||||||
.where(eq(clientSiteResources.siteResourceId, siteResourceId));
|
.where(eq(clientSiteResources.siteResourceId, siteResourceId));
|
||||||
@@ -257,9 +204,7 @@ export async function updateClientResources(
|
|||||||
|
|
||||||
results.push({
|
results.push({
|
||||||
newSiteResource: updatedResource,
|
newSiteResource: updatedResource,
|
||||||
oldSiteResource: existingResource,
|
oldSiteResource: existingResource
|
||||||
newSites: allSites,
|
|
||||||
oldSites: existingSiteIds
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let aliasAddress: string | null = null;
|
let aliasAddress: string | null = null;
|
||||||
@@ -268,22 +213,13 @@ export async function updateClientResources(
|
|||||||
aliasAddress = await getNextAvailableAliasAddress(orgId);
|
aliasAddress = await getNextAvailableAliasAddress(orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [network] = await trx
|
|
||||||
.insert(networks)
|
|
||||||
.values({
|
|
||||||
scope: "resource",
|
|
||||||
orgId: orgId
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
// Create new resource
|
// Create new resource
|
||||||
const [newResource] = await trx
|
const [newResource] = await trx
|
||||||
.insert(siteResources)
|
.insert(siteResources)
|
||||||
.values({
|
.values({
|
||||||
orgId: orgId,
|
orgId: orgId,
|
||||||
|
siteId: site.siteId,
|
||||||
niceId: resourceNiceId,
|
niceId: resourceNiceId,
|
||||||
networkId: network.networkId,
|
|
||||||
defaultNetworkId: network.networkId,
|
|
||||||
name: resourceData.name || resourceNiceId,
|
name: resourceData.name || resourceNiceId,
|
||||||
mode: resourceData.mode,
|
mode: resourceData.mode,
|
||||||
destination: resourceData.destination,
|
destination: resourceData.destination,
|
||||||
@@ -299,13 +235,6 @@ export async function updateClientResources(
|
|||||||
|
|
||||||
const siteResourceId = newResource.siteResourceId;
|
const siteResourceId = newResource.siteResourceId;
|
||||||
|
|
||||||
for (const site of allSites) {
|
|
||||||
await trx.insert(siteNetworks).values({
|
|
||||||
siteId: site.siteId,
|
|
||||||
networkId: network.networkId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const [adminRole] = await trx
|
const [adminRole] = await trx
|
||||||
.select()
|
.select()
|
||||||
.from(roles)
|
.from(roles)
|
||||||
@@ -395,11 +324,7 @@ export async function updateClientResources(
|
|||||||
`Created new client resource ${newResource.name} (${newResource.siteResourceId}) for org ${orgId}`
|
`Created new client resource ${newResource.name} (${newResource.siteResourceId}) for org ${orgId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
results.push({
|
results.push({ newSiteResource: newResource });
|
||||||
newSiteResource: newResource,
|
|
||||||
newSites: allSites,
|
|
||||||
oldSites: existingSiteIds
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -312,8 +312,7 @@ export const ClientResourceSchema = z
|
|||||||
.object({
|
.object({
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
mode: z.enum(["host", "cidr"]),
|
mode: z.enum(["host", "cidr"]),
|
||||||
site: z.string(), // DEPRECATED IN FAVOR OF sites
|
site: z.string(),
|
||||||
sites: z.array(z.string()).optional().default([]),
|
|
||||||
// protocol: z.enum(["tcp", "udp"]).optional(),
|
// protocol: z.enum(["tcp", "udp"]).optional(),
|
||||||
// proxyPort: z.int().positive().optional(),
|
// proxyPort: z.int().positive().optional(),
|
||||||
// destinationPort: z.int().positive().optional(),
|
// destinationPort: z.int().positive().optional(),
|
||||||
|
|||||||
@@ -286,12 +286,14 @@ export class TraefikConfigManager {
|
|||||||
// Check non-wildcard certs for expiry (within 45 days to match
|
// Check non-wildcard certs for expiry (within 45 days to match
|
||||||
// the server-side renewal window in certificate-service)
|
// the server-side renewal window in certificate-service)
|
||||||
for (const domain of domainsNeedingCerts) {
|
for (const domain of domainsNeedingCerts) {
|
||||||
const localState = this.lastLocalCertificateState.get(domain);
|
const localState =
|
||||||
|
this.lastLocalCertificateState.get(domain);
|
||||||
if (localState?.expiresAt) {
|
if (localState?.expiresAt) {
|
||||||
const nowInSeconds = Math.floor(Date.now() / 1000);
|
const nowInSeconds = Math.floor(Date.now() / 1000);
|
||||||
const secondsUntilExpiry =
|
const secondsUntilExpiry =
|
||||||
localState.expiresAt - nowInSeconds;
|
localState.expiresAt - nowInSeconds;
|
||||||
const daysUntilExpiry = secondsUntilExpiry / (60 * 60 * 24);
|
const daysUntilExpiry =
|
||||||
|
secondsUntilExpiry / (60 * 60 * 24);
|
||||||
if (daysUntilExpiry < 45) {
|
if (daysUntilExpiry < 45) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Fetching certificates due to upcoming expiry for ${domain} (${Math.round(daysUntilExpiry)} days remaining)`
|
`Fetching certificates due to upcoming expiry for ${domain} (${Math.round(daysUntilExpiry)} days remaining)`
|
||||||
@@ -304,11 +306,18 @@ export class TraefikConfigManager {
|
|||||||
// Also check wildcard certificates for expiry. These are not
|
// Also check wildcard certificates for expiry. These are not
|
||||||
// included in domainsNeedingCerts since their subdomains are
|
// included in domainsNeedingCerts since their subdomains are
|
||||||
// filtered out, so we must check them separately.
|
// filtered out, so we must check them separately.
|
||||||
for (const [certDomain, state] of this.lastLocalCertificateState) {
|
for (const [certDomain, state] of this
|
||||||
if (state.exists && state.wildcard && state.expiresAt) {
|
.lastLocalCertificateState) {
|
||||||
|
if (
|
||||||
|
state.exists &&
|
||||||
|
state.wildcard &&
|
||||||
|
state.expiresAt
|
||||||
|
) {
|
||||||
const nowInSeconds = Math.floor(Date.now() / 1000);
|
const nowInSeconds = Math.floor(Date.now() / 1000);
|
||||||
const secondsUntilExpiry = state.expiresAt - nowInSeconds;
|
const secondsUntilExpiry =
|
||||||
const daysUntilExpiry = secondsUntilExpiry / (60 * 60 * 24);
|
state.expiresAt - nowInSeconds;
|
||||||
|
const daysUntilExpiry =
|
||||||
|
secondsUntilExpiry / (60 * 60 * 24);
|
||||||
if (daysUntilExpiry < 45) {
|
if (daysUntilExpiry < 45) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Fetching certificates due to upcoming expiry for wildcard cert ${certDomain} (${Math.round(daysUntilExpiry)} days remaining)`
|
`Fetching certificates due to upcoming expiry for wildcard cert ${certDomain} (${Math.round(daysUntilExpiry)} days remaining)`
|
||||||
@@ -396,8 +405,14 @@ export class TraefikConfigManager {
|
|||||||
// their subdomains were filtered out above.
|
// their subdomains were filtered out above.
|
||||||
for (const [certDomain, state] of this
|
for (const [certDomain, state] of this
|
||||||
.lastLocalCertificateState) {
|
.lastLocalCertificateState) {
|
||||||
if (state.exists && state.wildcard && state.expiresAt) {
|
if (
|
||||||
const nowInSeconds = Math.floor(Date.now() / 1000);
|
state.exists &&
|
||||||
|
state.wildcard &&
|
||||||
|
state.expiresAt
|
||||||
|
) {
|
||||||
|
const nowInSeconds = Math.floor(
|
||||||
|
Date.now() / 1000
|
||||||
|
);
|
||||||
const secondsUntilExpiry =
|
const secondsUntilExpiry =
|
||||||
state.expiresAt - nowInSeconds;
|
state.expiresAt - nowInSeconds;
|
||||||
const daysUntilExpiry =
|
const daysUntilExpiry =
|
||||||
@@ -557,18 +572,11 @@ export class TraefikConfigManager {
|
|||||||
config.getRawConfig().server
|
config.getRawConfig().server
|
||||||
.session_cookie_name,
|
.session_cookie_name,
|
||||||
|
|
||||||
|
// deprecated
|
||||||
accessTokenQueryParam:
|
accessTokenQueryParam:
|
||||||
config.getRawConfig().server
|
config.getRawConfig().server
|
||||||
.resource_access_token_param,
|
.resource_access_token_param,
|
||||||
|
|
||||||
accessTokenIdHeader:
|
|
||||||
config.getRawConfig().server
|
|
||||||
.resource_access_token_headers.id,
|
|
||||||
|
|
||||||
accessTokenHeader:
|
|
||||||
config.getRawConfig().server
|
|
||||||
.resource_access_token_headers.token,
|
|
||||||
|
|
||||||
resourceSessionRequestParam:
|
resourceSessionRequestParam:
|
||||||
config.getRawConfig().server
|
config.getRawConfig().server
|
||||||
.resource_session_request_param
|
.resource_session_request_param
|
||||||
|
|||||||
@@ -14,11 +14,7 @@ import logger from "@server/logger";
|
|||||||
import { initPeerAddHandshake, updatePeer } from "../olm/peers";
|
import { initPeerAddHandshake, updatePeer } from "../olm/peers";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import {
|
import { generateSubnetProxyTargets, SubnetProxyTarget } from "@server/lib/ip";
|
||||||
formatEndpoint,
|
|
||||||
generateSubnetProxyTargets,
|
|
||||||
SubnetProxyTarget
|
|
||||||
} from "@server/lib/ip";
|
|
||||||
|
|
||||||
export async function buildClientConfigurationForNewtClient(
|
export async function buildClientConfigurationForNewtClient(
|
||||||
site: Site,
|
site: Site,
|
||||||
@@ -223,8 +219,8 @@ export async function buildTargetConfigurationForNewtClient(siteId: number) {
|
|||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format target into string (handles IPv6 bracketing)
|
// Format target into string
|
||||||
const formattedTarget = `${target.internalPort}:${formatEndpoint(target.ip, target.port)}`;
|
const formattedTarget = `${target.internalPort}:${target.ip}:${target.port}`;
|
||||||
|
|
||||||
// Add to the appropriate protocol array
|
// Add to the appropriate protocol array
|
||||||
if (target.protocol === "tcp") {
|
if (target.protocol === "tcp") {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db, Site, siteNetworks, siteResources } from "@server/db";
|
import { db, Site, siteResources } from "@server/db";
|
||||||
import { newts, newtSessions, sites } from "@server/db";
|
import { newts, newtSessions, sites } 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";
|
||||||
@@ -71,23 +71,18 @@ export async function deleteSite(
|
|||||||
await deletePeer(site.exitNodeId!, site.pubKey);
|
await deletePeer(site.exitNodeId!, site.pubKey);
|
||||||
}
|
}
|
||||||
} else if (site.type == "newt") {
|
} else if (site.type == "newt") {
|
||||||
const networks = await trx
|
// delete all of the site resources on this site
|
||||||
.select({ networkId: siteNetworks.networkId })
|
const siteResourcesOnSite = trx
|
||||||
.from(siteNetworks)
|
.delete(siteResources)
|
||||||
.where(eq(siteNetworks.siteId, siteId));
|
.where(eq(siteResources.siteId, siteId))
|
||||||
|
.returning();
|
||||||
|
|
||||||
// loop through them
|
// loop through them
|
||||||
for (const network of await networks) {
|
for (const removedSiteResource of await siteResourcesOnSite) {
|
||||||
const [siteResource] = await trx
|
await rebuildClientAssociationsFromSiteResource(
|
||||||
.select()
|
removedSiteResource,
|
||||||
.from(siteResources)
|
trx
|
||||||
.where(eq(siteResources.networkId, network.networkId));
|
);
|
||||||
if (siteResource) {
|
|
||||||
await rebuildClientAssociationsFromSiteResource(
|
|
||||||
siteResource,
|
|
||||||
trx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the newt on the site by querying the newt table for siteId
|
// get the newt on the site by querying the newt table for siteId
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import {
|
|||||||
orgs,
|
orgs,
|
||||||
roles,
|
roles,
|
||||||
roleSiteResources,
|
roleSiteResources,
|
||||||
siteNetworks,
|
|
||||||
networks,
|
|
||||||
SiteResource,
|
SiteResource,
|
||||||
siteResources,
|
siteResources,
|
||||||
sites,
|
sites,
|
||||||
@@ -25,7 +23,7 @@ import response from "@server/lib/response";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { and, eq, inArray } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -39,7 +37,7 @@ const createSiteResourceSchema = z
|
|||||||
.strictObject({
|
.strictObject({
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
mode: z.enum(["host", "cidr", "port"]),
|
mode: z.enum(["host", "cidr", "port"]),
|
||||||
siteIds: z.array(z.int()),
|
siteId: z.int(),
|
||||||
// protocol: z.enum(["tcp", "udp"]).optional(),
|
// protocol: z.enum(["tcp", "udp"]).optional(),
|
||||||
// proxyPort: z.int().positive().optional(),
|
// proxyPort: z.int().positive().optional(),
|
||||||
// destinationPort: z.int().positive().optional(),
|
// destinationPort: z.int().positive().optional(),
|
||||||
@@ -161,7 +159,7 @@ export async function createSiteResource(
|
|||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
siteIds,
|
siteId,
|
||||||
mode,
|
mode,
|
||||||
// protocol,
|
// protocol,
|
||||||
// proxyPort,
|
// proxyPort,
|
||||||
@@ -180,16 +178,14 @@ export async function createSiteResource(
|
|||||||
} = parsedBody.data;
|
} = parsedBody.data;
|
||||||
|
|
||||||
// Verify the site exists and belongs to the org
|
// Verify the site exists and belongs to the org
|
||||||
const sitesToAssign = await db
|
const [site] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(and(inArray(sites.siteId, siteIds), eq(sites.orgId, orgId)))
|
.where(and(eq(sites.siteId, siteId), eq(sites.orgId, orgId)))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (sitesToAssign.length !== siteIds.length) {
|
if (!site) {
|
||||||
return next(
|
return next(createHttpError(HttpCode.NOT_FOUND, "Site not found"));
|
||||||
createHttpError(HttpCode.NOT_FOUND, "Some site not found")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [org] = await db
|
const [org] = await db
|
||||||
@@ -291,29 +287,12 @@ export async function createSiteResource(
|
|||||||
|
|
||||||
let newSiteResource: SiteResource | undefined;
|
let newSiteResource: SiteResource | undefined;
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
const [network] = await trx
|
|
||||||
.insert(networks)
|
|
||||||
.values({
|
|
||||||
scope: "resource",
|
|
||||||
orgId: orgId
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
if (!network) {
|
|
||||||
return next(
|
|
||||||
createHttpError(
|
|
||||||
HttpCode.INTERNAL_SERVER_ERROR,
|
|
||||||
`Failed to create network`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the site resource
|
// Create the site resource
|
||||||
const insertValues: typeof siteResources.$inferInsert = {
|
const insertValues: typeof siteResources.$inferInsert = {
|
||||||
|
siteId,
|
||||||
niceId,
|
niceId,
|
||||||
orgId,
|
orgId,
|
||||||
name,
|
name,
|
||||||
networkId: network.networkId,
|
|
||||||
mode: mode as "host" | "cidr",
|
mode: mode as "host" | "cidr",
|
||||||
destination,
|
destination,
|
||||||
enabled,
|
enabled,
|
||||||
@@ -338,13 +317,6 @@ export async function createSiteResource(
|
|||||||
|
|
||||||
//////////////////// update the associations ////////////////////
|
//////////////////// update the associations ////////////////////
|
||||||
|
|
||||||
for (const siteId of siteIds) {
|
|
||||||
await trx.insert(siteNetworks).values({
|
|
||||||
siteId: siteId,
|
|
||||||
networkId: network.networkId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const [adminRole] = await trx
|
const [adminRole] = await trx
|
||||||
.select()
|
.select()
|
||||||
.from(roles)
|
.from(roles)
|
||||||
@@ -387,21 +359,16 @@ export async function createSiteResource(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const siteToAssign of sitesToAssign) {
|
const [newt] = await trx
|
||||||
const [newt] = await trx
|
.select()
|
||||||
.select()
|
.from(newts)
|
||||||
.from(newts)
|
.where(eq(newts.siteId, site.siteId))
|
||||||
.where(eq(newts.siteId, siteToAssign.siteId))
|
.limit(1);
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!newt) {
|
if (!newt) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(HttpCode.NOT_FOUND, "Newt not found")
|
||||||
HttpCode.NOT_FOUND,
|
);
|
||||||
`Newt not found for site ${siteToAssign.siteId}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await rebuildClientAssociationsFromSiteResource(
|
await rebuildClientAssociationsFromSiteResource(
|
||||||
@@ -420,7 +387,7 @@ export async function createSiteResource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`Created site resource ${newSiteResource.siteResourceId} for org ${orgId}`
|
`Created site resource ${newSiteResource.siteResourceId} for site ${siteId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
|
|||||||
@@ -70,18 +70,17 @@ export async function deleteSiteResource(
|
|||||||
.where(and(eq(siteResources.siteResourceId, siteResourceId)))
|
.where(and(eq(siteResources.siteResourceId, siteResourceId)))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
// not sure why this is here...
|
const [newt] = await trx
|
||||||
// const [newt] = await trx
|
.select()
|
||||||
// .select()
|
.from(newts)
|
||||||
// .from(newts)
|
.where(eq(newts.siteId, removedSiteResource.siteId))
|
||||||
// .where(eq(newts.siteId, removedSiteResource.siteId))
|
.limit(1);
|
||||||
// .limit(1);
|
|
||||||
|
|
||||||
// if (!newt) {
|
if (!newt) {
|
||||||
// return next(
|
return next(
|
||||||
// createHttpError(HttpCode.NOT_FOUND, "Newt not found")
|
createHttpError(HttpCode.NOT_FOUND, "Newt not found")
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
await rebuildClientAssociationsFromSiteResource(
|
await rebuildClientAssociationsFromSiteResource(
|
||||||
removedSiteResource,
|
removedSiteResource,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { db, SiteResource, siteNetworks, siteResources, sites } from "@server/db";
|
import { db, SiteResource, siteResources, sites } from "@server/db";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
@@ -73,9 +73,9 @@ const listAllSiteResourcesByOrgQuerySchema = z.object({
|
|||||||
|
|
||||||
export type ListAllSiteResourcesByOrgResponse = PaginatedResponse<{
|
export type ListAllSiteResourcesByOrgResponse = PaginatedResponse<{
|
||||||
siteResources: (SiteResource & {
|
siteResources: (SiteResource & {
|
||||||
siteNames: string[];
|
siteName: string;
|
||||||
siteNiceIds: string[];
|
siteNiceId: string;
|
||||||
siteAddresses: (string | null)[];
|
siteAddress: string | null;
|
||||||
})[];
|
})[];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
@@ -83,6 +83,7 @@ function querySiteResourcesBase() {
|
|||||||
return db
|
return db
|
||||||
.select({
|
.select({
|
||||||
siteResourceId: siteResources.siteResourceId,
|
siteResourceId: siteResources.siteResourceId,
|
||||||
|
siteId: siteResources.siteId,
|
||||||
orgId: siteResources.orgId,
|
orgId: siteResources.orgId,
|
||||||
niceId: siteResources.niceId,
|
niceId: siteResources.niceId,
|
||||||
name: siteResources.name,
|
name: siteResources.name,
|
||||||
@@ -99,19 +100,14 @@ function querySiteResourcesBase() {
|
|||||||
disableIcmp: siteResources.disableIcmp,
|
disableIcmp: siteResources.disableIcmp,
|
||||||
authDaemonMode: siteResources.authDaemonMode,
|
authDaemonMode: siteResources.authDaemonMode,
|
||||||
authDaemonPort: siteResources.authDaemonPort,
|
authDaemonPort: siteResources.authDaemonPort,
|
||||||
networkId: siteResources.networkId,
|
siteName: sites.name,
|
||||||
defaultNetworkId: siteResources.defaultNetworkId,
|
siteNiceId: sites.niceId,
|
||||||
siteNames: sql<string[]>`array_agg(${sites.name})`,
|
siteAddress: sites.address
|
||||||
siteNiceIds: sql<string[]>`array_agg(${sites.niceId})`,
|
|
||||||
siteAddresses: sql<(string | null)[]>`array_agg(${sites.address})`
|
|
||||||
})
|
})
|
||||||
.from(siteResources)
|
.from(siteResources)
|
||||||
.innerJoin(siteNetworks, eq(siteResources.networkId, siteNetworks.networkId))
|
.innerJoin(sites, eq(siteResources.siteId, sites.siteId));
|
||||||
.innerJoin(sites, eq(siteNetworks.siteId, sites.siteId))
|
|
||||||
.groupBy(siteResources.siteResourceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: "/org/{orgId}/site-resources",
|
path: "/org/{orgId}/site-resources",
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ import {
|
|||||||
orgs,
|
orgs,
|
||||||
roles,
|
roles,
|
||||||
roleSiteResources,
|
roleSiteResources,
|
||||||
siteNetworks,
|
|
||||||
sites,
|
sites,
|
||||||
networks,
|
|
||||||
Transaction,
|
Transaction,
|
||||||
userSiteResources
|
userSiteResources
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
@@ -18,7 +16,7 @@ import { siteResources, SiteResource } from "@server/db";
|
|||||||
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 { eq, and, ne, inArray } from "drizzle-orm";
|
import { eq, and, ne } from "drizzle-orm";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
@@ -44,7 +42,7 @@ const updateSiteResourceParamsSchema = z.strictObject({
|
|||||||
const updateSiteResourceSchema = z
|
const updateSiteResourceSchema = z
|
||||||
.strictObject({
|
.strictObject({
|
||||||
name: z.string().min(1).max(255).optional(),
|
name: z.string().min(1).max(255).optional(),
|
||||||
siteIds: z.array(z.int()),
|
siteId: z.int(),
|
||||||
// niceId: z.string().min(1).max(255).regex(/^[a-zA-Z0-9-]+$/, "niceId can only contain letters, numbers, and dashes").optional(),
|
// niceId: z.string().min(1).max(255).regex(/^[a-zA-Z0-9-]+$/, "niceId can only contain letters, numbers, and dashes").optional(),
|
||||||
// mode: z.enum(["host", "cidr", "port"]).optional(),
|
// mode: z.enum(["host", "cidr", "port"]).optional(),
|
||||||
mode: z.enum(["host", "cidr"]).optional(),
|
mode: z.enum(["host", "cidr"]).optional(),
|
||||||
@@ -168,7 +166,7 @@ export async function updateSiteResource(
|
|||||||
const { siteResourceId } = parsedParams.data;
|
const { siteResourceId } = parsedParams.data;
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
siteIds, // because it can change
|
siteId, // because it can change
|
||||||
mode,
|
mode,
|
||||||
destination,
|
destination,
|
||||||
alias,
|
alias,
|
||||||
@@ -183,6 +181,16 @@ export async function updateSiteResource(
|
|||||||
authDaemonMode
|
authDaemonMode
|
||||||
} = parsedBody.data;
|
} = parsedBody.data;
|
||||||
|
|
||||||
|
const [site] = await db
|
||||||
|
.select()
|
||||||
|
.from(sites)
|
||||||
|
.where(eq(sites.siteId, siteId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!site) {
|
||||||
|
return next(createHttpError(HttpCode.NOT_FOUND, "Site not found"));
|
||||||
|
}
|
||||||
|
|
||||||
// Check if site resource exists
|
// Check if site resource exists
|
||||||
const [existingSiteResource] = await db
|
const [existingSiteResource] = await db
|
||||||
.select()
|
.select()
|
||||||
@@ -222,24 +230,6 @@ export async function updateSiteResource(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the site exists and belongs to the org
|
|
||||||
const sitesToAssign = await db
|
|
||||||
.select()
|
|
||||||
.from(sites)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
inArray(sites.siteId, siteIds),
|
|
||||||
eq(sites.orgId, existingSiteResource.orgId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (sitesToAssign.length !== siteIds.length) {
|
|
||||||
return next(
|
|
||||||
createHttpError(HttpCode.NOT_FOUND, "Some site not found")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only check if destination is an IP address
|
// Only check if destination is an IP address
|
||||||
const isIp = z
|
const isIp = z
|
||||||
.union([z.ipv4(), z.ipv6()])
|
.union([z.ipv4(), z.ipv6()])
|
||||||
@@ -257,24 +247,25 @@ export async function updateSiteResource(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sitesChanged = false;
|
let existingSite = site;
|
||||||
const existingSiteIds = existingSiteResource.networkId
|
let siteChanged = false;
|
||||||
? await db
|
if (existingSiteResource.siteId !== siteId) {
|
||||||
.select()
|
siteChanged = true;
|
||||||
.from(siteNetworks)
|
// get the existing site
|
||||||
.where(
|
[existingSite] = await db
|
||||||
eq(siteNetworks.networkId, existingSiteResource.networkId)
|
.select()
|
||||||
)
|
.from(sites)
|
||||||
: [];
|
.where(eq(sites.siteId, existingSiteResource.siteId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
const existingSiteIdSet = new Set(existingSiteIds.map((s) => s.siteId));
|
if (!existingSite) {
|
||||||
const newSiteIdSet = new Set(siteIds);
|
return next(
|
||||||
|
createHttpError(
|
||||||
if (
|
HttpCode.NOT_FOUND,
|
||||||
existingSiteIdSet.size !== newSiteIdSet.size ||
|
"Existing site not found"
|
||||||
![...existingSiteIdSet].every((id) => newSiteIdSet.has(id))
|
)
|
||||||
) {
|
);
|
||||||
sitesChanged = true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the alias is unique within the org if provided
|
// make sure the alias is unique within the org if provided
|
||||||
@@ -304,7 +295,7 @@ export async function updateSiteResource(
|
|||||||
let updatedSiteResource: SiteResource | undefined;
|
let updatedSiteResource: SiteResource | undefined;
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
// if the site is changed we need to delete and recreate the resource to avoid complications with the rebuild function otherwise we can just update in place
|
// if the site is changed we need to delete and recreate the resource to avoid complications with the rebuild function otherwise we can just update in place
|
||||||
if (sitesChanged) {
|
if (siteChanged) {
|
||||||
// delete the existing site resource
|
// delete the existing site resource
|
||||||
await trx
|
await trx
|
||||||
.delete(siteResources)
|
.delete(siteResources)
|
||||||
@@ -330,8 +321,7 @@ export async function updateSiteResource(
|
|||||||
|
|
||||||
const sshPamSet =
|
const sshPamSet =
|
||||||
isLicensedSshPam &&
|
isLicensedSshPam &&
|
||||||
(authDaemonPort !== undefined ||
|
(authDaemonPort !== undefined || authDaemonMode !== undefined)
|
||||||
authDaemonMode !== undefined)
|
|
||||||
? {
|
? {
|
||||||
...(authDaemonPort !== undefined && {
|
...(authDaemonPort !== undefined && {
|
||||||
authDaemonPort
|
authDaemonPort
|
||||||
@@ -345,6 +335,7 @@ export async function updateSiteResource(
|
|||||||
.update(siteResources)
|
.update(siteResources)
|
||||||
.set({
|
.set({
|
||||||
name: name,
|
name: name,
|
||||||
|
siteId: siteId,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
destination: destination,
|
destination: destination,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
@@ -432,8 +423,7 @@ export async function updateSiteResource(
|
|||||||
// Update the site resource
|
// Update the site resource
|
||||||
const sshPamSet =
|
const sshPamSet =
|
||||||
isLicensedSshPam &&
|
isLicensedSshPam &&
|
||||||
(authDaemonPort !== undefined ||
|
(authDaemonPort !== undefined || authDaemonMode !== undefined)
|
||||||
authDaemonMode !== undefined)
|
|
||||||
? {
|
? {
|
||||||
...(authDaemonPort !== undefined && {
|
...(authDaemonPort !== undefined && {
|
||||||
authDaemonPort
|
authDaemonPort
|
||||||
@@ -447,6 +437,7 @@ export async function updateSiteResource(
|
|||||||
.update(siteResources)
|
.update(siteResources)
|
||||||
.set({
|
.set({
|
||||||
name: name,
|
name: name,
|
||||||
|
siteId: siteId,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
destination: destination,
|
destination: destination,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
@@ -463,20 +454,6 @@ export async function updateSiteResource(
|
|||||||
|
|
||||||
//////////////////// update the associations ////////////////////
|
//////////////////// update the associations ////////////////////
|
||||||
|
|
||||||
// delete the site - site resources associations
|
|
||||||
await trx
|
|
||||||
.delete(siteNetworks)
|
|
||||||
.where(
|
|
||||||
eq(siteNetworks.networkId, updatedSiteResource.networkId!)
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const siteId of siteIds) {
|
|
||||||
await trx.insert(siteNetworks).values({
|
|
||||||
siteId: siteId,
|
|
||||||
networkId: updatedSiteResource.networkId!
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await trx
|
await trx
|
||||||
.delete(clientSiteResources)
|
.delete(clientSiteResources)
|
||||||
.where(
|
.where(
|
||||||
@@ -547,16 +524,13 @@ export async function updateSiteResource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`Updated site resource ${siteResourceId}`
|
`Updated site resource ${siteResourceId} for site ${siteId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
await handleMessagingForUpdatedSiteResource(
|
await handleMessagingForUpdatedSiteResource(
|
||||||
existingSiteResource,
|
existingSiteResource,
|
||||||
updatedSiteResource,
|
updatedSiteResource,
|
||||||
siteIds.map((siteId) => ({
|
{ siteId: site.siteId, orgId: site.orgId },
|
||||||
siteId,
|
|
||||||
orgId: existingSiteResource.orgId
|
|
||||||
})),
|
|
||||||
trx
|
trx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -583,7 +557,7 @@ export async function updateSiteResource(
|
|||||||
export async function handleMessagingForUpdatedSiteResource(
|
export async function handleMessagingForUpdatedSiteResource(
|
||||||
existingSiteResource: SiteResource | undefined,
|
existingSiteResource: SiteResource | undefined,
|
||||||
updatedSiteResource: SiteResource,
|
updatedSiteResource: SiteResource,
|
||||||
sites: { siteId: number; orgId: string }[],
|
site: { siteId: number; orgId: string },
|
||||||
trx: Transaction
|
trx: Transaction
|
||||||
) {
|
) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
@@ -620,116 +594,101 @@ export async function handleMessagingForUpdatedSiteResource(
|
|||||||
// if the existingSiteResource is undefined (new resource) we don't need to do anything here, the rebuild above handled it all
|
// if the existingSiteResource is undefined (new resource) we don't need to do anything here, the rebuild above handled it all
|
||||||
|
|
||||||
if (destinationChanged || aliasChanged || portRangesChanged) {
|
if (destinationChanged || aliasChanged || portRangesChanged) {
|
||||||
for (const site of sites) {
|
const [newt] = await trx
|
||||||
const [newt] = await trx
|
.select()
|
||||||
.select()
|
.from(newts)
|
||||||
.from(newts)
|
.where(eq(newts.siteId, site.siteId))
|
||||||
.where(eq(newts.siteId, site.siteId))
|
.limit(1);
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!newt) {
|
if (!newt) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Newt not found for site during site resource update"
|
"Newt not found for site during site resource update"
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// Only update targets on newt if destination changed
|
|
||||||
if (destinationChanged || portRangesChanged) {
|
|
||||||
const oldTargets = generateSubnetProxyTargets(
|
|
||||||
existingSiteResource,
|
|
||||||
mergedAllClients
|
|
||||||
);
|
|
||||||
const newTargets = generateSubnetProxyTargets(
|
|
||||||
updatedSiteResource,
|
|
||||||
mergedAllClients
|
|
||||||
);
|
|
||||||
|
|
||||||
await updateTargets(
|
|
||||||
newt.newtId,
|
|
||||||
{
|
|
||||||
oldTargets: oldTargets,
|
|
||||||
newTargets: newTargets
|
|
||||||
},
|
|
||||||
newt.version
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const olmJobs: Promise<void>[] = [];
|
|
||||||
for (const client of mergedAllClients) {
|
|
||||||
// does this client have access to another resource on this site that has the same destination still? if so we dont want to remove it from their olm yet
|
|
||||||
// todo: optimize this query if needed
|
|
||||||
const oldDestinationStillInUseSites = await trx
|
|
||||||
.select()
|
|
||||||
.from(siteResources)
|
|
||||||
.innerJoin(
|
|
||||||
clientSiteResourcesAssociationsCache,
|
|
||||||
eq(
|
|
||||||
clientSiteResourcesAssociationsCache.siteResourceId,
|
|
||||||
siteResources.siteResourceId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.innerJoin(
|
|
||||||
siteNetworks,
|
|
||||||
eq(
|
|
||||||
siteNetworks.networkId,
|
|
||||||
siteResources.networkId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(
|
|
||||||
clientSiteResourcesAssociationsCache.clientId,
|
|
||||||
client.clientId
|
|
||||||
),
|
|
||||||
eq(siteNetworks.siteId, site.siteId),
|
|
||||||
eq(
|
|
||||||
siteResources.destination,
|
|
||||||
existingSiteResource.destination
|
|
||||||
),
|
|
||||||
ne(
|
|
||||||
siteResources.siteResourceId,
|
|
||||||
existingSiteResource.siteResourceId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
const oldDestinationStillInUseByASite =
|
|
||||||
oldDestinationStillInUseSites.length > 0;
|
|
||||||
|
|
||||||
// we also need to update the remote subnets on the olms for each client that has access to this site
|
|
||||||
olmJobs.push(
|
|
||||||
updatePeerData(
|
|
||||||
client.clientId,
|
|
||||||
site.siteId,
|
|
||||||
destinationChanged
|
|
||||||
? {
|
|
||||||
oldRemoteSubnets:
|
|
||||||
!oldDestinationStillInUseByASite
|
|
||||||
? generateRemoteSubnets([
|
|
||||||
existingSiteResource
|
|
||||||
])
|
|
||||||
: [],
|
|
||||||
newRemoteSubnets: generateRemoteSubnets([
|
|
||||||
updatedSiteResource
|
|
||||||
])
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
aliasChanged
|
|
||||||
? {
|
|
||||||
oldAliases: generateAliasConfig([
|
|
||||||
existingSiteResource
|
|
||||||
]),
|
|
||||||
newAliases: generateAliasConfig([
|
|
||||||
updatedSiteResource
|
|
||||||
])
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(olmJobs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only update targets on newt if destination changed
|
||||||
|
if (destinationChanged || portRangesChanged) {
|
||||||
|
const oldTargets = generateSubnetProxyTargets(
|
||||||
|
existingSiteResource,
|
||||||
|
mergedAllClients
|
||||||
|
);
|
||||||
|
const newTargets = generateSubnetProxyTargets(
|
||||||
|
updatedSiteResource,
|
||||||
|
mergedAllClients
|
||||||
|
);
|
||||||
|
|
||||||
|
await updateTargets(newt.newtId, {
|
||||||
|
oldTargets: oldTargets,
|
||||||
|
newTargets: newTargets
|
||||||
|
}, newt.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
const olmJobs: Promise<void>[] = [];
|
||||||
|
for (const client of mergedAllClients) {
|
||||||
|
// does this client have access to another resource on this site that has the same destination still? if so we dont want to remove it from their olm yet
|
||||||
|
// todo: optimize this query if needed
|
||||||
|
const oldDestinationStillInUseSites = await trx
|
||||||
|
.select()
|
||||||
|
.from(siteResources)
|
||||||
|
.innerJoin(
|
||||||
|
clientSiteResourcesAssociationsCache,
|
||||||
|
eq(
|
||||||
|
clientSiteResourcesAssociationsCache.siteResourceId,
|
||||||
|
siteResources.siteResourceId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(
|
||||||
|
clientSiteResourcesAssociationsCache.clientId,
|
||||||
|
client.clientId
|
||||||
|
),
|
||||||
|
eq(siteResources.siteId, site.siteId),
|
||||||
|
eq(
|
||||||
|
siteResources.destination,
|
||||||
|
existingSiteResource.destination
|
||||||
|
),
|
||||||
|
ne(
|
||||||
|
siteResources.siteResourceId,
|
||||||
|
existingSiteResource.siteResourceId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const oldDestinationStillInUseByASite =
|
||||||
|
oldDestinationStillInUseSites.length > 0;
|
||||||
|
|
||||||
|
// we also need to update the remote subnets on the olms for each client that has access to this site
|
||||||
|
olmJobs.push(
|
||||||
|
updatePeerData(
|
||||||
|
client.clientId,
|
||||||
|
updatedSiteResource.siteId,
|
||||||
|
destinationChanged
|
||||||
|
? {
|
||||||
|
oldRemoteSubnets: !oldDestinationStillInUseByASite
|
||||||
|
? generateRemoteSubnets([
|
||||||
|
existingSiteResource
|
||||||
|
])
|
||||||
|
: [],
|
||||||
|
newRemoteSubnets: generateRemoteSubnets([
|
||||||
|
updatedSiteResource
|
||||||
|
])
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
aliasChanged
|
||||||
|
? {
|
||||||
|
oldAliases: generateAliasConfig([
|
||||||
|
existingSiteResource
|
||||||
|
]),
|
||||||
|
newAliases: generateAliasConfig([
|
||||||
|
updatedSiteResource
|
||||||
|
])
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(olmJobs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,18 +39,11 @@ export async function traefikConfigProvider(
|
|||||||
userSessionCookieName:
|
userSessionCookieName:
|
||||||
config.getRawConfig().server.session_cookie_name,
|
config.getRawConfig().server.session_cookie_name,
|
||||||
|
|
||||||
|
// deprecated
|
||||||
accessTokenQueryParam:
|
accessTokenQueryParam:
|
||||||
config.getRawConfig().server
|
config.getRawConfig().server
|
||||||
.resource_access_token_param,
|
.resource_access_token_param,
|
||||||
|
|
||||||
accessTokenIdHeader:
|
|
||||||
config.getRawConfig().server
|
|
||||||
.resource_access_token_headers.id,
|
|
||||||
|
|
||||||
accessTokenHeader:
|
|
||||||
config.getRawConfig().server
|
|
||||||
.resource_access_token_headers.token,
|
|
||||||
|
|
||||||
resourceSessionRequestParam:
|
resourceSessionRequestParam:
|
||||||
config.getRawConfig().server
|
config.getRawConfig().server
|
||||||
.resource_session_request_param
|
.resource_session_request_param
|
||||||
|
|||||||
@@ -129,11 +129,6 @@ const ResourceInfo = ({ resource }: { resource: Resource }) => {
|
|||||||
resource.pincode ||
|
resource.pincode ||
|
||||||
resource.whitelist;
|
resource.whitelist;
|
||||||
|
|
||||||
const hasAnyInfo =
|
|
||||||
Boolean(resource.siteName) || Boolean(hasAuthMethods) || !resource.enabled;
|
|
||||||
|
|
||||||
if (!hasAnyInfo) return null;
|
|
||||||
|
|
||||||
const infoContent = (
|
const infoContent = (
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
{/* Site Information */}
|
{/* Site Information */}
|
||||||
@@ -833,12 +828,6 @@ export default function MemberResourcesPortal({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
|
||||||
<span className="font-medium">Destination:</span>
|
|
||||||
<span className="ml-2 text-muted-foreground">
|
|
||||||
{siteResource.destination}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{siteResource.alias && (
|
{siteResource.alias && (
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium">Alias:</span>
|
<span className="font-medium">Alias:</span>
|
||||||
@@ -847,6 +836,14 @@ export default function MemberResourcesPortal({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{siteResource.aliasAddress && (
|
||||||
|
<div>
|
||||||
|
<span className="font-medium">Alias Address:</span>
|
||||||
|
<span className="ml-2 text-muted-foreground">
|
||||||
|
{siteResource.aliasAddress}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium">Status:</span>
|
<span className="font-medium">Status:</span>
|
||||||
<span className={`ml-2 ${siteResource.enabled ? 'text-green-600' : 'text-red-600'}`}>
|
<span className={`ml-2 ${siteResource.enabled ? 'text-green-600' : 'text-red-600'}`}>
|
||||||
|
|||||||
Reference in New Issue
Block a user