mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-11 13:16:38 +00:00
Merge branch 'dev' into clients-user
This commit is contained in:
194
diff
Normal file
194
diff
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
@@ -216,36 +216,12 @@ export const ClientResourceSchema = z.object({
|
||||||
|
// Schema for the entire configuration object
|
||||||
|
export const ConfigSchema = z
|
||||||
|
.object({
|
||||||
|
- "proxy-resources": z.record(z.string(), ResourceSchema).optional().default({}),
|
||||||
|
- "client-resources": z.record(z.string(), ClientResourceSchema).optional().default({}),
|
||||||
|
- sites: z.record(z.string(), SiteSchema).optional().default({})
|
||||||
|
+ "proxy-resources": z.record(z.string(), ResourceSchema).optional().prefault({}),
|
||||||
|
+ "client-resources": z.record(z.string(), ClientResourceSchema).optional().prefault({}),
|
||||||
|
+ sites: z.record(z.string(), SiteSchema).optional().prefault({})
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
// Enforce the full-domain uniqueness across resources in the same stack
|
||||||
|
- (config) => {
|
||||||
|
- // Extract all full-domain values with their resource keys
|
||||||
|
- const fullDomainMap = new Map<string, string[]>();
|
||||||
|
-
|
||||||
|
--
|
||||||
|
.optional(),
|
||||||
|
log_level: z
|
||||||
|
@@ -31,14 +29,14 @@ export const configSchema = z
|
||||||
|
anonymous_usage: z.boolean().optional().default(true)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({}),
|
||||||
|
+ .prefault({}),
|
||||||
|
notifications: z
|
||||||
|
.object({
|
||||||
|
product_updates: z.boolean().optional().default(true),
|
||||||
|
new_releases: z.boolean().optional().default(true)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({})
|
||||||
|
+ .prefault({})
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.default({
|
||||||
|
@@ -107,7 +105,7 @@ export const configSchema = z
|
||||||
|
token: z.string().optional().default("P-Access-Token")
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({}),
|
||||||
|
+ .prefault({}),
|
||||||
|
resource_session_request_param: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
@@ -132,7 +130,7 @@ export const configSchema = z
|
||||||
|
credentials: z.boolean().optional()
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
--
|
||||||
|
maxmind_db_path: z.string().optional()
|
||||||
|
})
|
||||||
|
@@ -189,7 +187,7 @@ export const configSchema = z
|
||||||
|
.default(5000)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({})
|
||||||
|
+ .prefault({})
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
traefik: z
|
||||||
|
@@ -222,7 +220,7 @@ export const configSchema = z
|
||||||
|
.default("pp-transport-v")
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({}),
|
||||||
|
+ .prefault({}),
|
||||||
|
gerbil: z
|
||||||
|
.object({
|
||||||
|
exit_node_name: z.string().optional(),
|
||||||
|
@@ -247,7 +245,7 @@ export const configSchema = z
|
||||||
|
.default(30)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({}),
|
||||||
|
+ .prefault({}),
|
||||||
|
orgs: z
|
||||||
|
.object({
|
||||||
|
block_size: z.number().positive().gt(0).optional().default(24),
|
||||||
|
@@ -276,7 +274,7 @@ export const configSchema = z
|
||||||
|
.default(500)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({}),
|
||||||
|
+ .prefault({}),
|
||||||
|
auth: z
|
||||||
|
.object({
|
||||||
|
window_minutes: z
|
||||||
|
@@ -293,10 +291,10 @@ export const configSchema = z
|
||||||
|
.default(500)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({})
|
||||||
|
+ .prefault({})
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({}),
|
||||||
|
+ .prefault({}),
|
||||||
|
email: z
|
||||||
|
.object({
|
||||||
|
smtp_host: z.string().optional(),
|
||||||
|
@@ -308,7 +306,7 @@ export const configSchema = z
|
||||||
|
.transform(getEnvOrYaml("EMAIL_SMTP_PASS")),
|
||||||
|
smtp_secure: z.boolean().optional(),
|
||||||
|
smtp_tls_reject_unauthorized: z.boolean().optional(),
|
||||||
|
--
|
||||||
|
.optional(),
|
||||||
|
flags: z
|
||||||
|
@@ -340,7 +338,7 @@ export const configSchema = z
|
||||||
|
.default("cname.pangolin.net")
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({})
|
||||||
|
+ .prefault({})
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
@@ -355,7 +353,7 @@ export const configSchema = z
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
--
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
@@ -79,14 +79,14 @@ export const privateConfigSchema = z.object({
|
||||||
|
.default("http://gerbil:3004")
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({}),
|
||||||
|
+ .prefault({}),
|
||||||
|
flags: z
|
||||||
|
.object({
|
||||||
|
enable_redis: z.boolean().optional().default(false),
|
||||||
|
use_pangolin_dns: z.boolean().optional().default(false)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
- .default({}),
|
||||||
|
+ .prefault({}),
|
||||||
|
branding: z
|
||||||
|
.object({
|
||||||
|
app_name: z.string().optional(),
|
||||||
|
diff --git a/server/private/routers/auditLogs/queryAccessAuditLog.ts b/server/private/routers/auditLogs/queryAccessAuditLog.ts
|
||||||
|
index 33383c25..3e0b4601 100644
|
||||||
|
--- a/server/private/routers/auditLogs/queryAccessAuditLog.ts
|
||||||
|
+++ b/server/private/routers/auditLogs/queryAccessAuditLog.ts
|
||||||
|
--
|
||||||
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
|
- message: "timeEnd must be a valid ISO date string"
|
||||||
|
+ error: "timeEnd must be a valid ISO date string"
|
||||||
|
})
|
||||||
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
||||||
|
.optional()
|
||||||
|
- .default(new Date().toISOString()),
|
||||||
|
+ .prefault(new Date().toISOString()),
|
||||||
|
action: z
|
||||||
|
.union([z.boolean(), z.string()])
|
||||||
|
.transform((val) => (typeof val === "string" ? val === "true" : val))
|
||||||
|
@@ -51,7 +51,7 @@ export const queryAccessAuditLogsQuery = z.object({
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform(Number)
|
||||||
|
--
|
||||||
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
|
- message: "timeEnd must be a valid ISO date string"
|
||||||
|
+ error: "timeEnd must be a valid ISO date string"
|
||||||
|
})
|
||||||
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
||||||
|
.optional()
|
||||||
|
- .default(new Date().toISOString()),
|
||||||
|
+ .prefault(new Date().toISOString()),
|
||||||
|
action: z.string().optional(),
|
||||||
|
actorType: z.string().optional(),
|
||||||
|
actorId: z.string().optional(),
|
||||||
|
@@ -50,13 +50,13 @@ export const queryActionAuditLogsQuery = z.object({
|
||||||
|
.optional()
|
||||||
|
.default("1000")
|
||||||
|
.transform(Number)
|
||||||
|
--
|
||||||
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
|
- message: "timeEnd must be a valid ISO date string"
|
||||||
|
+ error: "timeEnd must be a valid ISO date string"
|
||||||
|
})
|
||||||
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
||||||
|
.optional()
|
||||||
|
- .default(new Date().toISOString()),
|
||||||
|
+ .prefault(new Date().toISOString()),
|
||||||
|
action: z
|
||||||
|
.union([z.boolean(), z.string()])
|
||||||
|
.transform((val) => (typeof val === "string" ? val === "true" : val))
|
||||||
|
@@ -37,13 +37,13 @@ export const queryAccessAuditLogsQuery = z.object({
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform(Number)
|
||||||
@@ -1021,7 +1021,7 @@
|
|||||||
"actionDeleteSite": "Standort löschen",
|
"actionDeleteSite": "Standort löschen",
|
||||||
"actionGetSite": "Standort abrufen",
|
"actionGetSite": "Standort abrufen",
|
||||||
"actionListSites": "Standorte auflisten",
|
"actionListSites": "Standorte auflisten",
|
||||||
"actionApplyBlueprint": "Blaupause anwenden",
|
"actionApplyBlueprint": "Blueprint anwenden",
|
||||||
"setupToken": "Setup-Token",
|
"setupToken": "Setup-Token",
|
||||||
"setupTokenDescription": "Geben Sie das Setup-Token von der Serverkonsole ein.",
|
"setupTokenDescription": "Geben Sie das Setup-Token von der Serverkonsole ein.",
|
||||||
"setupTokenRequired": "Setup-Token ist erforderlich",
|
"setupTokenRequired": "Setup-Token ist erforderlich",
|
||||||
@@ -1080,11 +1080,11 @@
|
|||||||
"actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen",
|
"actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen",
|
||||||
"actionListIdpOrgs": "IDP-Organisationen auflisten",
|
"actionListIdpOrgs": "IDP-Organisationen auflisten",
|
||||||
"actionUpdateIdpOrg": "IDP-Organisation aktualisieren",
|
"actionUpdateIdpOrg": "IDP-Organisation aktualisieren",
|
||||||
"actionCreateClient": "Kunde erstellen",
|
"actionCreateClient": "Client erstellen",
|
||||||
"actionDeleteClient": "Kunde löschen",
|
"actionDeleteClient": "Client löschen",
|
||||||
"actionUpdateClient": "Kunde aktualisieren",
|
"actionUpdateClient": "Client aktualisieren",
|
||||||
"actionListClients": "Kunden auflisten",
|
"actionListClients": "Clientsn auflisten",
|
||||||
"actionGetClient": "Kunde holen",
|
"actionGetClient": "Client abrufen",
|
||||||
"actionCreateSiteResource": "Site-Ressource erstellen",
|
"actionCreateSiteResource": "Site-Ressource erstellen",
|
||||||
"actionDeleteSiteResource": "Site-Ressource löschen",
|
"actionDeleteSiteResource": "Site-Ressource löschen",
|
||||||
"actionGetSiteResource": "Site-Ressource abrufen",
|
"actionGetSiteResource": "Site-Ressource abrufen",
|
||||||
@@ -1161,7 +1161,7 @@
|
|||||||
"sidebarAllUsers": "Alle Benutzer",
|
"sidebarAllUsers": "Alle Benutzer",
|
||||||
"sidebarIdentityProviders": "Identitätsanbieter",
|
"sidebarIdentityProviders": "Identitätsanbieter",
|
||||||
"sidebarLicense": "Lizenz",
|
"sidebarLicense": "Lizenz",
|
||||||
"sidebarClients": "Kunden",
|
"sidebarClients": "Clients",
|
||||||
"sidebarDomains": "Domänen",
|
"sidebarDomains": "Domänen",
|
||||||
"sidebarBluePrints": "Baupläne",
|
"sidebarBluePrints": "Baupläne",
|
||||||
"blueprints": "Baupläne",
|
"blueprints": "Baupläne",
|
||||||
@@ -1175,9 +1175,9 @@
|
|||||||
"blueprintInfo": "Blaupauseninformation",
|
"blueprintInfo": "Blaupauseninformation",
|
||||||
"message": "Nachricht",
|
"message": "Nachricht",
|
||||||
"blueprintContentsDescription": "Definieren Sie den YAML-Inhalt, der Ihre Infrastruktur beschreibt",
|
"blueprintContentsDescription": "Definieren Sie den YAML-Inhalt, der Ihre Infrastruktur beschreibt",
|
||||||
"blueprintErrorCreateDescription": "Fehler beim Anwenden der Blaupause",
|
"blueprintErrorCreateDescription": "Fehler beim Anwenden des Blueprints",
|
||||||
"blueprintErrorCreate": "Fehler beim Erstellen der Blaupause",
|
"blueprintErrorCreate": "Fehler beim Erstellen des Blueprints",
|
||||||
"searchBlueprintProgress": "Blaupausen suchen...",
|
"searchBlueprintProgress": "Blueprints suchen...",
|
||||||
"appliedAt": "Angewandt am",
|
"appliedAt": "Angewandt am",
|
||||||
"source": "Quelle",
|
"source": "Quelle",
|
||||||
"contents": "Inhalt",
|
"contents": "Inhalt",
|
||||||
@@ -1423,14 +1423,14 @@
|
|||||||
},
|
},
|
||||||
"siteRequired": "Standort ist erforderlich.",
|
"siteRequired": "Standort ist erforderlich.",
|
||||||
"olmTunnel": "Olm-Tunnel",
|
"olmTunnel": "Olm-Tunnel",
|
||||||
"olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung",
|
"olmTunnelDescription": "Nutzen Sie Olm für die Clientverbindung",
|
||||||
"errorCreatingClient": "Fehler beim Erstellen des Clients",
|
"errorCreatingClient": "Fehler beim Erstellen des Clients",
|
||||||
"clientDefaultsNotFound": "Standardeinstellungen des Clients nicht gefunden",
|
"clientDefaultsNotFound": "Standardeinstellungen des Clients nicht gefunden",
|
||||||
"createClient": "Client erstellen",
|
"createClient": "Client erstellen",
|
||||||
"createClientDescription": "Erstellen Sie einen neuen Client für die Verbindung zu Ihren Standorten.",
|
"createClientDescription": "Erstellen Sie einen neuen Client für die Verbindung zu Ihren Standorten.",
|
||||||
"seeAllClients": "Alle Clients anzeigen",
|
"seeAllClients": "Alle Clients anzeigen",
|
||||||
"clientInformation": "Kundeninformationen",
|
"clientInformation": "Clientninformationen",
|
||||||
"clientNamePlaceholder": "Kundenname",
|
"clientNamePlaceholder": "Clientname",
|
||||||
"address": "Adresse",
|
"address": "Adresse",
|
||||||
"subnetPlaceholder": "Subnetz",
|
"subnetPlaceholder": "Subnetz",
|
||||||
"addressDescription": "Die Adresse, die dieser Client für die Verbindung verwenden wird.",
|
"addressDescription": "Die Adresse, die dieser Client für die Verbindung verwenden wird.",
|
||||||
@@ -2049,7 +2049,7 @@
|
|||||||
"orgOrDomainIdMissing": "Organisation oder Domänen-ID fehlt",
|
"orgOrDomainIdMissing": "Organisation oder Domänen-ID fehlt",
|
||||||
"loadingDNSRecords": "Lade DNS-Einträge...",
|
"loadingDNSRecords": "Lade DNS-Einträge...",
|
||||||
"olmUpdateAvailableInfo": "Eine aktualisierte Version von Olm ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für die beste Erfahrung.",
|
"olmUpdateAvailableInfo": "Eine aktualisierte Version von Olm ist verfügbar. Bitte aktualisieren Sie auf die neueste Version für die beste Erfahrung.",
|
||||||
"client": "Kunde",
|
"client": "Client",
|
||||||
"proxyProtocol": "Proxy-Protokoll-Einstellungen",
|
"proxyProtocol": "Proxy-Protokoll-Einstellungen",
|
||||||
"proxyProtocolDescription": "Konfigurieren Sie das Proxy-Protokoll, um die IP-Adressen des Clients für TCP/UDP-Dienste zu erhalten.",
|
"proxyProtocolDescription": "Konfigurieren Sie das Proxy-Protokoll, um die IP-Adressen des Clients für TCP/UDP-Dienste zu erhalten.",
|
||||||
"enableProxyProtocol": "Proxy-Protokoll aktivieren",
|
"enableProxyProtocol": "Proxy-Protokoll aktivieren",
|
||||||
|
|||||||
@@ -1279,6 +1279,15 @@
|
|||||||
"settingsErrorUpdateDescription": "An error occurred while updating settings",
|
"settingsErrorUpdateDescription": "An error occurred while updating settings",
|
||||||
"sidebarCollapse": "Collapse",
|
"sidebarCollapse": "Collapse",
|
||||||
"sidebarExpand": "Expand",
|
"sidebarExpand": "Expand",
|
||||||
|
"productUpdateMoreInfo": "{noOfUpdates} more updates",
|
||||||
|
"productUpdateInfo": "{noOfUpdates} updates",
|
||||||
|
"productUpdateWhatsNew": "What's New",
|
||||||
|
"productUpdateTitle": "Product Updates",
|
||||||
|
"productUpdateEmpty": "No updates",
|
||||||
|
"dismissAll": "Dismiss all",
|
||||||
|
"pangolinUpdateAvailable": "New version available",
|
||||||
|
"pangolinUpdateAvailableInfo": "Version {version} is ready to install",
|
||||||
|
"pangolinUpdateAvailableReleaseNotes": "View release notes",
|
||||||
"newtUpdateAvailable": "Update Available",
|
"newtUpdateAvailable": "Update Available",
|
||||||
"newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.",
|
"newtUpdateAvailableInfo": "A new version of Newt is available. Please update to the latest version for the best experience.",
|
||||||
"domainPickerEnterDomain": "Domain",
|
"domainPickerEnterDomain": "Domain",
|
||||||
@@ -1521,6 +1530,13 @@
|
|||||||
"resourcesTableNoInternalResourcesFound": "No internal resources found.",
|
"resourcesTableNoInternalResourcesFound": "No internal resources found.",
|
||||||
"resourcesTableDestination": "Destination",
|
"resourcesTableDestination": "Destination",
|
||||||
"resourcesTableClients": "Clients",
|
"resourcesTableClients": "Clients",
|
||||||
|
"resourcesTableAndOnlyAccessibleInternally": "and are only accessible internally when connected with a client.",
|
||||||
|
"resourcesTableNoTargets": "No targets",
|
||||||
|
"resourcesTableHealthy": "Healthy",
|
||||||
|
"resourcesTableDegraded": "Degraded",
|
||||||
|
"resourcesTableOffline": "Offline",
|
||||||
|
"resourcesTableUnknown": "Unknown",
|
||||||
|
"resourcesTableNotMonitored": "Not monitored",
|
||||||
"editInternalResourceDialogEditClientResource": "Edit Client Resource",
|
"editInternalResourceDialogEditClientResource": "Edit Client Resource",
|
||||||
"editInternalResourceDialogUpdateResourceProperties": "Update the resource properties and target configuration for {resourceName}.",
|
"editInternalResourceDialogUpdateResourceProperties": "Update the resource properties and target configuration for {resourceName}.",
|
||||||
"editInternalResourceDialogResourceProperties": "Resource Properties",
|
"editInternalResourceDialogResourceProperties": "Resource Properties",
|
||||||
@@ -2155,5 +2171,31 @@
|
|||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"accessClientSelect": "Select machine clients",
|
"accessClientSelect": "Select machine clients",
|
||||||
"resourceClientDescription": "Machine clients that can access this resource",
|
"resourceClientDescription": "Machine clients that can access this resource",
|
||||||
"regenerate": "Regenerate"
|
"regenerate": "Regenerate",
|
||||||
|
"credentials": "Credentials",
|
||||||
|
"savecredentials": "Save Credentials",
|
||||||
|
"regeneratecredentials": "Re-key",
|
||||||
|
"regenerateCredentials": "Regenerate and save your credentials",
|
||||||
|
"generatedcredentials": "Generated Credentials",
|
||||||
|
"copyandsavethesecredentials": "Copy and save these credentials",
|
||||||
|
"copyandsavethesecredentialsdescription": "These credentials will not be shown again after you leave this page. Save them securely now.",
|
||||||
|
"credentialsSaved" : "Credentials Saved",
|
||||||
|
"credentialsSavedDescription": "Credentials have been regenerated and saved successfully.",
|
||||||
|
"credentialsSaveError": "Credentials Save Error",
|
||||||
|
"credentialsSaveErrorDescription": "An error occurred while regenerating and saving the credentials.",
|
||||||
|
"regenerateCredentialsWarning": "Regenerating credentials will invalidate the previous ones. Make sure to update any configurations that use these credentials.",
|
||||||
|
"confirm": "Confirm",
|
||||||
|
"regenerateCredentialsConfirmation": "Are you sure you want to regenerate the credentials?",
|
||||||
|
"endpoint": "Endpoint",
|
||||||
|
"Id": "Id",
|
||||||
|
"SecretKey": "Secret Key",
|
||||||
|
"featureDisabledTooltip": "This feature is only available in the enterprise plan and require a license to use it.",
|
||||||
|
"niceId": "Nice ID",
|
||||||
|
"niceIdUpdated": "Nice ID Updated",
|
||||||
|
"niceIdUpdatedSuccessfully": "Nice ID Updated Successfully",
|
||||||
|
"niceIdUpdateError": "Error updating Nice ID",
|
||||||
|
"niceIdUpdateErrorDescription": "An error occurred while updating the Nice ID.",
|
||||||
|
"niceIdCannotBeEmpty": "Nice ID cannot be empty",
|
||||||
|
"enterIdentifier": "Enter identifier",
|
||||||
|
"identifier": "Identifier"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
import createNextIntlPlugin from "next-intl/plugin";
|
import createNextIntlPlugin from "next-intl/plugin";
|
||||||
|
|
||||||
const withNextIntl = createNextIntlPlugin();
|
const withNextIntl = createNextIntlPlugin();
|
||||||
|
|
||||||
/** @type {import("next").NextConfig} */
|
const nextConfig: NextConfig = {
|
||||||
const nextConfig = {
|
|
||||||
eslint: {
|
eslint: {
|
||||||
ignoreDuringBuilds: true
|
ignoreDuringBuilds: true
|
||||||
},
|
},
|
||||||
output: "standalone",
|
output: "standalone"
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withNextIntl(nextConfig);
|
export default withNextIntl(nextConfig);
|
||||||
302
package-lock.json
generated
302
package-lock.json
generated
@@ -9,9 +9,10 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "SEE LICENSE IN LICENSE AND README.md",
|
"license": "SEE LICENSE IN LICENSE AND README.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asteasolutions/zod-to-openapi": "^7.3.4",
|
"@asteasolutions/zod-to-openapi": "8.1.0",
|
||||||
"@aws-sdk/client-s3": "3.922.0",
|
"@aws-sdk/client-s3": "3.922.0",
|
||||||
"@faker-js/faker": "^10.1.0",
|
"@faker-js/faker": "^10.1.0",
|
||||||
|
"@headlessui/react": "^2.2.9",
|
||||||
"@hookform/resolvers": "5.2.2",
|
"@hookform/resolvers": "5.2.2",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@node-rs/argon2": "^2.0.2",
|
"@node-rs/argon2": "^2.0.2",
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
"@simplewebauthn/browser": "^13.2.2",
|
"@simplewebauthn/browser": "^13.2.2",
|
||||||
"@simplewebauthn/server": "^13.2.2",
|
"@simplewebauthn/server": "^13.2.2",
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
|
"@tanstack/react-query": "^5.90.6",
|
||||||
"@tanstack/react-table": "8.21.3",
|
"@tanstack/react-table": "8.21.3",
|
||||||
"arctic": "^3.7.0",
|
"arctic": "^3.7.0",
|
||||||
"axios": "^1.13.1",
|
"axios": "^1.13.1",
|
||||||
@@ -106,14 +108,15 @@
|
|||||||
"ws": "8.18.3",
|
"ws": "8.18.3",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
"yargs": "18.0.0",
|
"yargs": "18.0.0",
|
||||||
"zod": "3.25.76",
|
"zod": "4.1.12",
|
||||||
"zod-validation-error": "3.5.2"
|
"zod-validation-error": "5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@dotenvx/dotenvx": "1.51.1",
|
"@dotenvx/dotenvx": "1.51.1",
|
||||||
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
||||||
"@react-email/preview-server": "4.3.2",
|
"@react-email/preview-server": "4.3.2",
|
||||||
"@tailwindcss/postcss": "^4.1.17",
|
"@tailwindcss/postcss": "^4.1.17",
|
||||||
|
"@tanstack/react-query-devtools": "^5.90.2",
|
||||||
"@types/better-sqlite3": "7.6.12",
|
"@types/better-sqlite3": "7.6.12",
|
||||||
"@types/cookie-parser": "1.4.10",
|
"@types/cookie-parser": "1.4.10",
|
||||||
"@types/cors": "2.8.19",
|
"@types/cors": "2.8.19",
|
||||||
@@ -172,15 +175,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@asteasolutions/zod-to-openapi": {
|
"node_modules/@asteasolutions/zod-to-openapi": {
|
||||||
"version": "7.3.4",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-8.1.0.tgz",
|
||||||
"integrity": "sha512-/2rThQ5zPi9OzVwes6U7lK1+Yvug0iXu25olp7S0XsYmOqnyMfxH7gdSQjn/+DSOHRg7wnotwGJSyL+fBKdnEA==",
|
"integrity": "sha512-tQFxVs05J/6QXXqIzj6rTRk3nj1HFs4pe+uThwE95jL5II2JfpVXkK+CqkO7aT0Do5AYqO6LDrKpleLUFXgY+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"openapi3-ts": "^4.1.2"
|
"openapi3-ts": "^4.1.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"zod": "^3.20.2"
|
"zod": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-crypto/crc32": {
|
"node_modules/@aws-crypto/crc32": {
|
||||||
@@ -1641,6 +1644,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
|
||||||
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
|
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.2.0",
|
"@ampproject/remapping": "^2.2.0",
|
||||||
"@babel/code-frame": "^7.26.2",
|
"@babel/code-frame": "^7.26.2",
|
||||||
@@ -3129,6 +3133,21 @@
|
|||||||
"@floating-ui/utils": "^0.2.10"
|
"@floating-ui/utils": "^0.2.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@floating-ui/react": {
|
||||||
|
"version": "0.26.28",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz",
|
||||||
|
"integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/react-dom": "^2.1.2",
|
||||||
|
"@floating-ui/utils": "^0.2.8",
|
||||||
|
"tabbable": "^6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@floating-ui/react-dom": {
|
"node_modules/@floating-ui/react-dom": {
|
||||||
"version": "2.1.6",
|
"version": "2.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
|
||||||
@@ -3208,6 +3227,26 @@
|
|||||||
"tslib": "2"
|
"tslib": "2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@headlessui/react": {
|
||||||
|
"version": "2.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.9.tgz",
|
||||||
|
"integrity": "sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/react": "^0.26.16",
|
||||||
|
"@react-aria/focus": "^3.20.2",
|
||||||
|
"@react-aria/interactions": "^3.25.0",
|
||||||
|
"@tanstack/react-virtual": "^3.13.9",
|
||||||
|
"use-sync-external-store": "^1.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^18 || ^19 || ^19.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@hexagon/base64": {
|
"node_modules/@hexagon/base64": {
|
||||||
"version": "1.1.28",
|
"version": "1.1.28",
|
||||||
"resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz",
|
"resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz",
|
||||||
@@ -4035,6 +4074,7 @@
|
|||||||
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
|
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.21.3 || >=16"
|
"node": "^14.21.3 || >=16"
|
||||||
},
|
},
|
||||||
@@ -5999,6 +6039,73 @@
|
|||||||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-aria/focus": {
|
||||||
|
"version": "3.21.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz",
|
||||||
|
"integrity": "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-aria/interactions": "^3.25.6",
|
||||||
|
"@react-aria/utils": "^3.31.0",
|
||||||
|
"@react-types/shared": "^3.32.1",
|
||||||
|
"@swc/helpers": "^0.5.0",
|
||||||
|
"clsx": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-aria/interactions": {
|
||||||
|
"version": "3.25.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.6.tgz",
|
||||||
|
"integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-aria/ssr": "^3.9.10",
|
||||||
|
"@react-aria/utils": "^3.31.0",
|
||||||
|
"@react-stately/flags": "^3.1.2",
|
||||||
|
"@react-types/shared": "^3.32.1",
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-aria/ssr": {
|
||||||
|
"version": "3.9.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
|
||||||
|
"integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-aria/utils": {
|
||||||
|
"version": "3.31.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.31.0.tgz",
|
||||||
|
"integrity": "sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-aria/ssr": "^3.9.10",
|
||||||
|
"@react-stately/flags": "^3.1.2",
|
||||||
|
"@react-stately/utils": "^3.10.8",
|
||||||
|
"@react-types/shared": "^3.32.1",
|
||||||
|
"@swc/helpers": "^0.5.0",
|
||||||
|
"clsx": "^2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@react-email/body": {
|
"node_modules/@react-email/body": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.1.0.tgz",
|
||||||
@@ -6864,6 +6971,7 @@
|
|||||||
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
|
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@@ -7069,6 +7177,7 @@
|
|||||||
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -7079,6 +7188,7 @@
|
|||||||
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.25.0"
|
"scheduler": "^0.25.0"
|
||||||
},
|
},
|
||||||
@@ -7287,6 +7397,36 @@
|
|||||||
"react": "^18.0 || ^19.0 || ^19.0.0-rc"
|
"react": "^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-stately/flags": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-stately/utils": {
|
||||||
|
"version": "3.10.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz",
|
||||||
|
"integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-types/shared": {
|
||||||
|
"version": "3.32.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
|
||||||
|
"integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rtsao/scc": {
|
"node_modules/@rtsao/scc": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||||
@@ -8457,6 +8597,62 @@
|
|||||||
"tailwindcss": "4.1.17"
|
"tailwindcss": "4.1.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/query-core": {
|
||||||
|
"version": "5.90.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.6.tgz",
|
||||||
|
"integrity": "sha512-AnZSLF26R8uX+tqb/ivdrwbVdGemdEDm1Q19qM6pry6eOZ6bEYiY7mWhzXT1YDIPTNEVcZ5kYP9nWjoxDLiIVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/query-devtools": {
|
||||||
|
"version": "5.90.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.90.1.tgz",
|
||||||
|
"integrity": "sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-query": {
|
||||||
|
"version": "5.90.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.6.tgz",
|
||||||
|
"integrity": "sha512-gB1sljYjcobZKxjPbKSa31FUTyr+ROaBdoH+wSSs9Dk+yDCmMs+TkTV3PybRRVLC7ax7q0erJ9LvRWnMktnRAw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/query-core": "5.90.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-query-devtools": {
|
||||||
|
"version": "5.90.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.90.2.tgz",
|
||||||
|
"integrity": "sha512-vAXJzZuBXtCQtrY3F/yUNJCV4obT/A/n81kb3+YqLbro5Z2+phdAbceO+deU3ywPw8B42oyJlp4FhO0SoivDFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/query-devtools": "5.90.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@tanstack/react-query": "^5.90.2",
|
||||||
|
"react": "^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tanstack/react-table": {
|
"node_modules/@tanstack/react-table": {
|
||||||
"version": "8.21.3",
|
"version": "8.21.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
||||||
@@ -8477,6 +8673,23 @@
|
|||||||
"react-dom": ">=16.8"
|
"react-dom": ">=16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/react-virtual": {
|
||||||
|
"version": "3.13.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz",
|
||||||
|
"integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/virtual-core": "3.13.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tanstack/table-core": {
|
"node_modules/@tanstack/table-core": {
|
||||||
"version": "8.21.3",
|
"version": "8.21.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
|
||||||
@@ -8490,6 +8703,16 @@
|
|||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/virtual-core": {
|
||||||
|
"version": "3.13.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
|
||||||
|
"integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tybys/wasm-util": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||||
@@ -8506,6 +8729,7 @@
|
|||||||
"integrity": "sha512-fnQmj8lELIj7BSrZQAdBMHEHX8OZLYIHXqAKT1O7tDfLxaINzf00PMjw22r3N/xXh0w/sGHlO6SVaCQ2mj78lg==",
|
"integrity": "sha512-fnQmj8lELIj7BSrZQAdBMHEHX8OZLYIHXqAKT1O7tDfLxaINzf00PMjw22r3N/xXh0w/sGHlO6SVaCQ2mj78lg==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -8592,6 +8816,7 @@
|
|||||||
"integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==",
|
"integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
"@types/express-serve-static-core": "^5.0.0",
|
"@types/express-serve-static-core": "^5.0.0",
|
||||||
@@ -8685,6 +8910,7 @@
|
|||||||
"integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==",
|
"integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
@@ -8720,6 +8946,7 @@
|
|||||||
"integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==",
|
"integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"pg-protocol": "*",
|
"pg-protocol": "*",
|
||||||
@@ -8753,6 +8980,7 @@
|
|||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@@ -8763,6 +8991,7 @@
|
|||||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
@@ -8906,6 +9135,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz",
|
||||||
"integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==",
|
"integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.46.3",
|
"@typescript-eslint/scope-manager": "8.46.3",
|
||||||
"@typescript-eslint/types": "8.46.3",
|
"@typescript-eslint/types": "8.46.3",
|
||||||
@@ -9579,6 +9809,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -10108,6 +10339,7 @@
|
|||||||
"integrity": "sha512-mXpa5jnIKKHeoGzBrUJrc65cXFKcILGZpU3FXR0pradUEm9MA7UZz02qfEejaMcm9iXrSOCenwwYMJ/tZ1y5Ig==",
|
"integrity": "sha512-mXpa5jnIKKHeoGzBrUJrc65cXFKcILGZpU3FXR0pradUEm9MA7UZz02qfEejaMcm9iXrSOCenwwYMJ/tZ1y5Ig==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bindings": "^1.5.0",
|
"bindings": "^1.5.0",
|
||||||
"prebuild-install": "^7.1.1"
|
"prebuild-install": "^7.1.1"
|
||||||
@@ -10220,6 +10452,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.9",
|
"baseline-browser-mapping": "^2.8.9",
|
||||||
"caniuse-lite": "^1.0.30001746",
|
"caniuse-lite": "^1.0.30001746",
|
||||||
@@ -11220,8 +11453,7 @@
|
|||||||
"version": "3.1.7",
|
"version": "3.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
|
||||||
"integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==",
|
"integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==",
|
||||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
"license": "(MPL-2.0 OR Apache-2.0)"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/domutils": {
|
"node_modules/domutils": {
|
||||||
"version": "3.2.2",
|
"version": "3.2.2",
|
||||||
@@ -11877,6 +12109,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
},
|
},
|
||||||
@@ -11973,6 +12206,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz",
|
||||||
"integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==",
|
"integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -12150,6 +12384,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
|
||||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rtsao/scc": "^1.1.0",
|
"@rtsao/scc": "^1.1.0",
|
||||||
"array-includes": "^3.1.9",
|
"array-includes": "^3.1.9",
|
||||||
@@ -12276,6 +12511,18 @@
|
|||||||
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
|
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-react-hooks/node_modules/zod-validation-error": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-react/node_modules/resolve": {
|
"node_modules/eslint-plugin-react/node_modules/resolve": {
|
||||||
"version": "2.0.0-next.5",
|
"version": "2.0.0-next.5",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
|
||||||
@@ -12446,6 +12693,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "^2.0.0",
|
"accepts": "^2.0.0",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
@@ -15055,7 +15303,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.54.0.tgz",
|
||||||
"integrity": "sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==",
|
"integrity": "sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dompurify": "3.1.7",
|
"dompurify": "3.1.7",
|
||||||
"marked": "14.0.0"
|
"marked": "14.0.0"
|
||||||
@@ -15066,7 +15313,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
|
||||||
"integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
|
"integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"marked": "bin/marked.js"
|
"marked": "bin/marked.js"
|
||||||
},
|
},
|
||||||
@@ -15189,6 +15435,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/next/-/next-15.5.6.tgz",
|
||||||
"integrity": "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ==",
|
"integrity": "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/env": "15.5.6",
|
"@next/env": "15.5.6",
|
||||||
"@swc/helpers": "0.5.15",
|
"@swc/helpers": "0.5.15",
|
||||||
@@ -17635,6 +17882,7 @@
|
|||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -18619,6 +18867,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
||||||
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pg-connection-string": "^2.9.1",
|
"pg-connection-string": "^2.9.1",
|
||||||
"pg-pool": "^3.10.1",
|
"pg-pool": "^3.10.1",
|
||||||
@@ -18795,6 +19044,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -19252,6 +19502,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -19282,6 +19533,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -19573,6 +19825,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.0.tgz",
|
||||||
"integrity": "sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==",
|
"integrity": "sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
@@ -20066,6 +20319,7 @@
|
|||||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fast-uri": "^3.0.1",
|
"fast-uri": "^3.0.1",
|
||||||
@@ -21265,6 +21519,12 @@
|
|||||||
"express": ">=4.0.0 || >=5.0.0-beta"
|
"express": ">=4.0.0 || >=5.0.0-beta"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tabbable": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tailwind-merge": {
|
"node_modules/tailwind-merge": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
|
||||||
@@ -21279,7 +21539,8 @@
|
|||||||
"version": "4.1.17",
|
"version": "4.1.17",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
||||||
"integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==",
|
"integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/tapable": {
|
"node_modules/tapable": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
@@ -21809,6 +22070,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -22322,6 +22584,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz",
|
"resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz",
|
||||||
"integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==",
|
"integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@colors/colors": "^1.6.0",
|
"@colors/colors": "^1.6.0",
|
||||||
"@dabh/diagnostics": "^2.0.8",
|
"@dabh/diagnostics": "^2.0.8",
|
||||||
@@ -22624,24 +22887,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.25.76",
|
"version": "4.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/zod-validation-error": {
|
"node_modules/zod-validation-error": {
|
||||||
"version": "3.5.2",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-5.0.0.tgz",
|
||||||
"integrity": "sha512-mdi7YOLtram5dzJ5aDtm1AG9+mxRma1iaMrZdYIpFO7epdKBUwLHIxTF8CPDeCQ828zAXYtizrKlEJAtzgfgrw==",
|
"integrity": "sha512-hmk+pkyKq7Q71PiWVSDUc3VfpzpvcRHZ3QPw9yEMVvmtCekaMeOHnbr3WbxfrgEnQTv6haGP4cmv0Ojmihzsxw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"zod": "^3.25.0"
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -32,8 +32,10 @@
|
|||||||
"build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs"
|
"build:cli": "node esbuild.mjs -e cli/index.ts -o dist/cli.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asteasolutions/zod-to-openapi": "^7.3.4",
|
"@asteasolutions/zod-to-openapi": "8.1.0",
|
||||||
"@aws-sdk/client-s3": "3.922.0",
|
"@aws-sdk/client-s3": "3.922.0",
|
||||||
|
"@faker-js/faker": "^10.1.0",
|
||||||
|
"@headlessui/react": "^2.2.9",
|
||||||
"@hookform/resolvers": "5.2.2",
|
"@hookform/resolvers": "5.2.2",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@node-rs/argon2": "^2.0.2",
|
"@node-rs/argon2": "^2.0.2",
|
||||||
@@ -63,6 +65,7 @@
|
|||||||
"@simplewebauthn/browser": "^13.2.2",
|
"@simplewebauthn/browser": "^13.2.2",
|
||||||
"@simplewebauthn/server": "^13.2.2",
|
"@simplewebauthn/server": "^13.2.2",
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
|
"@tanstack/react-query": "^5.90.6",
|
||||||
"@tanstack/react-table": "8.21.3",
|
"@tanstack/react-table": "8.21.3",
|
||||||
"arctic": "^3.7.0",
|
"arctic": "^3.7.0",
|
||||||
"axios": "^1.13.1",
|
"axios": "^1.13.1",
|
||||||
@@ -128,14 +131,14 @@
|
|||||||
"ws": "8.18.3",
|
"ws": "8.18.3",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
"yargs": "18.0.0",
|
"yargs": "18.0.0",
|
||||||
"zod": "3.25.76",
|
"zod": "4.1.12",
|
||||||
"zod-validation-error": "3.5.2",
|
"zod-validation-error": "5.0.0"
|
||||||
"@faker-js/faker": "^10.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@dotenvx/dotenvx": "1.51.1",
|
"@dotenvx/dotenvx": "1.51.1",
|
||||||
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
"@esbuild-plugins/tsconfig-paths": "0.1.2",
|
||||||
"@react-email/preview-server": "4.3.2",
|
"@react-email/preview-server": "4.3.2",
|
||||||
|
"@tanstack/react-query-devtools": "^5.90.2",
|
||||||
"@tailwindcss/postcss": "^4.1.17",
|
"@tailwindcss/postcss": "^4.1.17",
|
||||||
"@types/better-sqlite3": "7.6.12",
|
"@types/better-sqlite3": "7.6.12",
|
||||||
"@types/cookie-parser": "1.4.10",
|
"@types/cookie-parser": "1.4.10",
|
||||||
@@ -146,9 +149,9 @@
|
|||||||
"@types/jmespath": "^0.15.2",
|
"@types/jmespath": "^0.15.2",
|
||||||
"@types/js-yaml": "4.0.9",
|
"@types/js-yaml": "4.0.9",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/nprogress": "^0.2.3",
|
|
||||||
"@types/node": "24.9.2",
|
"@types/node": "24.9.2",
|
||||||
"@types/nodemailer": "7.0.3",
|
"@types/nodemailer": "7.0.3",
|
||||||
|
"@types/nprogress": "^0.2.3",
|
||||||
"@types/pg": "8.15.6",
|
"@types/pg": "8.15.6",
|
||||||
"@types/react": "19.2.2",
|
"@types/react": "19.2.2",
|
||||||
"@types/react-dom": "19.2.2",
|
"@types/react-dom": "19.2.2",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export enum ActionsEnum {
|
|||||||
getSite = "getSite",
|
getSite = "getSite",
|
||||||
listSites = "listSites",
|
listSites = "listSites",
|
||||||
updateSite = "updateSite",
|
updateSite = "updateSite",
|
||||||
|
reGenerateSecret = "reGenerateSecret",
|
||||||
createResource = "createResource",
|
createResource = "createResource",
|
||||||
deleteResource = "deleteResource",
|
deleteResource = "deleteResource",
|
||||||
getResource = "getResource",
|
getResource = "getResource",
|
||||||
|
|||||||
@@ -7,20 +7,20 @@ export const SiteSchema = z.object({
|
|||||||
|
|
||||||
export const TargetHealthCheckSchema = z.object({
|
export const TargetHealthCheckSchema = z.object({
|
||||||
hostname: z.string(),
|
hostname: z.string(),
|
||||||
port: z.number().int().min(1).max(65535),
|
port: z.int().min(1).max(65535),
|
||||||
enabled: z.boolean().optional().default(true),
|
enabled: z.boolean().optional().default(true),
|
||||||
path: z.string().optional(),
|
path: z.string().optional(),
|
||||||
scheme: z.string().optional(),
|
scheme: z.string().optional(),
|
||||||
mode: z.string().default("http"),
|
mode: z.string().default("http"),
|
||||||
interval: z.number().int().default(30),
|
interval: z.int().default(30),
|
||||||
"unhealthy-interval": z.number().int().default(30),
|
"unhealthy-interval": z.int().default(30),
|
||||||
unhealthyInterval: z.number().int().optional(), // deprecated alias
|
unhealthyInterval: z.int().optional(), // deprecated alias
|
||||||
timeout: z.number().int().default(5),
|
timeout: z.int().default(5),
|
||||||
headers: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional().default(null),
|
headers: z.array(z.object({ name: z.string(), value: z.string() })).nullable().optional().default(null),
|
||||||
"follow-redirects": z.boolean().default(true),
|
"follow-redirects": z.boolean().default(true),
|
||||||
followRedirects: z.boolean().optional(), // deprecated alias
|
followRedirects: z.boolean().optional(), // deprecated alias
|
||||||
method: z.string().default("GET"),
|
method: z.string().default("GET"),
|
||||||
status: z.number().int().optional()
|
status: z.int().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Schema for individual target within a resource
|
// Schema for individual target within a resource
|
||||||
@@ -28,16 +28,16 @@ export const TargetSchema = z.object({
|
|||||||
site: z.string().optional(),
|
site: z.string().optional(),
|
||||||
method: z.enum(["http", "https", "h2c"]).optional(),
|
method: z.enum(["http", "https", "h2c"]).optional(),
|
||||||
hostname: z.string(),
|
hostname: z.string(),
|
||||||
port: z.number().int().min(1).max(65535),
|
port: z.int().min(1).max(65535),
|
||||||
enabled: z.boolean().optional().default(true),
|
enabled: z.boolean().optional().default(true),
|
||||||
"internal-port": z.number().int().min(1).max(65535).optional(),
|
"internal-port": z.int().min(1).max(65535).optional(),
|
||||||
path: z.string().optional(),
|
path: z.string().optional(),
|
||||||
"path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable(),
|
"path-match": z.enum(["exact", "prefix", "regex"]).optional().nullable(),
|
||||||
healthcheck: TargetHealthCheckSchema.optional(),
|
healthcheck: TargetHealthCheckSchema.optional(),
|
||||||
rewritePath: z.string().optional(), // deprecated alias
|
rewritePath: z.string().optional(), // deprecated alias
|
||||||
"rewrite-path": z.string().optional(),
|
"rewrite-path": z.string().optional(),
|
||||||
"rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(),
|
"rewrite-match": z.enum(["exact", "prefix", "regex", "stripPrefix"]).optional().nullable(),
|
||||||
priority: z.number().int().min(1).max(1000).optional().default(100)
|
priority: z.int().min(1).max(1000).optional().default(100)
|
||||||
});
|
});
|
||||||
export type TargetData = z.infer<typeof TargetSchema>;
|
export type TargetData = z.infer<typeof TargetSchema>;
|
||||||
|
|
||||||
@@ -55,10 +55,10 @@ export const AuthSchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default([])
|
.default([])
|
||||||
.refine((roles) => !roles.includes("Admin"), {
|
.refine((roles) => !roles.includes("Admin"), {
|
||||||
message: "Admin role cannot be included in sso-roles"
|
error: "Admin role cannot be included in sso-roles"
|
||||||
}),
|
}),
|
||||||
"sso-users": z.array(z.string().email()).optional().default([]),
|
"sso-users": z.array(z.email()).optional().default([]),
|
||||||
"whitelist-users": z.array(z.string().email()).optional().default([]),
|
"whitelist-users": z.array(z.email()).optional().default([]),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const RuleSchema = z.object({
|
export const RuleSchema = z.object({
|
||||||
@@ -79,7 +79,7 @@ export const ResourceSchema = z
|
|||||||
protocol: z.enum(["http", "tcp", "udp"]).optional(),
|
protocol: z.enum(["http", "tcp", "udp"]).optional(),
|
||||||
ssl: z.boolean().optional(),
|
ssl: z.boolean().optional(),
|
||||||
"full-domain": z.string().optional(),
|
"full-domain": z.string().optional(),
|
||||||
"proxy-port": z.number().int().min(1).max(65535).optional(),
|
"proxy-port": z.int().min(1).max(65535).optional(),
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
targets: z.array(TargetSchema.nullable()).optional().default([]),
|
targets: z.array(TargetSchema.nullable()).optional().default([]),
|
||||||
auth: AuthSchema.optional(),
|
auth: AuthSchema.optional(),
|
||||||
@@ -100,9 +100,8 @@ export const ResourceSchema = z
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message:
|
path: ["name", "protocol"],
|
||||||
"Resource must either be targets-only (only 'targets' field) or have both 'name' and 'protocol' fields at a minimum",
|
error: "Resource must either be targets-only (only 'targets' field) or have both 'name' and 'protocol' fields at a minimum"
|
||||||
path: ["name", "protocol"]
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
@@ -117,6 +116,20 @@ export const ResourceSchema = z
|
|||||||
(target) => target == null || target.method !== undefined
|
(target) => target == null || target.method !== undefined
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ["targets"],
|
||||||
|
error: "When protocol is 'http', all targets must have a 'method' field"
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.refine(
|
||||||
|
(resource) => {
|
||||||
|
if (isTargetsOnlyResource(resource)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// If protocol is tcp or udp, no target should have method field
|
// If protocol is tcp or udp, no target should have method field
|
||||||
if (resource.protocol === "tcp" || resource.protocol === "udp") {
|
if (resource.protocol === "tcp" || resource.protocol === "udp") {
|
||||||
return resource.targets.every(
|
return resource.targets.every(
|
||||||
@@ -125,19 +138,9 @@ export const ResourceSchema = z
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
(resource) => {
|
{
|
||||||
if (resource.protocol === "http") {
|
path: ["targets"],
|
||||||
return {
|
error: "When protocol is 'tcp' or 'udp', targets must not have a 'method' field"
|
||||||
message:
|
|
||||||
"When protocol is 'http', all targets must have a 'method' field",
|
|
||||||
path: ["targets"]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
message:
|
|
||||||
"When protocol is 'tcp' or 'udp', targets must not have a 'method' field",
|
|
||||||
path: ["targets"]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
@@ -156,9 +159,8 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message:
|
path: ["full-domain"],
|
||||||
"When protocol is 'http', a 'full-domain' must be provided",
|
error: "When protocol is 'http', a 'full-domain' must be provided"
|
||||||
path: ["full-domain"]
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
@@ -174,9 +176,8 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message:
|
path: ["proxy-port", "exit-node"],
|
||||||
"When protocol is 'tcp' or 'udp', 'proxy-port' must be provided",
|
error: "When protocol is 'tcp' or 'udp', 'proxy-port' must be provided"
|
||||||
path: ["proxy-port", "exit-node"]
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
@@ -193,9 +194,8 @@ export const ResourceSchema = z
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message:
|
path: ["auth"],
|
||||||
"When protocol is 'tcp' or 'udp', 'auth' must not be provided",
|
error: "When protocol is 'tcp' or 'udp', 'auth' must not be provided"
|
||||||
path: ["auth"]
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -216,36 +216,12 @@ export const ClientResourceSchema = z.object({
|
|||||||
// Schema for the entire configuration object
|
// Schema for the entire configuration object
|
||||||
export const ConfigSchema = z
|
export const ConfigSchema = z
|
||||||
.object({
|
.object({
|
||||||
"proxy-resources": z.record(z.string(), ResourceSchema).optional().default({}),
|
"proxy-resources": z.record(z.string(), ResourceSchema).optional().prefault({}),
|
||||||
"client-resources": z.record(z.string(), ClientResourceSchema).optional().default({}),
|
"client-resources": z.record(z.string(), ClientResourceSchema).optional().prefault({}),
|
||||||
sites: z.record(z.string(), SiteSchema).optional().default({})
|
sites: z.record(z.string(), SiteSchema).optional().prefault({})
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
// Enforce the full-domain uniqueness across resources in the same stack
|
// Enforce the full-domain uniqueness across resources in the same stack
|
||||||
(config) => {
|
|
||||||
// Extract all full-domain values with their resource keys
|
|
||||||
const fullDomainMap = new Map<string, string[]>();
|
|
||||||
|
|
||||||
Object.entries(config["proxy-resources"]).forEach(
|
|
||||||
([resourceKey, resource]) => {
|
|
||||||
const fullDomain = resource["full-domain"];
|
|
||||||
if (fullDomain) {
|
|
||||||
// Only process if full-domain is defined
|
|
||||||
if (!fullDomainMap.has(fullDomain)) {
|
|
||||||
fullDomainMap.set(fullDomain, []);
|
|
||||||
}
|
|
||||||
fullDomainMap.get(fullDomain)!.push(resourceKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find duplicates
|
|
||||||
const duplicates = Array.from(fullDomainMap.entries()).filter(
|
|
||||||
([_, resourceKeys]) => resourceKeys.length > 1
|
|
||||||
);
|
|
||||||
|
|
||||||
return duplicates.length === 0;
|
|
||||||
},
|
|
||||||
(config) => {
|
(config) => {
|
||||||
// Extract duplicates for error message
|
// Extract duplicates for error message
|
||||||
const fullDomainMap = new Map<string, string[]>();
|
const fullDomainMap = new Map<string, string[]>();
|
||||||
@@ -271,38 +247,16 @@ export const ConfigSchema = z
|
|||||||
)
|
)
|
||||||
.join("; ");
|
.join("; ");
|
||||||
|
|
||||||
|
if (duplicates.length !== 0) {
|
||||||
return {
|
return {
|
||||||
message: `Duplicate 'full-domain' values found: ${duplicates}`,
|
path: ["resources"],
|
||||||
path: ["resources"]
|
error: `Duplicate 'full-domain' values found: ${duplicates}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
// Enforce proxy-port uniqueness within proxy-resources per protocol
|
// Enforce proxy-port uniqueness within proxy-resources per protocol
|
||||||
(config) => {
|
|
||||||
const protocolPortMap = new Map<string, string[]>();
|
|
||||||
|
|
||||||
Object.entries(config["proxy-resources"]).forEach(
|
|
||||||
([resourceKey, resource]) => {
|
|
||||||
const proxyPort = resource["proxy-port"];
|
|
||||||
const protocol = resource.protocol;
|
|
||||||
if (proxyPort !== undefined && protocol !== undefined) {
|
|
||||||
const key = `${protocol}:${proxyPort}`;
|
|
||||||
if (!protocolPortMap.has(key)) {
|
|
||||||
protocolPortMap.set(key, []);
|
|
||||||
}
|
|
||||||
protocolPortMap.get(key)!.push(resourceKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find duplicates
|
|
||||||
const duplicates = Array.from(protocolPortMap.entries()).filter(
|
|
||||||
([_, resourceKeys]) => resourceKeys.length > 1
|
|
||||||
);
|
|
||||||
|
|
||||||
return duplicates.length === 0;
|
|
||||||
},
|
|
||||||
(config) => {
|
(config) => {
|
||||||
// Extract duplicates for error message
|
// Extract duplicates for error message
|
||||||
const protocolPortMap = new Map<string, string[]>();
|
const protocolPortMap = new Map<string, string[]>();
|
||||||
@@ -331,36 +285,16 @@ export const ConfigSchema = z
|
|||||||
)
|
)
|
||||||
.join("; ");
|
.join("; ");
|
||||||
|
|
||||||
|
if (duplicates.length !== 0) {
|
||||||
return {
|
return {
|
||||||
message: `Duplicate 'proxy-port' values found in proxy-resources: ${duplicates}`,
|
path: ["proxy-resources"],
|
||||||
path: ["proxy-resources"]
|
error: `Duplicate 'proxy-port' values found in proxy-resources: ${duplicates}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
// Enforce proxy-port uniqueness within client-resources
|
// Enforce proxy-port uniqueness within client-resources
|
||||||
(config) => {
|
|
||||||
const proxyPortMap = new Map<number, string[]>();
|
|
||||||
|
|
||||||
Object.entries(config["client-resources"]).forEach(
|
|
||||||
([resourceKey, resource]) => {
|
|
||||||
const proxyPort = resource["proxy-port"];
|
|
||||||
if (proxyPort !== undefined) {
|
|
||||||
if (!proxyPortMap.has(proxyPort)) {
|
|
||||||
proxyPortMap.set(proxyPort, []);
|
|
||||||
}
|
|
||||||
proxyPortMap.get(proxyPort)!.push(resourceKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find duplicates
|
|
||||||
const duplicates = Array.from(proxyPortMap.entries()).filter(
|
|
||||||
([_, resourceKeys]) => resourceKeys.length > 1
|
|
||||||
);
|
|
||||||
|
|
||||||
return duplicates.length === 0;
|
|
||||||
},
|
|
||||||
(config) => {
|
(config) => {
|
||||||
// Extract duplicates for error message
|
// Extract duplicates for error message
|
||||||
const proxyPortMap = new Map<number, string[]>();
|
const proxyPortMap = new Map<number, string[]>();
|
||||||
@@ -385,11 +319,13 @@ export const ConfigSchema = z
|
|||||||
)
|
)
|
||||||
.join("; ");
|
.join("; ");
|
||||||
|
|
||||||
|
if (duplicates.length !== 0) {
|
||||||
return {
|
return {
|
||||||
message: `Duplicate 'proxy-port' values found in client-resources: ${duplicates}`,
|
path: ["client-resources"],
|
||||||
path: ["client-resources"]
|
error: `Duplicate 'proxy-port' values found in client-resources: ${duplicates}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Type inference from the schema
|
// Type inference from the schema
|
||||||
|
|||||||
@@ -89,6 +89,16 @@ export class Config {
|
|||||||
? "true"
|
? "true"
|
||||||
: "false";
|
: "false";
|
||||||
|
|
||||||
|
process.env.PRODUCT_UPDATES_NOTIFICATION_ENABLED = parsedConfig.app
|
||||||
|
.notifications.product_updates
|
||||||
|
? "true"
|
||||||
|
: "false";
|
||||||
|
|
||||||
|
process.env.NEW_RELEASES_NOTIFICATION_ENABLED = parsedConfig.app
|
||||||
|
.notifications.new_releases
|
||||||
|
? "true"
|
||||||
|
: "false";
|
||||||
|
|
||||||
if (parsedConfig.server.maxmind_db_path) {
|
if (parsedConfig.server.maxmind_db_path) {
|
||||||
process.env.MAXMIND_DB_PATH = parsedConfig.server.maxmind_db_path;
|
process.env.MAXMIND_DB_PATH = parsedConfig.server.maxmind_db_path;
|
||||||
}
|
}
|
||||||
@@ -158,7 +168,7 @@ export class Config {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"https://api.fossorial.io/api/v1/license/validate",
|
`https://api.fossorial.io/api/v1/license/validate`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ export function generateRemoteSubnetsStr(allSiteResources: SiteResource[]) {
|
|||||||
if (sr.mode === "cidr") return true;
|
if (sr.mode === "cidr") return true;
|
||||||
if (sr.mode === "host") {
|
if (sr.mode === "host") {
|
||||||
// check if its a valid IP using zod
|
// check if its a valid IP using zod
|
||||||
const ipSchema = z.string().ip();
|
const ipSchema = z.union([z.ipv4(), z.ipv6()]);
|
||||||
const parseResult = ipSchema.safeParse(sr.destination);
|
const parseResult = ipSchema.safeParse(sr.destination);
|
||||||
return parseResult.success;
|
return parseResult.success;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ export const configSchema = z
|
|||||||
.object({
|
.object({
|
||||||
app: z
|
app: z
|
||||||
.object({
|
.object({
|
||||||
dashboard_url: z
|
dashboard_url: z.url()
|
||||||
.string()
|
.pipe(z.url())
|
||||||
.url()
|
|
||||||
.pipe(z.string().url())
|
|
||||||
.transform((url) => url.toLowerCase())
|
.transform((url) => url.toLowerCase())
|
||||||
.optional(),
|
.optional(),
|
||||||
log_level: z
|
log_level: z
|
||||||
@@ -31,7 +29,14 @@ export const configSchema = z
|
|||||||
anonymous_usage: z.boolean().optional().default(true)
|
anonymous_usage: z.boolean().optional().default(true)
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({})
|
.prefault({}),
|
||||||
|
notifications: z
|
||||||
|
.object({
|
||||||
|
product_updates: z.boolean().optional().default(true),
|
||||||
|
new_releases: z.boolean().optional().default(true)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.prefault({})
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({
|
.default({
|
||||||
@@ -40,6 +45,10 @@ export const configSchema = z
|
|||||||
log_failed_attempts: false,
|
log_failed_attempts: false,
|
||||||
telemetry: {
|
telemetry: {
|
||||||
anonymous_usage: true
|
anonymous_usage: true
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
product_updates: true,
|
||||||
|
new_releases: true
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
domains: z
|
domains: z
|
||||||
@@ -96,7 +105,7 @@ export const configSchema = z
|
|||||||
token: z.string().optional().default("P-Access-Token")
|
token: z.string().optional().default("P-Access-Token")
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.prefault({}),
|
||||||
resource_session_request_param: z
|
resource_session_request_param: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -121,7 +130,7 @@ export const configSchema = z
|
|||||||
credentials: z.boolean().optional()
|
credentials: z.boolean().optional()
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
trust_proxy: z.number().int().gte(0).optional().default(1),
|
trust_proxy: z.int().gte(0).optional().default(1),
|
||||||
secret: z.string().pipe(z.string().min(8)).optional(),
|
secret: z.string().pipe(z.string().min(8)).optional(),
|
||||||
maxmind_db_path: z.string().optional()
|
maxmind_db_path: z.string().optional()
|
||||||
})
|
})
|
||||||
@@ -178,7 +187,7 @@ export const configSchema = z
|
|||||||
.default(5000)
|
.default(5000)
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({})
|
.prefault({})
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
traefik: z
|
traefik: z
|
||||||
@@ -205,10 +214,13 @@ 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()
|
||||||
.default({}),
|
.prefault({}),
|
||||||
gerbil: z
|
gerbil: z
|
||||||
.object({
|
.object({
|
||||||
exit_node_name: z.string().optional(),
|
exit_node_name: z.string().optional(),
|
||||||
@@ -233,7 +245,7 @@ export const configSchema = z
|
|||||||
.default(30)
|
.default(30)
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.prefault({}),
|
||||||
orgs: z
|
orgs: z
|
||||||
.object({
|
.object({
|
||||||
block_size: z.number().positive().gt(0).optional().default(24),
|
block_size: z.number().positive().gt(0).optional().default(24),
|
||||||
@@ -262,7 +274,7 @@ export const configSchema = z
|
|||||||
.default(500)
|
.default(500)
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.prefault({}),
|
||||||
auth: z
|
auth: z
|
||||||
.object({
|
.object({
|
||||||
window_minutes: z
|
window_minutes: z
|
||||||
@@ -279,10 +291,10 @@ export const configSchema = z
|
|||||||
.default(500)
|
.default(500)
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({})
|
.prefault({})
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.prefault({}),
|
||||||
email: z
|
email: z
|
||||||
.object({
|
.object({
|
||||||
smtp_host: z.string().optional(),
|
smtp_host: z.string().optional(),
|
||||||
@@ -294,7 +306,7 @@ export const configSchema = z
|
|||||||
.transform(getEnvOrYaml("EMAIL_SMTP_PASS")),
|
.transform(getEnvOrYaml("EMAIL_SMTP_PASS")),
|
||||||
smtp_secure: z.boolean().optional(),
|
smtp_secure: z.boolean().optional(),
|
||||||
smtp_tls_reject_unauthorized: z.boolean().optional(),
|
smtp_tls_reject_unauthorized: z.boolean().optional(),
|
||||||
no_reply: z.string().email().optional()
|
no_reply: z.email().optional()
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
flags: z
|
flags: z
|
||||||
@@ -315,11 +327,18 @@ export const configSchema = z
|
|||||||
nameservers: z
|
nameservers: z
|
||||||
.array(z.string().optional().optional())
|
.array(z.string().optional().optional())
|
||||||
.optional()
|
.optional()
|
||||||
.default(["ns1.pangolin.net", "ns2.pangolin.net", "ns3.pangolin.net"]),
|
.default([
|
||||||
cname_extension: z.string().optional().default("cname.pangolin.net")
|
"ns1.pangolin.net",
|
||||||
|
"ns2.pangolin.net",
|
||||||
|
"ns3.pangolin.net"
|
||||||
|
]),
|
||||||
|
cname_extension: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.default("cname.pangolin.net")
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({})
|
.prefault({})
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
@@ -334,7 +353,7 @@ export const configSchema = z
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: "At least one domain must be defined"
|
error: "At least one domain must be defined"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
@@ -349,7 +368,7 @@ export const configSchema = z
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: "Server secret must be defined"
|
error: "Server secret must be defined"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
@@ -361,7 +380,7 @@ export const configSchema = z
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
message: "Dashboard URL must be defined"
|
error: "Dashboard URL must be defined"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ class TelemetryClient {
|
|||||||
license_tier: licenseStatus.tier || "unknown"
|
license_tier: licenseStatus.tier || "unknown"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
logger.debug("Sending enterprise startup telemtry payload:", {
|
logger.debug("Sending enterprise startup telemetry payload:", {
|
||||||
payload
|
payload
|
||||||
});
|
});
|
||||||
// this.client.capture(payload);
|
// this.client.capture(payload);
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
import ipaddr from "ipaddr.js";
|
||||||
|
|
||||||
export function isValidCIDR(cidr: string): boolean {
|
export function isValidCIDR(cidr: string): boolean {
|
||||||
return z.string().cidr().safeParse(cidr).success;
|
return z.cidrv4().safeParse(cidr).success || z.cidrv6().safeParse(cidr).success;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidIP(ip: string): boolean {
|
export function isValidIP(ip: string): boolean {
|
||||||
return z.string().ip().safeParse(ip).success;
|
return z.ipv4().safeParse(ip).success || z.ipv6().safeParse(ip).success;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidUrlGlobPattern(pattern: string): boolean {
|
export function isValidUrlGlobPattern(pattern: string): boolean {
|
||||||
@@ -83,12 +84,15 @@ export function isTargetValid(value: string | undefined) {
|
|||||||
|
|
||||||
const DOMAIN_REGEX =
|
const DOMAIN_REGEX =
|
||||||
/^[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 IPV4_REGEX =
|
// const IPV4_REGEX =
|
||||||
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
// /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||||
const IPV6_REGEX = /^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$/i;
|
// const IPV6_REGEX = /^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$/i;
|
||||||
|
|
||||||
if (IPV4_REGEX.test(value) || IPV6_REGEX.test(value)) {
|
try {
|
||||||
return true;
|
const addr = ipaddr.parse(value);
|
||||||
|
return addr.kind() === "ipv4" || addr.kind() === "ipv6";
|
||||||
|
} catch {
|
||||||
|
// fall through to domain regex check
|
||||||
}
|
}
|
||||||
|
|
||||||
return DOMAIN_REGEX.test(value);
|
return DOMAIN_REGEX.test(value);
|
||||||
|
|||||||
@@ -50,14 +50,14 @@ export const privateConfigSchema = z.object({
|
|||||||
host: z.string(),
|
host: z.string(),
|
||||||
port: portSchema,
|
port: portSchema,
|
||||||
password: z.string().optional(),
|
password: z.string().optional(),
|
||||||
db: z.number().int().nonnegative().optional().default(0),
|
db: z.int().nonnegative().optional().default(0),
|
||||||
replicas: z
|
replicas: z
|
||||||
.array(
|
.array(
|
||||||
z.object({
|
z.object({
|
||||||
host: z.string(),
|
host: z.string(),
|
||||||
port: portSchema,
|
port: portSchema,
|
||||||
password: z.string().optional(),
|
password: z.string().optional(),
|
||||||
db: z.number().int().nonnegative().optional().default(0)
|
db: z.int().nonnegative().optional().default(0)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.optional()
|
.optional()
|
||||||
@@ -79,14 +79,14 @@ export const privateConfigSchema = z.object({
|
|||||||
.default("http://gerbil:3004")
|
.default("http://gerbil:3004")
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.prefault({}),
|
||||||
flags: z
|
flags: z
|
||||||
.object({
|
.object({
|
||||||
enable_redis: z.boolean().optional().default(false),
|
enable_redis: z.boolean().optional().default(false),
|
||||||
use_pangolin_dns: z.boolean().optional().default(false)
|
use_pangolin_dns: z.boolean().optional().default(false)
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.default({}),
|
.prefault({}),
|
||||||
branding: z
|
branding: z
|
||||||
.object({
|
.object({
|
||||||
app_name: z.string().optional(),
|
app_name: z.string().optional(),
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ export const queryAccessAuditLogsQuery = z.object({
|
|||||||
timeStart: z
|
timeStart: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => !isNaN(Date.parse(val)), {
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
message: "timeStart must be a valid ISO date string"
|
error: "timeStart must be a valid ISO date string"
|
||||||
})
|
})
|
||||||
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
|
||||||
timeEnd: z
|
timeEnd: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => !isNaN(Date.parse(val)), {
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
message: "timeEnd must be a valid ISO date string"
|
error: "timeEnd must be a valid ISO date string"
|
||||||
})
|
})
|
||||||
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
||||||
.optional()
|
.optional()
|
||||||
.default(new Date().toISOString()),
|
.prefault(new Date().toISOString()),
|
||||||
action: z
|
action: z
|
||||||
.union([z.boolean(), z.string()])
|
.union([z.boolean(), z.string()])
|
||||||
.transform((val) => (typeof val === "string" ? val === "true" : val))
|
.transform((val) => (typeof val === "string" ? val === "true" : val))
|
||||||
@@ -51,7 +51,7 @@ export const queryAccessAuditLogsQuery = z.object({
|
|||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive())
|
.pipe(z.int().positive())
|
||||||
.optional(),
|
.optional(),
|
||||||
actor: z.string().optional(),
|
actor: z.string().optional(),
|
||||||
type: z.string().optional(),
|
type: z.string().optional(),
|
||||||
@@ -61,13 +61,13 @@ export const queryAccessAuditLogsQuery = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
export const queryAccessAuditLogsParams = z.object({
|
export const queryAccessAuditLogsParams = z.object({
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ export const queryActionAuditLogsQuery = z.object({
|
|||||||
timeStart: z
|
timeStart: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => !isNaN(Date.parse(val)), {
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
message: "timeStart must be a valid ISO date string"
|
error: "timeStart must be a valid ISO date string"
|
||||||
})
|
})
|
||||||
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
|
||||||
timeEnd: z
|
timeEnd: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => !isNaN(Date.parse(val)), {
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
message: "timeEnd must be a valid ISO date string"
|
error: "timeEnd must be a valid ISO date string"
|
||||||
})
|
})
|
||||||
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
||||||
.optional()
|
.optional()
|
||||||
.default(new Date().toISOString()),
|
.prefault(new Date().toISOString()),
|
||||||
action: z.string().optional(),
|
action: z.string().optional(),
|
||||||
actorType: z.string().optional(),
|
actorType: z.string().optional(),
|
||||||
actorId: z.string().optional(),
|
actorId: z.string().optional(),
|
||||||
@@ -50,13 +50,13 @@ export const queryActionAuditLogsQuery = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
export const queryActionAuditLogsParams = z.object({
|
export const queryActionAuditLogsParams = z.object({
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { response } from "@server/lib/response";
|
|||||||
import { encrypt } from "@server/lib/crypto";
|
import { encrypt } from "@server/lib/crypto";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
const paramsSchema = z.object({}).strict();
|
const paramsSchema = z.strictObject({});
|
||||||
|
|
||||||
export type GetSessionTransferTokenRenponse = {
|
export type GetSessionTransferTokenRenponse = {
|
||||||
token: string;
|
token: string;
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ import { isTargetValid } from "@server/lib/validators";
|
|||||||
import { listExitNodes } from "#private/lib/exitNodes";
|
import { listExitNodes } from "#private/lib/exitNodes";
|
||||||
|
|
||||||
const bodySchema = z.object({
|
const bodySchema = z.object({
|
||||||
email: z.string().toLowerCase().email(),
|
email: z.email().toLowerCase(),
|
||||||
ip: z.string().refine(isTargetValid),
|
ip: z.string().refine(isTargetValid),
|
||||||
method: z.enum(["http", "https"]),
|
method: z.enum(["http", "https"]),
|
||||||
port: z.number().int().min(1).max(65535),
|
port: z.int().min(1).max(65535),
|
||||||
pincode: z
|
pincode: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^\d{6}$/)
|
.regex(/^\d{6}$/)
|
||||||
|
|||||||
@@ -25,11 +25,9 @@ import stripe from "#private/lib/stripe";
|
|||||||
import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/billing";
|
import { getLineItems, getStandardFeaturePriceSet } from "@server/lib/billing";
|
||||||
import { getTierPriceSet, TierId } from "@server/lib/billing/tiers";
|
import { getTierPriceSet, TierId } from "@server/lib/billing/tiers";
|
||||||
|
|
||||||
const createCheckoutSessionSchema = z
|
const createCheckoutSessionSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export async function createCheckoutSession(
|
export async function createCheckoutSession(
|
||||||
req: Request,
|
req: Request,
|
||||||
|
|||||||
@@ -23,11 +23,9 @@ import config from "@server/lib/config";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import stripe from "#private/lib/stripe";
|
import stripe from "#private/lib/stripe";
|
||||||
|
|
||||||
const createPortalSessionSchema = z
|
const createPortalSessionSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export async function createPortalSession(
|
export async function createPortalSession(
|
||||||
req: Request,
|
req: Request,
|
||||||
|
|||||||
@@ -33,11 +33,9 @@ import {
|
|||||||
SubscriptionItem
|
SubscriptionItem
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
|
|
||||||
const getOrgSchema = z
|
const getOrgSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "get",
|
method: "get",
|
||||||
|
|||||||
@@ -27,11 +27,9 @@ import { usageService } from "@server/lib/billing/usageService";
|
|||||||
import { FeatureId } from "@server/lib/billing";
|
import { FeatureId } from "@server/lib/billing";
|
||||||
import { GetOrgUsageResponse } from "@server/routers/billing/types";
|
import { GetOrgUsageResponse } from "@server/routers/billing/types";
|
||||||
|
|
||||||
const getOrgSchema = z
|
const getOrgSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "get",
|
method: "get",
|
||||||
|
|||||||
@@ -21,11 +21,9 @@ import { fromZodError } from "zod-validation-error";
|
|||||||
import { getOrgTierData } from "#private/lib/billing";
|
import { getOrgTierData } from "#private/lib/billing";
|
||||||
import { GetOrgTierResponse } from "@server/routers/billing/types";
|
import { GetOrgTierResponse } from "@server/routers/billing/types";
|
||||||
|
|
||||||
const getOrgSchema = z
|
const getOrgSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export async function getOrgTier(
|
export async function getOrgTier(
|
||||||
req: Request,
|
req: Request,
|
||||||
|
|||||||
@@ -23,13 +23,11 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { registry } from "@server/openApi";
|
import { registry } from "@server/openApi";
|
||||||
import { GetCertificateResponse } from "@server/routers/certificates/types";
|
import { GetCertificateResponse } from "@server/routers/certificates/types";
|
||||||
|
|
||||||
const getCertificateSchema = z
|
const getCertificateSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
domainId: z.string(),
|
domainId: z.string(),
|
||||||
domain: z.string().min(1).max(255),
|
domain: z.string().min(1).max(255),
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(domainId: string, domain: string) {
|
async function query(domainId: string, domain: string) {
|
||||||
const [domainRecord] = await db
|
const [domainRecord] = await db
|
||||||
|
|||||||
@@ -24,12 +24,10 @@ import stoi from "@server/lib/stoi";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const restartCertificateParamsSchema = z
|
const restartCertificateParamsSchema = z.strictObject({
|
||||||
.object({
|
certId: z.string().transform(stoi).pipe(z.int().positive()),
|
||||||
certId: z.string().transform(stoi).pipe(z.number().int().positive()),
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "post",
|
method: "post",
|
||||||
@@ -41,7 +39,7 @@ registry.registerPath({
|
|||||||
certId: z
|
certId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(stoi)
|
.transform(stoi)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,13 +23,11 @@ import { db, domainNamespaces, resources } from "@server/db";
|
|||||||
import { inArray } from "drizzle-orm";
|
import { inArray } from "drizzle-orm";
|
||||||
import { CheckDomainAvailabilityResponse } from "@server/routers/domain/types";
|
import { CheckDomainAvailabilityResponse } from "@server/routers/domain/types";
|
||||||
|
|
||||||
const paramsSchema = z.object({}).strict();
|
const paramsSchema = z.strictObject({});
|
||||||
|
|
||||||
const querySchema = z
|
const querySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
subdomain: z.string()
|
subdomain: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "get",
|
method: "get",
|
||||||
|
|||||||
@@ -23,24 +23,22 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const paramsSchema = z.object({}).strict();
|
const paramsSchema = z.strictObject({});
|
||||||
|
|
||||||
const querySchema = z
|
const querySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
limit: z
|
limit: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative()),
|
.pipe(z.int().nonnegative()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(limit: number, offset: number) {
|
async function query(limit: number, offset: number) {
|
||||||
const res = await db
|
const res = await db
|
||||||
|
|||||||
@@ -23,11 +23,15 @@ import * as license from "#private/routers/license";
|
|||||||
import * as generateLicense from "./generatedLicense";
|
import * as generateLicense from "./generatedLicense";
|
||||||
import * as logs from "#private/routers/auditLogs";
|
import * as logs from "#private/routers/auditLogs";
|
||||||
import * as misc from "#private/routers/misc";
|
import * as misc from "#private/routers/misc";
|
||||||
|
import * as reKey from "#private/routers/re-key";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
verifyOrgAccess,
|
verifyOrgAccess,
|
||||||
verifyUserHasAction,
|
verifyUserHasAction,
|
||||||
verifyUserIsServerAdmin
|
verifyUserIsServerAdmin,
|
||||||
|
verifySiteAccess,
|
||||||
|
verifyClientAccess,
|
||||||
|
verifyClientsEnabled,
|
||||||
} from "@server/middlewares";
|
} from "@server/middlewares";
|
||||||
import { ActionsEnum } from "@server/auth/actions";
|
import { ActionsEnum } from "@server/auth/actions";
|
||||||
import {
|
import {
|
||||||
@@ -403,3 +407,26 @@ authenticated.get(
|
|||||||
logActionAudit(ActionsEnum.exportLogs),
|
logActionAudit(ActionsEnum.exportLogs),
|
||||||
logs.exportAccessAuditLogs
|
logs.exportAccessAuditLogs
|
||||||
);
|
);
|
||||||
|
|
||||||
|
authenticated.post(
|
||||||
|
"/re-key/:clientId/regenerate-client-secret",
|
||||||
|
verifyClientsEnabled,
|
||||||
|
verifyClientAccess,
|
||||||
|
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
||||||
|
reKey.reGenerateClientSecret
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.post(
|
||||||
|
"/re-key/:siteId/regenerate-site-secret",
|
||||||
|
verifySiteAccess,
|
||||||
|
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
||||||
|
reKey.reGenerateSiteSecret
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.put(
|
||||||
|
"/re-key/:orgId/reGenerate-remote-exit-node-secret",
|
||||||
|
verifyValidLicense,
|
||||||
|
verifyOrgAccess,
|
||||||
|
verifyUserHasAction(ActionsEnum.updateRemoteExitNode),
|
||||||
|
reKey.reGenerateExitNodeSecret
|
||||||
|
);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ async function createNewLicense(orgId: string, licenseData: any): Promise<any> {
|
|||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
logger.debug("Fossorial API response:", {data});
|
logger.debug("Fossorial API response:", { data });
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating new license:", error);
|
console.error("Error creating new license:", error);
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ import createHttpError from "http-errors";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { response as sendResponse } from "@server/lib/response";
|
import { response as sendResponse } from "@server/lib/response";
|
||||||
import privateConfig from "#private/lib/config";
|
import privateConfig from "#private/lib/config";
|
||||||
import { GeneratedLicenseKey, ListGeneratedLicenseKeysResponse } from "@server/routers/generatedLicense/types";
|
import {
|
||||||
|
GeneratedLicenseKey,
|
||||||
|
ListGeneratedLicenseKeysResponse
|
||||||
|
} from "@server/routers/generatedLicense/types";
|
||||||
|
|
||||||
async function fetchLicenseKeys(orgId: string): Promise<any> {
|
async function fetchLicenseKeys(orgId: string): Promise<any> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -78,105 +78,78 @@ import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToke
|
|||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
|
|
||||||
// Zod schemas for request validation
|
// Zod schemas for request validation
|
||||||
const getResourceByDomainParamsSchema = z
|
const getResourceByDomainParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
domain: z.string().min(1, "Domain is required")
|
domain: z.string().min(1, "Domain is required")
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const getUserSessionParamsSchema = z
|
const getUserSessionParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
userSessionId: z.string().min(1, "User session ID is required")
|
userSessionId: z.string().min(1, "User session ID is required")
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const getUserOrgRoleParamsSchema = z
|
const getUserOrgRoleParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
userId: z.string().min(1, "User ID is required"),
|
userId: z.string().min(1, "User ID is required"),
|
||||||
orgId: z.string().min(1, "Organization ID is required")
|
orgId: z.string().min(1, "Organization ID is required")
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const getRoleResourceAccessParamsSchema = z
|
const getRoleResourceAccessParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
roleId: z
|
roleId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(
|
.pipe(
|
||||||
z.number().int().positive("Role ID must be a positive integer")
|
z.int().positive("Role ID must be a positive integer")
|
||||||
),
|
),
|
||||||
resourceId: z
|
resourceId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(
|
.pipe(
|
||||||
z
|
z.int()
|
||||||
.number()
|
|
||||||
.int()
|
|
||||||
.positive("Resource ID must be a positive integer")
|
.positive("Resource ID must be a positive integer")
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const getUserResourceAccessParamsSchema = z
|
const getUserResourceAccessParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
userId: z.string().min(1, "User ID is required"),
|
userId: z.string().min(1, "User ID is required"),
|
||||||
resourceId: z
|
resourceId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(
|
.pipe(
|
||||||
z
|
z.int()
|
||||||
.number()
|
|
||||||
.int()
|
|
||||||
.positive("Resource ID must be a positive integer")
|
.positive("Resource ID must be a positive integer")
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const getResourceRulesParamsSchema = z
|
const getResourceRulesParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
resourceId: z
|
resourceId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(
|
.pipe(
|
||||||
z
|
z.int()
|
||||||
.number()
|
|
||||||
.int()
|
|
||||||
.positive("Resource ID must be a positive integer")
|
.positive("Resource ID must be a positive integer")
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const validateResourceSessionTokenParamsSchema = z
|
const validateResourceSessionTokenParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
resourceId: z
|
resourceId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(
|
.pipe(
|
||||||
z
|
z.int()
|
||||||
.number()
|
|
||||||
.int()
|
|
||||||
.positive("Resource ID must be a positive integer")
|
.positive("Resource ID must be a positive integer")
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const validateResourceSessionTokenBodySchema = z
|
const validateResourceSessionTokenBodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
token: z.string().min(1, "Token is required")
|
token: z.string().min(1, "Token is required")
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const validateResourceAccessTokenBodySchema = z
|
const validateResourceAccessTokenBodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
accessTokenId: z.string().optional(),
|
accessTokenId: z.string().optional(),
|
||||||
resourceId: z.number().optional(),
|
resourceId: z.number().optional(),
|
||||||
accessToken: z.string()
|
accessToken: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
// Certificates by domains query validation
|
// Certificates by domains query validation
|
||||||
const getCertificatesByDomainsQuerySchema = z
|
const getCertificatesByDomainsQuerySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
// Accept domains as string or array (domains or domains[])
|
// Accept domains as string or array (domains or domains[])
|
||||||
domains: z
|
domains: z
|
||||||
.union([z.array(z.string().min(1)), z.string().min(1)])
|
.union([z.array(z.string().min(1)), z.string().min(1)])
|
||||||
@@ -185,8 +158,7 @@ const getCertificatesByDomainsQuerySchema = z
|
|||||||
"domains[]": z
|
"domains[]": z
|
||||||
.union([z.array(z.string().min(1)), z.string().min(1)])
|
.union([z.array(z.string().min(1)), z.string().min(1)])
|
||||||
.optional()
|
.optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
// Type exports for request schemas
|
// Type exports for request schemas
|
||||||
export type GetResourceByDomainParams = z.infer<
|
export type GetResourceByDomainParams = z.infer<
|
||||||
@@ -591,11 +563,9 @@ hybridRouter.get(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const getOrgLoginPageParamsSchema = z
|
const getOrgLoginPageParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string().min(1)
|
orgId: z.string().min(1)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
hybridRouter.get(
|
hybridRouter.get(
|
||||||
"/org/:orgId/login-page",
|
"/org/:orgId/login-page",
|
||||||
@@ -1217,7 +1187,7 @@ hybridRouter.post(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const geoIpLookupParamsSchema = z.object({
|
const geoIpLookupParamsSchema = z.object({
|
||||||
ip: z.string().ip()
|
ip: z.union([z.ipv4(), z.ipv6()])
|
||||||
});
|
});
|
||||||
hybridRouter.get(
|
hybridRouter.get(
|
||||||
"/geoip/:ip",
|
"/geoip/:ip",
|
||||||
|
|||||||
@@ -20,11 +20,9 @@ import license from "#private/license/license";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
licenseKey: z.string().min(1).max(255)
|
licenseKey: z.string().min(1).max(255)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export async function activateLicense(
|
export async function activateLicense(
|
||||||
req: Request,
|
req: Request,
|
||||||
|
|||||||
@@ -23,11 +23,9 @@ import { eq } from "drizzle-orm";
|
|||||||
import { licenseKey } from "@server/db";
|
import { licenseKey } from "@server/db";
|
||||||
import license from "#private/license/license";
|
import license from "#private/license/license";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
licenseKey: z.string().min(1).max(255)
|
licenseKey: z.string().min(1).max(255)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export async function deleteLicenseKey(
|
export async function deleteLicenseKey(
|
||||||
req: Request,
|
req: Request,
|
||||||
|
|||||||
@@ -35,18 +35,14 @@ import { TierId } from "@server/lib/billing/tiers";
|
|||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import { CreateLoginPageResponse } from "@server/routers/loginPage/types";
|
import { CreateLoginPageResponse } from "@server/routers/loginPage/types";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
subdomain: z.string().nullable().optional(),
|
subdomain: z.string().nullable().optional(),
|
||||||
domainId: z.string()
|
domainId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type CreateLoginPageBody = z.infer<typeof bodySchema>;
|
export type CreateLoginPageBody = z.infer<typeof bodySchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { DeleteLoginPageResponse } from "@server/routers/loginPage/types";
|
|||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
orgId: z.string(),
|
orgId: z.string(),
|
||||||
loginPageId: z.coerce.number()
|
loginPageId: z.coerce.number<number>()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,9 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { GetLoginPageResponse } from "@server/routers/loginPage/types";
|
import { GetLoginPageResponse } from "@server/routers/loginPage/types";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(orgId: string) {
|
async function query(orgId: string) {
|
||||||
const [res] = await db
|
const [res] = await db
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { LoadLoginPageResponse } from "@server/routers/loginPage/types";
|
import { LoadLoginPageResponse } from "@server/routers/loginPage/types";
|
||||||
|
|
||||||
const querySchema = z.object({
|
const querySchema = z.object({
|
||||||
resourceId: z.coerce.number().int().positive().optional(),
|
resourceId: z.coerce.number<number>().int().positive().optional(),
|
||||||
idpId: z.coerce.number().int().positive().optional(),
|
idpId: z.coerce.number<number>().int().positive().optional(),
|
||||||
orgId: z.string().min(1).optional(),
|
orgId: z.string().min(1).optional(),
|
||||||
fullDomain: z.string().min(1)
|
fullDomain: z.string().min(1)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,18 +31,16 @@ import { UpdateLoginPageResponse } from "@server/routers/loginPage/types";
|
|||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
orgId: z.string(),
|
orgId: z.string(),
|
||||||
loginPageId: z.coerce.number()
|
loginPageId: z.coerce.number<number>()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
subdomain: subdomainSchema.nullable().optional(),
|
subdomain: subdomainSchema.nullable().optional(),
|
||||||
domainId: z.string().optional()
|
domainId: z.string().optional()
|
||||||
})
|
})
|
||||||
.strict()
|
|
||||||
.refine((data) => Object.keys(data).length > 0, {
|
.refine((data) => Object.keys(data).length > 0, {
|
||||||
message: "At least one field must be provided for update"
|
error: "At least one field must be provided for update"
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
@@ -51,7 +49,9 @@ const bodySchema = z
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
{ message: "Invalid subdomain" }
|
{
|
||||||
|
error: "Invalid subdomain"
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export type UpdateLoginPageBody = z.infer<typeof bodySchema>;
|
export type UpdateLoginPageBody = z.infer<typeof bodySchema>;
|
||||||
|
|||||||
@@ -22,12 +22,10 @@ import { sendEmail } from "@server/emails";
|
|||||||
import SupportEmail from "@server/emails/templates/SupportEmail";
|
import SupportEmail from "@server/emails/templates/SupportEmail";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
body: z.string().min(1),
|
body: z.string().min(1),
|
||||||
subject: z.string().min(1).max(255)
|
subject: z.string().min(1).max(255)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export async function sendSupportEmail(
|
export async function sendSupportEmail(
|
||||||
req: Request,
|
req: Request,
|
||||||
@@ -68,7 +66,7 @@ export async function sendSupportEmail(
|
|||||||
{
|
{
|
||||||
name: req.user?.email || "Support User",
|
name: req.user?.email || "Support User",
|
||||||
to: "support@pangolin.net",
|
to: "support@pangolin.net",
|
||||||
from: req.user?.email || config.getNoReplyEmail(),
|
from: config.getNoReplyEmail(),
|
||||||
subject: `Support Request: ${subject}`
|
subject: `Support Request: ${subject}`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,15 +29,14 @@ import { getOrgTierData } from "#private/lib/billing";
|
|||||||
import { TierId } from "@server/lib/billing/tiers";
|
import { TierId } from "@server/lib/billing/tiers";
|
||||||
import { CreateOrgIdpResponse } from "@server/routers/orgIdp/types";
|
import { CreateOrgIdpResponse } from "@server/routers/orgIdp/types";
|
||||||
|
|
||||||
const paramsSchema = z.object({ orgId: z.string().nonempty() }).strict();
|
const paramsSchema = z.strictObject({ orgId: z.string().nonempty() });
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
name: z.string().nonempty(),
|
name: z.string().nonempty(),
|
||||||
clientId: z.string().nonempty(),
|
clientId: z.string().nonempty(),
|
||||||
clientSecret: z.string().nonempty(),
|
clientSecret: z.string().nonempty(),
|
||||||
authUrl: z.string().url(),
|
authUrl: z.url(),
|
||||||
tokenUrl: z.string().url(),
|
tokenUrl: z.url(),
|
||||||
identifierPath: z.string().nonempty(),
|
identifierPath: z.string().nonempty(),
|
||||||
emailPath: z.string().optional(),
|
emailPath: z.string().optional(),
|
||||||
namePath: z.string().optional(),
|
namePath: z.string().optional(),
|
||||||
@@ -45,8 +44,7 @@ const bodySchema = z
|
|||||||
autoProvision: z.boolean().optional(),
|
autoProvision: z.boolean().optional(),
|
||||||
variant: z.enum(["oidc", "google", "azure"]).optional().default("oidc"),
|
variant: z.enum(["oidc", "google", "azure"]).optional().default("oidc"),
|
||||||
roleMapping: z.string().optional()
|
roleMapping: z.string().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
// registry.registerPath({
|
// registry.registerPath({
|
||||||
// method: "put",
|
// method: "put",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { OpenAPITags, registry } from "@server/openApi";
|
|||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
orgId: z.string().optional(), // Optional; used with org idp in saas
|
orgId: z.string().optional(), // Optional; used with org idp in saas
|
||||||
idpId: z.coerce.number()
|
idpId: z.coerce.number<number>()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { GetOrgIdpResponse } from "@server/routers/orgIdp/types";
|
|||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
orgId: z.string().nonempty(),
|
orgId: z.string().nonempty(),
|
||||||
idpId: z.coerce.number()
|
idpId: z.coerce.number<number>()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
|
|||||||
@@ -24,28 +24,24 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { ListOrgIdpsResponse } from "@server/routers/orgIdp/types";
|
import { ListOrgIdpsResponse } from "@server/routers/orgIdp/types";
|
||||||
|
|
||||||
const querySchema = z
|
const querySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
limit: z
|
limit: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative()),
|
.pipe(z.int().nonnegative()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string().nonempty()
|
orgId: z.string().nonempty()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(orgId: string, limit: number, offset: number) {
|
async function query(orgId: string, limit: number, offset: number) {
|
||||||
const res = await db
|
const res = await db
|
||||||
|
|||||||
@@ -31,12 +31,11 @@ import { TierId } from "@server/lib/billing/tiers";
|
|||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
orgId: z.string().nonempty(),
|
orgId: z.string().nonempty(),
|
||||||
idpId: z.coerce.number()
|
idpId: z.coerce.number<number>()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
clientId: z.string().optional(),
|
clientId: z.string().optional(),
|
||||||
clientSecret: z.string().optional(),
|
clientSecret: z.string().optional(),
|
||||||
@@ -48,8 +47,7 @@ const bodySchema = z
|
|||||||
scopes: z.string().optional(),
|
scopes: z.string().optional(),
|
||||||
autoProvision: z.boolean().optional(),
|
autoProvision: z.boolean().optional(),
|
||||||
roleMapping: z.string().optional()
|
roleMapping: z.string().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type UpdateOrgIdpResponse = {
|
export type UpdateOrgIdpResponse = {
|
||||||
idpId: number;
|
idpId: number;
|
||||||
|
|||||||
16
server/private/routers/re-key/index.ts
Normal file
16
server/private/routers/re-key/index.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from "./reGenerateClientSecret";
|
||||||
|
export * from "./reGenerateSiteSecret";
|
||||||
|
export * from "./reGenerateExitNodeSecret";
|
||||||
139
server/private/routers/re-key/reGenerateClientSecret.ts
Normal file
139
server/private/routers/re-key/reGenerateClientSecret.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { db, olms, } from "@server/db";
|
||||||
|
import { clients } from "@server/db";
|
||||||
|
import response from "@server/lib/response";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import { eq, and } from "drizzle-orm";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
import { hashPassword } from "@server/auth/password";
|
||||||
|
|
||||||
|
const reGenerateSecretParamsSchema = z.strictObject({
|
||||||
|
clientId: z.string().transform(Number).pipe(z.int().positive())
|
||||||
|
});
|
||||||
|
|
||||||
|
const reGenerateSecretBodySchema = z.strictObject({
|
||||||
|
olmId: z.string().min(1).optional(),
|
||||||
|
secret: z.string().min(1).optional(),
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ReGenerateSecretBody = z.infer<typeof reGenerateSecretBodySchema>;
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "post",
|
||||||
|
path: "/re-key/{clientId}/regenerate-client-secret",
|
||||||
|
description: "Regenerate a client's OLM credentials by its client ID.",
|
||||||
|
tags: [OpenAPITags.Client],
|
||||||
|
request: {
|
||||||
|
params: reGenerateSecretParamsSchema,
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: reGenerateSecretBodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export async function reGenerateClientSecret(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
const parsedBody = reGenerateSecretBodySchema.safeParse(req.body);
|
||||||
|
if (!parsedBody.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
fromError(parsedBody.error).toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { olmId, secret } = parsedBody.data;
|
||||||
|
|
||||||
|
const parsedParams = reGenerateSecretParamsSchema.safeParse(req.params);
|
||||||
|
if (!parsedParams.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
fromError(parsedParams.error).toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { clientId } = parsedParams.data;
|
||||||
|
|
||||||
|
let secretHash = undefined;
|
||||||
|
if (secret) {
|
||||||
|
secretHash = await hashPassword(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch the client to make sure it exists and the user has access to it
|
||||||
|
const [client] = await db
|
||||||
|
.select()
|
||||||
|
.from(clients)
|
||||||
|
.where(eq(clients.clientId, clientId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
`Client with ID ${clientId} not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [existingOlm] = await db
|
||||||
|
.select()
|
||||||
|
.from(olms)
|
||||||
|
.where(eq(olms.clientId, clientId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (existingOlm && olmId && secretHash) {
|
||||||
|
await db
|
||||||
|
.update(olms)
|
||||||
|
.set({
|
||||||
|
olmId,
|
||||||
|
secretHash
|
||||||
|
})
|
||||||
|
.where(eq(olms.clientId, clientId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response(res, {
|
||||||
|
data: existingOlm,
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Credentials regenerated successfully",
|
||||||
|
status: HttpCode.OK
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
127
server/private/routers/re-key/reGenerateExitNodeSecret.ts
Normal file
127
server/private/routers/re-key/reGenerateExitNodeSecret.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NextFunction, Request, Response } from "express";
|
||||||
|
import { db, exitNodes, exitNodeOrgs, ExitNode, ExitNodeOrg } from "@server/db";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { remoteExitNodes } from "@server/db";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import response from "@server/lib/response";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
import { hashPassword } from "@server/auth/password";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import { UpdateRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
|
export const paramsSchema = z.object({
|
||||||
|
orgId: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
const bodySchema = z.strictObject({
|
||||||
|
remoteExitNodeId: z.string().length(15),
|
||||||
|
secret: z.string().length(48)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "post",
|
||||||
|
path: "/re-key/{orgId}/regenerate-secret",
|
||||||
|
description: "Regenerate a exit node credentials by its org ID.",
|
||||||
|
tags: [OpenAPITags.Org],
|
||||||
|
request: {
|
||||||
|
params: paramsSchema,
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: bodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function reGenerateExitNodeSecret(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
const parsedParams = paramsSchema.safeParse(req.params);
|
||||||
|
if (!parsedParams.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
fromError(parsedParams.error).toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedBody = bodySchema.safeParse(req.body);
|
||||||
|
if (!parsedBody.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
fromError(parsedBody.error).toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { remoteExitNodeId, secret } = parsedBody.data;
|
||||||
|
|
||||||
|
if (req.user && !req.userOrgRoleId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.FORBIDDEN, "User does not have a role")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [existingRemoteExitNode] = await db
|
||||||
|
.select()
|
||||||
|
.from(remoteExitNodes)
|
||||||
|
.where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId));
|
||||||
|
|
||||||
|
if (!existingRemoteExitNode) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.NOT_FOUND, "Remote Exit Node does not exist")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretHash = await hashPassword(secret);
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(remoteExitNodes)
|
||||||
|
.set({ secretHash })
|
||||||
|
.where(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId));
|
||||||
|
|
||||||
|
return response<UpdateRemoteExitNodeResponse>(res, {
|
||||||
|
data: {
|
||||||
|
remoteExitNodeId,
|
||||||
|
secret,
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Remote Exit Node secret updated successfully",
|
||||||
|
status: HttpCode.OK,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Failed to update remoteExitNode", e);
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Failed to update remoteExitNode"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
164
server/private/routers/re-key/reGenerateSiteSecret.ts
Normal file
164
server/private/routers/re-key/reGenerateSiteSecret.ts
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of a proprietary work.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2025 Fossorial, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is licensed under the Fossorial Commercial License.
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||||
|
*
|
||||||
|
* This file is not licensed under the AGPLv3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { db, newts, sites } from "@server/db";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import response from "@server/lib/response";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import { fromError } from "zod-validation-error";
|
||||||
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
import { hashPassword } from "@server/auth/password";
|
||||||
|
import { addPeer } from "@server/routers/gerbil/peers";
|
||||||
|
|
||||||
|
|
||||||
|
const updateSiteParamsSchema = z.strictObject({
|
||||||
|
siteId: z.string().transform(Number).pipe(z.int().positive())
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateSiteBodySchema = z.strictObject({
|
||||||
|
type: z.enum(["newt", "wireguard"]),
|
||||||
|
newtId: z.string().min(1).max(255).optional(),
|
||||||
|
newtSecret: z.string().min(1).max(255).optional(),
|
||||||
|
exitNodeId: z.int().positive().optional(),
|
||||||
|
pubKey: z.string().optional(),
|
||||||
|
subnet: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: "post",
|
||||||
|
path: "/re-key/{siteId}/regenerate-site-secret",
|
||||||
|
description: "Regenerate a site's Newt or WireGuard credentials by its site ID.",
|
||||||
|
tags: [OpenAPITags.Site],
|
||||||
|
request: {
|
||||||
|
params: updateSiteParamsSchema,
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: updateSiteBodySchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function reGenerateSiteSecret(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
const parsedParams = updateSiteParamsSchema.safeParse(req.params);
|
||||||
|
if (!parsedParams.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, fromError(parsedParams.error).toString())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedBody = updateSiteBodySchema.safeParse(req.body);
|
||||||
|
if (!parsedBody.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, fromError(parsedBody.error).toString())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { siteId } = parsedParams.data;
|
||||||
|
const { type, exitNodeId, pubKey, subnet, newtId, newtSecret } = parsedBody.data;
|
||||||
|
|
||||||
|
let updatedSite = undefined;
|
||||||
|
|
||||||
|
if (type === "newt") {
|
||||||
|
if (!newtSecret) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, "newtSecret is required for newt sites")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretHash = await hashPassword(newtSecret);
|
||||||
|
|
||||||
|
updatedSite = await db
|
||||||
|
.update(newts)
|
||||||
|
.set({
|
||||||
|
newtId,
|
||||||
|
secretHash,
|
||||||
|
})
|
||||||
|
.where(eq(newts.siteId, siteId))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
logger.info(`Regenerated Newt credentials for site ${siteId}`);
|
||||||
|
|
||||||
|
} else if (type === "wireguard") {
|
||||||
|
if (!pubKey) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, "Public key is required for wireguard sites")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exitNodeId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"Exit node ID is required for wireguard sites"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
updatedSite = await db.transaction(async (tx) => {
|
||||||
|
await addPeer(exitNodeId, {
|
||||||
|
publicKey: pubKey,
|
||||||
|
allowedIps: subnet ? [subnet] : [],
|
||||||
|
});
|
||||||
|
const result = await tx
|
||||||
|
.update(sites)
|
||||||
|
.set({ pubKey })
|
||||||
|
.where(eq(sites.siteId, siteId))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Regenerated WireGuard credentials for site ${siteId}`);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(
|
||||||
|
`Transaction failed while regenerating WireGuard secret for site ${siteId}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Failed to regenerate WireGuard credentials. Rolled back transaction."
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response(res, {
|
||||||
|
data: updatedSite,
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Credentials regenerated successfully",
|
||||||
|
status: HttpCode.OK,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Unexpected error in reGenerateSiteSecret", error);
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An unexpected error occurred")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,12 +35,10 @@ export const paramsSchema = z.object({
|
|||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
remoteExitNodeId: z.string().length(15),
|
remoteExitNodeId: z.string().length(15),
|
||||||
secret: z.string().length(48)
|
secret: z.string().length(48)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type CreateRemoteExitNodeBody = z.infer<typeof bodySchema>;
|
export type CreateRemoteExitNodeBody = z.infer<typeof bodySchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,10 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { usageService } from "@server/lib/billing/usageService";
|
import { usageService } from "@server/lib/billing/usageService";
|
||||||
import { FeatureId } from "@server/lib/billing";
|
import { FeatureId } from "@server/lib/billing";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string().min(1),
|
orgId: z.string().min(1),
|
||||||
remoteExitNodeId: z.string().min(1)
|
remoteExitNodeId: z.string().min(1)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export async function deleteRemoteExitNode(
|
export async function deleteRemoteExitNode(
|
||||||
req: Request,
|
req: Request,
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { GetRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types";
|
import { GetRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types";
|
||||||
|
|
||||||
const getRemoteExitNodeSchema = z
|
const getRemoteExitNodeSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string().min(1),
|
orgId: z.string().min(1),
|
||||||
remoteExitNodeId: z.string().min(1)
|
remoteExitNodeId: z.string().min(1)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(remoteExitNodeId: string) {
|
async function query(remoteExitNodeId: string) {
|
||||||
const [remoteExitNode] = await db
|
const [remoteExitNode] = await db
|
||||||
|
|||||||
@@ -23,11 +23,9 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types";
|
import { ListRemoteExitNodesResponse } from "@server/routers/remoteExitNode/types";
|
||||||
|
|
||||||
const listRemoteExitNodesParamsSchema = z
|
const listRemoteExitNodesParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const listRemoteExitNodesSchema = z.object({
|
const listRemoteExitNodesSchema = z.object({
|
||||||
limit: z
|
limit: z
|
||||||
@@ -35,13 +33,13 @@ const listRemoteExitNodesSchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
export function queryRemoteExitNodes(orgId: string) {
|
export function queryRemoteExitNodes(orgId: string) {
|
||||||
|
|||||||
@@ -21,11 +21,9 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { PickRemoteExitNodeDefaultsResponse } from "@server/routers/remoteExitNode/types";
|
import { PickRemoteExitNodeDefaultsResponse } from "@server/routers/remoteExitNode/types";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export async function pickRemoteExitNodeDefaults(
|
export async function pickRemoteExitNodeDefaults(
|
||||||
req: Request,
|
req: Request,
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ import { and, eq } from "drizzle-orm";
|
|||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const deleteAccessTokenParamsSchema = z
|
const deleteAccessTokenParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
accessTokenId: z.string()
|
accessTokenId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "delete",
|
method: "delete",
|
||||||
|
|||||||
@@ -24,22 +24,18 @@ import { encodeHexLowerCase } from "@oslojs/encoding";
|
|||||||
import { sha256 } from "@oslojs/crypto/sha2";
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
export const generateAccessTokenBodySchema = z
|
export const generateAccessTokenBodySchema = z.strictObject({
|
||||||
.object({
|
validForSeconds: z.int().positive().optional(), // seconds
|
||||||
validForSeconds: z.number().int().positive().optional(), // seconds
|
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
description: z.string().optional()
|
description: z.string().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export const generateAccssTokenParamsSchema = z
|
export const generateAccssTokenParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
resourceId: z
|
resourceId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive())
|
.pipe(z.int().positive())
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type GenerateAccessTokenResponse = Omit<
|
export type GenerateAccessTokenResponse = Omit<
|
||||||
ResourceAccessToken,
|
ResourceAccessToken,
|
||||||
|
|||||||
@@ -17,18 +17,16 @@ import stoi from "@server/lib/stoi";
|
|||||||
import { fromZodError } from "zod-validation-error";
|
import { fromZodError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const listAccessTokensParamsSchema = z
|
const listAccessTokensParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
resourceId: z
|
resourceId: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.transform(stoi)
|
.transform(stoi)
|
||||||
.pipe(z.number().int().positive().optional()),
|
.pipe(z.int().positive().optional()),
|
||||||
orgId: z.string().optional()
|
orgId: z.string().optional()
|
||||||
})
|
})
|
||||||
.strict()
|
|
||||||
.refine((data) => !!data.resourceId !== !!data.orgId, {
|
.refine((data) => !!data.resourceId !== !!data.orgId, {
|
||||||
message: "Either resourceId or orgId must be provided, but not both"
|
error: "Either resourceId or orgId must be provided, but not both"
|
||||||
});
|
});
|
||||||
|
|
||||||
const listAccessTokensSchema = z.object({
|
const listAccessTokensSchema = z.object({
|
||||||
@@ -37,14 +35,14 @@ const listAccessTokensSchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative()),
|
.pipe(z.int().nonnegative()),
|
||||||
|
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
function queryAccessTokens(
|
function queryAccessTokens(
|
||||||
|
|||||||
@@ -14,11 +14,9 @@ import {
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { hashPassword } from "@server/auth/password";
|
import { hashPassword } from "@server/auth/password";
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
name: z.string().min(1).max(255)
|
name: z.string().min(1).max(255)
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type CreateRootApiKeyBody = z.infer<typeof bodySchema>;
|
export type CreateRootApiKeyBody = z.infer<typeof bodySchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ const querySchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
function queryActions(apiKeyId: string) {
|
function queryActions(apiKeyId: string) {
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ const querySchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
const paramsSchema = z.object({
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ const querySchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
function queryApiKeys() {
|
function queryApiKeys() {
|
||||||
|
|||||||
@@ -10,13 +10,10 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { eq, and, inArray } from "drizzle-orm";
|
import { eq, and, inArray } from "drizzle-orm";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
actionIds: z.tuple([z.string()], z.string())
|
||||||
actionIds: z
|
|
||||||
.array(z.string().nonempty())
|
|
||||||
.transform((v) => Array.from(new Set(v)))
|
.transform((v) => Array.from(new Set(v)))
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
const paramsSchema = z.object({
|
||||||
apiKeyId: z.string().nonempty()
|
apiKeyId: z.string().nonempty()
|
||||||
|
|||||||
@@ -9,13 +9,10 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { eq, and, inArray } from "drizzle-orm";
|
import { eq, and, inArray } from "drizzle-orm";
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
orgIds: z.tuple([z.string()], z.string())
|
||||||
orgIds: z
|
|
||||||
.array(z.string().nonempty())
|
|
||||||
.transform((v) => Array.from(new Set(v)))
|
.transform((v) => Array.from(new Set(v)))
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
const paramsSchema = z.object({
|
||||||
apiKeyId: z.string().nonempty()
|
apiKeyId: z.string().nonempty()
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ export const queryAccessAuditLogsQuery = z.object({
|
|||||||
timeStart: z
|
timeStart: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => !isNaN(Date.parse(val)), {
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
message: "timeStart must be a valid ISO date string"
|
error: "timeStart must be a valid ISO date string"
|
||||||
})
|
})
|
||||||
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000)),
|
||||||
timeEnd: z
|
timeEnd: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => !isNaN(Date.parse(val)), {
|
.refine((val) => !isNaN(Date.parse(val)), {
|
||||||
message: "timeEnd must be a valid ISO date string"
|
error: "timeEnd must be a valid ISO date string"
|
||||||
})
|
})
|
||||||
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
.transform((val) => Math.floor(new Date(val).getTime() / 1000))
|
||||||
.optional()
|
.optional()
|
||||||
.default(new Date().toISOString()),
|
.prefault(new Date().toISOString()),
|
||||||
action: z
|
action: z
|
||||||
.union([z.boolean(), z.string()])
|
.union([z.boolean(), z.string()])
|
||||||
.transform((val) => (typeof val === "string" ? val === "true" : val))
|
.transform((val) => (typeof val === "string" ? val === "true" : val))
|
||||||
@@ -37,13 +37,13 @@ export const queryAccessAuditLogsQuery = z.object({
|
|||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive())
|
.pipe(z.int().positive())
|
||||||
.optional(),
|
.optional(),
|
||||||
resourceId: z
|
resourceId: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive())
|
.pipe(z.int().positive())
|
||||||
.optional(),
|
.optional(),
|
||||||
actor: z.string().optional(),
|
actor: z.string().optional(),
|
||||||
location: z.string().optional(),
|
location: z.string().optional(),
|
||||||
@@ -54,13 +54,13 @@ export const queryAccessAuditLogsQuery = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
});
|
});
|
||||||
|
|
||||||
export const queryRequestAuditLogsParams = z.object({
|
export const queryRequestAuditLogsParams = z.object({
|
||||||
|
|||||||
@@ -22,13 +22,11 @@ import { sendEmail } from "@server/emails";
|
|||||||
import ConfirmPasswordReset from "@server/emails/templates/NotifyResetPassword";
|
import ConfirmPasswordReset from "@server/emails/templates/NotifyResetPassword";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
export const changePasswordBody = z
|
export const changePasswordBody = z.strictObject({
|
||||||
.object({
|
|
||||||
oldPassword: z.string(),
|
oldPassword: z.string(),
|
||||||
newPassword: passwordSchema,
|
newPassword: passwordSchema,
|
||||||
code: z.string().optional()
|
code: z.string().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type ChangePasswordBody = z.infer<typeof changePasswordBody>;
|
export type ChangePasswordBody = z.infer<typeof changePasswordBody>;
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import { response } from "@server/lib/response";
|
|||||||
import { validateResourceSessionToken } from "@server/auth/sessions/resource";
|
import { validateResourceSessionToken } from "@server/auth/sessions/resource";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
||||||
export const params = z.object({
|
export const params = z.strictObject({
|
||||||
token: z.string(),
|
token: z.string(),
|
||||||
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
|
resourceId: z.string().transform(Number).pipe(z.int().positive()),
|
||||||
}).strict();
|
});
|
||||||
|
|
||||||
export type CheckResourceSessionParams = z.infer<typeof params>;
|
export type CheckResourceSessionParams = z.infer<typeof params>;
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,10 @@ import config from "@server/lib/config";
|
|||||||
import { unauthorized } from "@server/auth/unauthorizedResponse";
|
import { unauthorized } from "@server/auth/unauthorizedResponse";
|
||||||
import { UserType } from "@server/types/UserTypes";
|
import { UserType } from "@server/types/UserTypes";
|
||||||
|
|
||||||
export const disable2faBody = z
|
export const disable2faBody = z.strictObject({
|
||||||
.object({
|
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
code: z.string().optional()
|
code: z.string().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type Disable2faBody = z.infer<typeof disable2faBody>;
|
export type Disable2faBody = z.infer<typeof disable2faBody>;
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,12 @@ import { verifySession } from "@server/auth/sessions/verifySession";
|
|||||||
import { UserType } from "@server/types/UserTypes";
|
import { UserType } from "@server/types/UserTypes";
|
||||||
import { logAccessAudit } from "#dynamic/lib/logAccessAudit";
|
import { logAccessAudit } from "#dynamic/lib/logAccessAudit";
|
||||||
|
|
||||||
export const loginBodySchema = z
|
export const loginBodySchema = z.strictObject({
|
||||||
.object({
|
email: z.email().toLowerCase(),
|
||||||
email: z.string().toLowerCase().email(),
|
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
code: z.string().optional(),
|
code: z.string().optional(),
|
||||||
resourceGuid: z.string().optional()
|
resourceGuid: z.string().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type LoginBody = z.infer<typeof loginBodySchema>;
|
export type LoginBody = z.infer<typeof loginBodySchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,9 @@ import ResetPasswordCode from "@server/emails/templates/ResetPasswordCode";
|
|||||||
import { hashPassword } from "@server/auth/password";
|
import { hashPassword } from "@server/auth/password";
|
||||||
import { UserType } from "@server/types/UserTypes";
|
import { UserType } from "@server/types/UserTypes";
|
||||||
|
|
||||||
export const requestPasswordResetBody = z
|
export const requestPasswordResetBody = z.strictObject({
|
||||||
.object({
|
email: z.email().toLowerCase()
|
||||||
email: z.string().toLowerCase().email()
|
});
|
||||||
})
|
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type RequestPasswordResetBody = z.infer<typeof requestPasswordResetBody>;
|
export type RequestPasswordResetBody = z.infer<typeof requestPasswordResetBody>;
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,10 @@ import { UserType } from "@server/types/UserTypes";
|
|||||||
import { verifySession } from "@server/auth/sessions/verifySession";
|
import { verifySession } from "@server/auth/sessions/verifySession";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
export const requestTotpSecretBody = z
|
export const requestTotpSecretBody = z.strictObject({
|
||||||
.object({
|
|
||||||
password: z.string(),
|
password: z.string(),
|
||||||
email: z.string().email().optional()
|
email: z.email().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type RequestTotpSecretBody = z.infer<typeof requestTotpSecretBody>;
|
export type RequestTotpSecretBody = z.infer<typeof requestTotpSecretBody>;
|
||||||
|
|
||||||
|
|||||||
@@ -17,14 +17,12 @@ import ConfirmPasswordReset from "@server/emails/templates/NotifyResetPassword";
|
|||||||
import { sendEmail } from "@server/emails";
|
import { sendEmail } from "@server/emails";
|
||||||
import { passwordSchema } from "@server/auth/passwordSchema";
|
import { passwordSchema } from "@server/auth/passwordSchema";
|
||||||
|
|
||||||
export const resetPasswordBody = z
|
export const resetPasswordBody = z.strictObject({
|
||||||
.object({
|
email: z.email().toLowerCase(),
|
||||||
email: z.string().toLowerCase().email(),
|
|
||||||
token: z.string(), // reset secret code
|
token: z.string(), // reset secret code
|
||||||
newPassword: passwordSchema,
|
newPassword: passwordSchema,
|
||||||
code: z.string().optional() // 2fa code
|
code: z.string().optional() // 2fa code
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type ResetPasswordBody = z.infer<typeof resetPasswordBody>;
|
export type ResetPasswordBody = z.infer<typeof resetPasswordBody>;
|
||||||
|
|
||||||
|
|||||||
@@ -99,28 +99,28 @@ async function clearChallenge(sessionId: string) {
|
|||||||
await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId));
|
await db.delete(webauthnChallenge).where(eq(webauthnChallenge.sessionId, sessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const registerSecurityKeyBody = z.object({
|
export const registerSecurityKeyBody = z.strictObject({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
password: z.string().min(1),
|
password: z.string().min(1),
|
||||||
code: z.string().optional()
|
code: z.string().optional()
|
||||||
}).strict();
|
});
|
||||||
|
|
||||||
export const verifyRegistrationBody = z.object({
|
export const verifyRegistrationBody = z.strictObject({
|
||||||
credential: z.any()
|
credential: z.any()
|
||||||
}).strict();
|
});
|
||||||
|
|
||||||
export const startAuthenticationBody = z.object({
|
export const startAuthenticationBody = z.strictObject({
|
||||||
email: z.string().email().optional()
|
email: z.email().optional()
|
||||||
}).strict();
|
});
|
||||||
|
|
||||||
export const verifyAuthenticationBody = z.object({
|
export const verifyAuthenticationBody = z.strictObject({
|
||||||
credential: z.any()
|
credential: z.any()
|
||||||
}).strict();
|
});
|
||||||
|
|
||||||
export const deleteSecurityKeyBody = z.object({
|
export const deleteSecurityKeyBody = z.strictObject({
|
||||||
password: z.string().min(1),
|
password: z.string().min(1),
|
||||||
code: z.string().optional()
|
code: z.string().optional()
|
||||||
}).strict();
|
});
|
||||||
|
|
||||||
export async function startRegistration(
|
export async function startRegistration(
|
||||||
req: Request,
|
req: Request,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import { eq, and } from "drizzle-orm";
|
|||||||
import { UserType } from "@server/types/UserTypes";
|
import { UserType } from "@server/types/UserTypes";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
|
||||||
const bodySchema = z.object({
|
export const bodySchema = z.object({
|
||||||
email: z.string().toLowerCase().email(),
|
email: z.email().toLowerCase(),
|
||||||
password: passwordSchema,
|
password: passwordSchema,
|
||||||
setupToken: z.string().min(1, "Setup token is required")
|
setupToken: z.string().min(1, "Setup token is required")
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { build } from "@server/build";
|
|||||||
import resend, { AudienceIds, moveEmailToAudience } from "#dynamic/lib/resend";
|
import resend, { AudienceIds, moveEmailToAudience } from "#dynamic/lib/resend";
|
||||||
|
|
||||||
export const signupBodySchema = z.object({
|
export const signupBodySchema = z.object({
|
||||||
email: z.string().toLowerCase().email(),
|
email: z.email().toLowerCase(),
|
||||||
password: passwordSchema,
|
password: passwordSchema,
|
||||||
inviteToken: z.string().optional(),
|
inviteToken: z.string().optional(),
|
||||||
inviteId: z.string().optional(),
|
inviteId: z.string().optional(),
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ import createHttpError from "http-errors";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
|
|
||||||
const validateSetupTokenSchema = z
|
const validateSetupTokenSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
token: z.string().min(1, "Token is required")
|
token: z.string().min(1, "Token is required")
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type ValidateSetupTokenResponse = {
|
export type ValidateSetupTokenResponse = {
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
|
|||||||
@@ -13,11 +13,9 @@ import logger from "@server/logger";
|
|||||||
import { freeLimitSet, limitsService } from "@server/lib/billing";
|
import { freeLimitSet, limitsService } from "@server/lib/billing";
|
||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
|
|
||||||
export const verifyEmailBody = z
|
export const verifyEmailBody = z.strictObject({
|
||||||
.object({
|
|
||||||
code: z.string()
|
code: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type VerifyEmailBody = z.infer<typeof verifyEmailBody>;
|
export type VerifyEmailBody = z.infer<typeof verifyEmailBody>;
|
||||||
|
|
||||||
|
|||||||
@@ -18,13 +18,11 @@ import { generateBackupCodes } from "@server/lib/totp";
|
|||||||
import { verifySession } from "@server/auth/sessions/verifySession";
|
import { verifySession } from "@server/auth/sessions/verifySession";
|
||||||
import { unauthorized } from "@server/auth/unauthorizedResponse";
|
import { unauthorized } from "@server/auth/unauthorizedResponse";
|
||||||
|
|
||||||
export const verifyTotpBody = z
|
export const verifyTotpBody = z.strictObject({
|
||||||
.object({
|
email: z.email().optional(),
|
||||||
email: z.string().email().optional(),
|
|
||||||
password: z.string().optional(),
|
password: z.string().optional(),
|
||||||
code: z.string()
|
code: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type VerifyTotpBody = z.infer<typeof verifyTotpBody>;
|
export type VerifyTotpBody = z.infer<typeof verifyTotpBody>;
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ import { logRequestAudit } from "./logRequestAudit";
|
|||||||
import cache from "@server/lib/cache";
|
import cache from "@server/lib/cache";
|
||||||
|
|
||||||
const verifyResourceSessionSchema = z.object({
|
const verifyResourceSessionSchema = z.object({
|
||||||
sessions: z.record(z.string()).optional(),
|
sessions: z.record(z.string(), z.string()).optional(),
|
||||||
headers: z.record(z.string()).optional(),
|
headers: z.record(z.string(), z.string()).optional(),
|
||||||
query: z.record(z.string()).optional(),
|
query: z.record(z.string(), z.string()).optional(),
|
||||||
originalRequestURL: z.string().url(),
|
originalRequestURL: z.url(),
|
||||||
scheme: z.string(),
|
scheme: z.string(),
|
||||||
host: z.string(),
|
host: z.string(),
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
@@ -60,6 +60,7 @@ type BasicUserData = {
|
|||||||
username: string;
|
username: string;
|
||||||
email: string | null;
|
email: string | null;
|
||||||
name: string | null;
|
name: string | null;
|
||||||
|
role: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VerifyUserResponse = {
|
export type VerifyUserResponse = {
|
||||||
@@ -883,7 +884,8 @@ async function isUserAllowedToAccessResource(
|
|||||||
return {
|
return {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
name: user.name
|
name: user.name,
|
||||||
|
role: user.role
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,7 +898,8 @@ async function isUserAllowedToAccessResource(
|
|||||||
return {
|
return {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
name: user.name
|
name: user.name,
|
||||||
|
role: user.role
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,13 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { applyBlueprint } from "@server/lib/blueprints/applyBlueprint";
|
import { applyBlueprint } from "@server/lib/blueprints/applyBlueprint";
|
||||||
|
|
||||||
const applyBlueprintSchema = z
|
const applyBlueprintSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
blueprint: z.string()
|
blueprint: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const applyBlueprintParamsSchema = z
|
const applyBlueprintParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "put",
|
method: "put",
|
||||||
|
|||||||
@@ -12,15 +12,13 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { BlueprintData } from "./types";
|
import { BlueprintData } from "./types";
|
||||||
|
|
||||||
const getBlueprintSchema = z
|
const getBlueprintSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
blueprintId: z
|
blueprintId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(stoi)
|
.transform(stoi)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(blueprintId: number, orgId: string) {
|
async function query(blueprintId: number, orgId: string) {
|
||||||
// Get the client
|
// Get the client
|
||||||
|
|||||||
@@ -10,28 +10,24 @@ import { fromZodError } from "zod-validation-error";
|
|||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { BlueprintData } from "./types";
|
import { BlueprintData } from "./types";
|
||||||
|
|
||||||
const listBluePrintsParamsSchema = z
|
const listBluePrintsParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const listBluePrintsSchema = z
|
const listBluePrintsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
limit: z
|
limit: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative()),
|
.pipe(z.int().nonnegative()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function queryBlueprints(orgId: string, limit: number, offset: number) {
|
async function queryBlueprints(orgId: string, limit: number, offset: number) {
|
||||||
const res = await db
|
const res = await db
|
||||||
|
|||||||
@@ -25,23 +25,19 @@ import { listExitNodes } from "#dynamic/lib/exitNodes";
|
|||||||
import { generateId } from "@server/auth/sessions/app";
|
import { generateId } from "@server/auth/sessions/app";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const paramsSchema = z
|
const createClientParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const bodySchema = z
|
const createClientSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
name: z.string().min(1).max(255),
|
name: z.string().min(1).max(255),
|
||||||
olmId: z.string(),
|
olmId: z.string(),
|
||||||
secret: z.string(),
|
secret: z.string(),
|
||||||
subnet: z.string(),
|
subnet: z.string(),
|
||||||
type: z.enum(["olm"])
|
type: z.enum(["olm"])
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type CreateClientBody = z.infer<typeof bodySchema>;
|
export type CreateClientBody = z.infer<typeof createClientSchema>;
|
||||||
|
|
||||||
export type CreateClientResponse = Client;
|
export type CreateClientResponse = Client;
|
||||||
|
|
||||||
@@ -51,11 +47,11 @@ registry.registerPath({
|
|||||||
description: "Create a new client for an organization.",
|
description: "Create a new client for an organization.",
|
||||||
tags: [OpenAPITags.Client, OpenAPITags.Org],
|
tags: [OpenAPITags.Client, OpenAPITags.Org],
|
||||||
request: {
|
request: {
|
||||||
params: paramsSchema,
|
params: createClientParamsSchema,
|
||||||
body: {
|
body: {
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: bodySchema
|
schema: createClientSchema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +65,7 @@ export async function createClient(
|
|||||||
next: NextFunction
|
next: NextFunction
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const parsedBody = bodySchema.safeParse(req.body);
|
const parsedBody = createClientSchema.safeParse(req.body);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
@@ -81,7 +77,7 @@ export async function createClient(
|
|||||||
|
|
||||||
const { name, type, olmId, secret, subnet } = parsedBody.data;
|
const { name, type, olmId, secret, subnet } = parsedBody.data;
|
||||||
|
|
||||||
const parsedParams = paramsSchema.safeParse(req.params);
|
const parsedParams = createClientParamsSchema.safeParse(req.params);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const deleteClientSchema = z
|
const deleteClientSchema = z.strictObject({
|
||||||
.object({
|
clientId: z.string().transform(Number).pipe(z.int().positive())
|
||||||
clientId: z.string().transform(Number).pipe(z.number().int().positive())
|
});
|
||||||
})
|
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "delete",
|
method: "delete",
|
||||||
|
|||||||
@@ -11,11 +11,9 @@ import stoi from "@server/lib/stoi";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const getClientSchema = z
|
const getClientSchema = z.strictObject({
|
||||||
.object({
|
clientId: z.string().transform(stoi).pipe(z.int().positive())
|
||||||
clientId: z.string().transform(stoi).pipe(z.number().int().positive())
|
});
|
||||||
})
|
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(clientId: number) {
|
async function query(clientId: number) {
|
||||||
// Get the client
|
// Get the client
|
||||||
|
|||||||
@@ -78,11 +78,9 @@ async function getLatestOlmVersion(): Promise<string | null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const listClientsParamsSchema = z
|
const listClientsParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const listClientsSchema = z.object({
|
const listClientsSchema = z.object({
|
||||||
limit: z
|
limit: z
|
||||||
@@ -90,13 +88,13 @@ const listClientsSchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().positive()),
|
.pipe(z.int().positive()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative()),
|
.pipe(z.int().nonnegative()),
|
||||||
filter: z
|
filter: z
|
||||||
.enum(["user", "machine"])
|
.enum(["user", "machine"])
|
||||||
.optional()
|
.optional()
|
||||||
|
|||||||
@@ -15,11 +15,9 @@ export type PickClientDefaultsResponse = {
|
|||||||
subnet: string;
|
subnet: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const pickClientDefaultsSchema = z
|
const pickClientDefaultsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "get",
|
method: "get",
|
||||||
|
|||||||
@@ -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 { Client, db, exitNodes, sites } from "@server/db";
|
import { Client, db, exitNodes, olms, sites } from "@server/db";
|
||||||
import { clients, clientSites } from "@server/db";
|
import { clients, clientSites } 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";
|
||||||
@@ -10,17 +10,13 @@ import { eq, and } from "drizzle-orm";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const updateClientParamsSchema = z
|
const updateClientParamsSchema = z.strictObject({
|
||||||
.object({
|
clientId: z.string().transform(Number).pipe(z.int().positive())
|
||||||
clientId: z.string().transform(Number).pipe(z.number().int().positive())
|
});
|
||||||
})
|
|
||||||
.strict();
|
|
||||||
|
|
||||||
const updateClientSchema = z
|
const updateClientSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
name: z.string().min(1).max(255).optional()
|
name: z.string().min(1).max(255).optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type UpdateClientBody = z.infer<typeof updateClientSchema>;
|
export type UpdateClientBody = z.infer<typeof updateClientSchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -15,20 +15,16 @@ import { isSecondLevelDomain, isValidDomain } from "@server/lib/validators";
|
|||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
type: z.enum(["ns", "cname", "wildcard"]),
|
type: z.enum(["ns", "cname", "wildcard"]),
|
||||||
baseDomain: subdomainSchema,
|
baseDomain: subdomainSchema,
|
||||||
certResolver: z.string().optional().nullable(),
|
certResolver: z.string().optional().nullable(),
|
||||||
preferWildcardCert: z.boolean().optional().nullable() // optional, only for wildcard
|
preferWildcardCert: z.boolean().optional().nullable() // optional, only for wildcard
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
|
|
||||||
export type CreateDomainResponse = {
|
export type CreateDomainResponse = {
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ import { and, eq } from "drizzle-orm";
|
|||||||
import { usageService } from "@server/lib/billing/usageService";
|
import { usageService } from "@server/lib/billing/usageService";
|
||||||
import { FeatureId } from "@server/lib/billing";
|
import { FeatureId } from "@server/lib/billing";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
domainId: z.string(),
|
domainId: z.string(),
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type DeleteAccountDomainResponse = {
|
export type DeleteAccountDomainResponse = {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { getServerIp } from "@server/lib/serverIpService"; // your in-memory IP module
|
import { getServerIp } from "@server/lib/serverIpService"; // your in-memory IP module
|
||||||
|
|
||||||
const getDNSRecordsSchema = z
|
const getDNSRecordsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
domainId: z.string(),
|
domainId: z.string(),
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(domainId: string) {
|
async function query(domainId: string) {
|
||||||
const records = await db
|
const records = await db
|
||||||
|
|||||||
@@ -10,14 +10,12 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { domain } from "zod/v4/core/regexes";
|
import { domain } from "zod/v4/core/regexes";
|
||||||
|
|
||||||
const getDomainSchema = z
|
const getDomainSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
domainId: z
|
domainId: z
|
||||||
.string()
|
.string()
|
||||||
.optional(),
|
.optional(),
|
||||||
orgId: z.string().optional()
|
orgId: z.string().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function query(domainId?: string, orgId?: string) {
|
async function query(domainId?: string, orgId?: string) {
|
||||||
if (domainId) {
|
if (domainId) {
|
||||||
|
|||||||
@@ -10,28 +10,24 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const listDomainsParamsSchema = z
|
const listDomainsParamsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const listDomainsSchema = z
|
const listDomainsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
limit: z
|
limit: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("1000")
|
.default("1000")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative()),
|
.pipe(z.int().nonnegative()),
|
||||||
offset: z
|
offset: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.int().nonnegative())
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
async function queryDomains(orgId: string, limit: number, offset: number) {
|
async function queryDomains(orgId: string, limit: number, offset: number) {
|
||||||
const res = await db
|
const res = await db
|
||||||
|
|||||||
@@ -8,12 +8,10 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
domainId: z.string(),
|
domainId: z.string(),
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type RestartOrgDomainResponse = {
|
export type RestartOrgDomainResponse = {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|||||||
@@ -9,19 +9,15 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
|
||||||
orgId: z.string(),
|
orgId: z.string(),
|
||||||
domainId: z.string()
|
domainId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
certResolver: z.string().optional().nullable(),
|
certResolver: z.string().optional().nullable(),
|
||||||
preferWildcardCert: z.boolean().optional().nullable()
|
preferWildcardCert: z.boolean().optional().nullable()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type UpdateDomainResponse = {
|
export type UpdateDomainResponse = {
|
||||||
domainId: string;
|
domainId: string;
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ authenticated.post(
|
|||||||
client.updateClient,
|
client.updateClient,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// authenticated.get(
|
// authenticated.get(
|
||||||
// "/site/:siteId/roles",
|
// "/site/:siteId/roles",
|
||||||
// verifySiteAccess,
|
// verifySiteAccess,
|
||||||
@@ -194,6 +195,7 @@ authenticated.post(
|
|||||||
logActionAudit(ActionsEnum.updateSite),
|
logActionAudit(ActionsEnum.updateSite),
|
||||||
site.updateSite,
|
site.updateSite,
|
||||||
);
|
);
|
||||||
|
|
||||||
authenticated.delete(
|
authenticated.delete(
|
||||||
"/site/:siteId",
|
"/site/:siteId",
|
||||||
verifySiteAccess,
|
verifySiteAccess,
|
||||||
|
|||||||
@@ -11,19 +11,15 @@ import config from "@server/lib/config";
|
|||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { idp, idpOrg } from "@server/db";
|
import { idp, idpOrg } from "@server/db";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
idpId: z.coerce.number<number>(),
|
||||||
idpId: z.coerce.number(),
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
roleMapping: z.string().optional(),
|
roleMapping: z.string().optional(),
|
||||||
orgMapping: z.string().optional()
|
orgMapping: z.string().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type CreateIdpOrgPolicyResponse = {};
|
export type CreateIdpOrgPolicyResponse = {};
|
||||||
|
|
||||||
|
|||||||
@@ -12,22 +12,20 @@ import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
|
|||||||
import { encrypt } from "@server/lib/crypto";
|
import { encrypt } from "@server/lib/crypto";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
const paramsSchema = z.object({}).strict();
|
const paramsSchema = z.strictObject({});
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
name: z.string().nonempty(),
|
name: z.string().nonempty(),
|
||||||
clientId: z.string().nonempty(),
|
clientId: z.string().nonempty(),
|
||||||
clientSecret: z.string().nonempty(),
|
clientSecret: z.string().nonempty(),
|
||||||
authUrl: z.string().url(),
|
authUrl: z.url(),
|
||||||
tokenUrl: z.string().url(),
|
tokenUrl: z.url(),
|
||||||
identifierPath: z.string().nonempty(),
|
identifierPath: z.string().nonempty(),
|
||||||
emailPath: z.string().optional(),
|
emailPath: z.string().optional(),
|
||||||
namePath: z.string().optional(),
|
namePath: z.string().optional(),
|
||||||
scopes: z.string().nonempty(),
|
scopes: z.string().nonempty(),
|
||||||
autoProvision: z.boolean().optional()
|
autoProvision: z.boolean().optional()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
export type CreateIdpResponse = {
|
export type CreateIdpResponse = {
|
||||||
idpId: number;
|
idpId: number;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { OpenAPITags, registry } from "@server/openApi";
|
|||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
orgId: z.string().optional(), // Optional; used with org idp in saas
|
orgId: z.string().optional(), // Optional; used with org idp in saas
|
||||||
idpId: z.coerce.number()
|
idpId: z.coerce.number<number>()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ import { idp, idpOrg } from "@server/db";
|
|||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z.strictObject({
|
||||||
.object({
|
idpId: z.coerce.number<number>(),
|
||||||
idpId: z.coerce.number(),
|
|
||||||
orgId: z.string()
|
orgId: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "delete",
|
method: "delete",
|
||||||
|
|||||||
@@ -19,15 +19,13 @@ import { TierId } from "@server/lib/billing/tiers";
|
|||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
idpId: z.coerce.number()
|
idpId: z.coerce.number<number>()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
const bodySchema = z
|
const bodySchema = z.strictObject({
|
||||||
.object({
|
|
||||||
redirectUrl: z.string()
|
redirectUrl: z.string()
|
||||||
})
|
});
|
||||||
.strict();
|
|
||||||
|
|
||||||
const querySchema = z.object({
|
const querySchema = z.object({
|
||||||
orgId: z.string().optional() // check what actuall calls it
|
orgId: z.string().optional() // check what actuall calls it
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { decrypt } from "@server/lib/crypto";
|
|||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
idpId: z.coerce.number()
|
idpId: z.coerce.number<number>()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user