@serve.zone/dcrouter 11.13.0 → 11.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_serve/bundle.js +2 -2
- package/package.json +1 -1
- package/readme.md +107 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/readme.md +17 -0
- package/ts_web/router.ts +1 -1
- package/dist_ts/00_commitinfo_data.d.ts +0 -8
- package/dist_ts/00_commitinfo_data.js +0 -9
- package/dist_ts/cache/classes.cache.cleaner.d.ts +0 -47
- package/dist_ts/cache/classes.cache.cleaner.js +0 -130
- package/dist_ts/cache/classes.cached.document.d.ts +0 -76
- package/dist_ts/cache/classes.cached.document.js +0 -100
- package/dist_ts/cache/classes.cachedb.d.ts +0 -60
- package/dist_ts/cache/classes.cachedb.js +0 -126
- package/dist_ts/cache/documents/classes.cached.email.d.ts +0 -125
- package/dist_ts/cache/documents/classes.cached.email.js +0 -337
- package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +0 -119
- package/dist_ts/cache/documents/classes.cached.ip.reputation.js +0 -323
- package/dist_ts/cache/documents/index.d.ts +0 -2
- package/dist_ts/cache/documents/index.js +0 -3
- package/dist_ts/cache/index.d.ts +0 -4
- package/dist_ts/cache/index.js +0 -7
- package/dist_ts/classes.cert-provision-scheduler.d.ts +0 -54
- package/dist_ts/classes.cert-provision-scheduler.js +0 -118
- package/dist_ts/classes.dcrouter.d.ts +0 -386
- package/dist_ts/classes.dcrouter.js +0 -1682
- package/dist_ts/classes.storage-cert-manager.d.ts +0 -18
- package/dist_ts/classes.storage-cert-manager.js +0 -43
- package/dist_ts/config/classes.api-token-manager.d.ts +0 -46
- package/dist_ts/config/classes.api-token-manager.js +0 -150
- package/dist_ts/config/classes.route-config-manager.d.ts +0 -38
- package/dist_ts/config/classes.route-config-manager.js +0 -256
- package/dist_ts/config/index.d.ts +0 -3
- package/dist_ts/config/index.js +0 -5
- package/dist_ts/config/validator.d.ts +0 -104
- package/dist_ts/config/validator.js +0 -152
- package/dist_ts/errors/base.errors.d.ts +0 -224
- package/dist_ts/errors/base.errors.js +0 -320
- package/dist_ts/errors/error-handler.d.ts +0 -98
- package/dist_ts/errors/error-handler.js +0 -282
- package/dist_ts/errors/error.codes.d.ts +0 -115
- package/dist_ts/errors/error.codes.js +0 -136
- package/dist_ts/errors/index.d.ts +0 -54
- package/dist_ts/errors/index.js +0 -136
- package/dist_ts/errors/reputation.errors.d.ts +0 -183
- package/dist_ts/errors/reputation.errors.js +0 -292
- package/dist_ts/http3/http3-route-augmentation.d.ts +0 -50
- package/dist_ts/http3/http3-route-augmentation.js +0 -98
- package/dist_ts/http3/index.d.ts +0 -1
- package/dist_ts/http3/index.js +0 -2
- package/dist_ts/index.d.ts +0 -8
- package/dist_ts/index.js +0 -29
- package/dist_ts/logger.d.ts +0 -21
- package/dist_ts/logger.js +0 -81
- package/dist_ts/monitoring/classes.metricscache.d.ts +0 -32
- package/dist_ts/monitoring/classes.metricscache.js +0 -63
- package/dist_ts/monitoring/classes.metricsmanager.d.ts +0 -184
- package/dist_ts/monitoring/classes.metricsmanager.js +0 -744
- package/dist_ts/monitoring/index.d.ts +0 -1
- package/dist_ts/monitoring/index.js +0 -2
- package/dist_ts/opsserver/classes.opsserver.d.ts +0 -38
- package/dist_ts/opsserver/classes.opsserver.js +0 -87
- package/dist_ts/opsserver/handlers/admin.handler.d.ts +0 -31
- package/dist_ts/opsserver/handlers/admin.handler.js +0 -180
- package/dist_ts/opsserver/handlers/api-token.handler.d.ts +0 -6
- package/dist_ts/opsserver/handlers/api-token.handler.js +0 -62
- package/dist_ts/opsserver/handlers/certificate.handler.d.ts +0 -32
- package/dist_ts/opsserver/handlers/certificate.handler.js +0 -421
- package/dist_ts/opsserver/handlers/config.handler.d.ts +0 -7
- package/dist_ts/opsserver/handlers/config.handler.js +0 -192
- package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +0 -30
- package/dist_ts/opsserver/handlers/email-ops.handler.js +0 -227
- package/dist_ts/opsserver/handlers/index.d.ts +0 -12
- package/dist_ts/opsserver/handlers/index.js +0 -13
- package/dist_ts/opsserver/handlers/logs.handler.d.ts +0 -25
- package/dist_ts/opsserver/handlers/logs.handler.js +0 -256
- package/dist_ts/opsserver/handlers/radius.handler.d.ts +0 -6
- package/dist_ts/opsserver/handlers/radius.handler.js +0 -295
- package/dist_ts/opsserver/handlers/remoteingress.handler.d.ts +0 -6
- package/dist_ts/opsserver/handlers/remoteingress.handler.js +0 -156
- package/dist_ts/opsserver/handlers/route-management.handler.d.ts +0 -14
- package/dist_ts/opsserver/handlers/route-management.handler.js +0 -117
- package/dist_ts/opsserver/handlers/security.handler.d.ts +0 -9
- package/dist_ts/opsserver/handlers/security.handler.js +0 -233
- package/dist_ts/opsserver/handlers/stats.handler.d.ts +0 -11
- package/dist_ts/opsserver/handlers/stats.handler.js +0 -403
- package/dist_ts/opsserver/handlers/vpn.handler.d.ts +0 -6
- package/dist_ts/opsserver/handlers/vpn.handler.js +0 -199
- package/dist_ts/opsserver/helpers/guards.d.ts +0 -27
- package/dist_ts/opsserver/helpers/guards.js +0 -43
- package/dist_ts/opsserver/index.d.ts +0 -1
- package/dist_ts/opsserver/index.js +0 -2
- package/dist_ts/paths.d.ts +0 -26
- package/dist_ts/paths.js +0 -45
- package/dist_ts/plugins.d.ts +0 -81
- package/dist_ts/plugins.js +0 -115
- package/dist_ts/radius/classes.accounting.manager.d.ts +0 -231
- package/dist_ts/radius/classes.accounting.manager.js +0 -462
- package/dist_ts/radius/classes.radius.server.d.ts +0 -171
- package/dist_ts/radius/classes.radius.server.js +0 -386
- package/dist_ts/radius/classes.vlan.manager.d.ts +0 -128
- package/dist_ts/radius/classes.vlan.manager.js +0 -279
- package/dist_ts/radius/index.d.ts +0 -13
- package/dist_ts/radius/index.js +0 -14
- package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +0 -94
- package/dist_ts/remoteingress/classes.remoteingress-manager.js +0 -271
- package/dist_ts/remoteingress/classes.tunnel-manager.d.ts +0 -59
- package/dist_ts/remoteingress/classes.tunnel-manager.js +0 -165
- package/dist_ts/remoteingress/index.d.ts +0 -2
- package/dist_ts/remoteingress/index.js +0 -3
- package/dist_ts/security/classes.contentscanner.d.ts +0 -164
- package/dist_ts/security/classes.contentscanner.js +0 -642
- package/dist_ts/security/classes.ipreputationchecker.d.ts +0 -160
- package/dist_ts/security/classes.ipreputationchecker.js +0 -537
- package/dist_ts/security/classes.securitylogger.d.ts +0 -144
- package/dist_ts/security/classes.securitylogger.js +0 -235
- package/dist_ts/security/index.d.ts +0 -3
- package/dist_ts/security/index.js +0 -4
- package/dist_ts/sms/classes.smsservice.d.ts +0 -15
- package/dist_ts/sms/classes.smsservice.js +0 -72
- package/dist_ts/sms/config/sms.config.d.ts +0 -93
- package/dist_ts/sms/config/sms.config.js +0 -2
- package/dist_ts/sms/config/sms.schema.d.ts +0 -5
- package/dist_ts/sms/config/sms.schema.js +0 -121
- package/dist_ts/sms/index.d.ts +0 -1
- package/dist_ts/sms/index.js +0 -2
- package/dist_ts/storage/classes.storagemanager.d.ts +0 -83
- package/dist_ts/storage/classes.storagemanager.js +0 -348
- package/dist_ts/storage/index.d.ts +0 -1
- package/dist_ts/storage/index.js +0 -3
- package/dist_ts/vpn/classes.vpn-manager.d.ts +0 -113
- package/dist_ts/vpn/classes.vpn-manager.js +0 -297
- package/dist_ts/vpn/index.d.ts +0 -1
- package/dist_ts/vpn/index.js +0 -2
- package/dist_ts_apiclient/classes.apitoken.d.ts +0 -41
- package/dist_ts_apiclient/classes.apitoken.js +0 -115
- package/dist_ts_apiclient/classes.certificate.d.ts +0 -57
- package/dist_ts_apiclient/classes.certificate.js +0 -69
- package/dist_ts_apiclient/classes.config.d.ts +0 -7
- package/dist_ts_apiclient/classes.config.js +0 -11
- package/dist_ts_apiclient/classes.dcrouterapiclient.d.ts +0 -41
- package/dist_ts_apiclient/classes.dcrouterapiclient.js +0 -81
- package/dist_ts_apiclient/classes.email.d.ts +0 -30
- package/dist_ts_apiclient/classes.email.js +0 -52
- package/dist_ts_apiclient/classes.logs.d.ts +0 -21
- package/dist_ts_apiclient/classes.logs.js +0 -14
- package/dist_ts_apiclient/classes.radius.d.ts +0 -59
- package/dist_ts_apiclient/classes.radius.js +0 -95
- package/dist_ts_apiclient/classes.remoteingress.d.ts +0 -54
- package/dist_ts_apiclient/classes.remoteingress.js +0 -136
- package/dist_ts_apiclient/classes.route.d.ts +0 -42
- package/dist_ts_apiclient/classes.route.js +0 -154
- package/dist_ts_apiclient/classes.stats.d.ts +0 -47
- package/dist_ts_apiclient/classes.stats.js +0 -38
- package/dist_ts_apiclient/index.d.ts +0 -10
- package/dist_ts_apiclient/index.js +0 -14
- package/dist_ts_apiclient/plugins.d.ts +0 -3
- package/dist_ts_apiclient/plugins.js +0 -5
- package/dist_ts_web/00_commitinfo_data.d.ts +0 -8
- package/dist_ts_web/00_commitinfo_data.js +0 -9
- package/dist_ts_web/appstate.d.ts +0 -238
- package/dist_ts_web/appstate.js +0 -1174
- package/dist_ts_web/elements/index.d.ts +0 -13
- package/dist_ts_web/elements/index.js +0 -14
- package/dist_ts_web/elements/ops-dashboard.d.ts +0 -23
- package/dist_ts_web/elements/ops-dashboard.js +0 -323
- package/dist_ts_web/elements/ops-view-apitokens.d.ts +0 -13
- package/dist_ts_web/elements/ops-view-apitokens.js +0 -371
- package/dist_ts_web/elements/ops-view-certificates.d.ts +0 -22
- package/dist_ts_web/elements/ops-view-certificates.js +0 -528
- package/dist_ts_web/elements/ops-view-config.d.ts +0 -19
- package/dist_ts_web/elements/ops-view-config.js +0 -339
- package/dist_ts_web/elements/ops-view-emails.d.ts +0 -21
- package/dist_ts_web/elements/ops-view-emails.js +0 -165
- package/dist_ts_web/elements/ops-view-logs.d.ts +0 -13
- package/dist_ts_web/elements/ops-view-logs.js +0 -159
- package/dist_ts_web/elements/ops-view-network.d.ts +0 -71
- package/dist_ts_web/elements/ops-view-network.js +0 -764
- package/dist_ts_web/elements/ops-view-overview.d.ts +0 -22
- package/dist_ts_web/elements/ops-view-overview.js +0 -456
- package/dist_ts_web/elements/ops-view-remoteingress.d.ts +0 -20
- package/dist_ts_web/elements/ops-view-remoteingress.js +0 -494
- package/dist_ts_web/elements/ops-view-routes.d.ts +0 -12
- package/dist_ts_web/elements/ops-view-routes.js +0 -404
- package/dist_ts_web/elements/ops-view-security.d.ts +0 -21
- package/dist_ts_web/elements/ops-view-security.js +0 -574
- package/dist_ts_web/elements/ops-view-vpn.d.ts +0 -14
- package/dist_ts_web/elements/ops-view-vpn.js +0 -369
- package/dist_ts_web/elements/shared/css.d.ts +0 -1
- package/dist_ts_web/elements/shared/css.js +0 -10
- package/dist_ts_web/elements/shared/index.d.ts +0 -2
- package/dist_ts_web/elements/shared/index.js +0 -3
- package/dist_ts_web/elements/shared/ops-sectionheading.d.ts +0 -5
- package/dist_ts_web/elements/shared/ops-sectionheading.js +0 -82
- package/dist_ts_web/index.d.ts +0 -1
- package/dist_ts_web/index.js +0 -10
- package/dist_ts_web/plugins.d.ts +0 -6
- package/dist_ts_web/plugins.js +0 -11
- package/dist_ts_web/router.d.ts +0 -19
- package/dist_ts_web/router.js +0 -91
package/dist_serve/bundle.js
CHANGED
|
@@ -40174,7 +40174,7 @@ Customer Support Team`}};async onActivate(b){this.appui=b.appui,this.appui.setCo
|
|
|
40174
40174
|
}
|
|
40175
40175
|
`];render(){return w`
|
|
40176
40176
|
<dees-appui></dees-appui>
|
|
40177
|
-
`}async firstUpdated(){let o=this.shadowRoot?.querySelector("dees-appui");o&&(o.configure({branding:{logoIcon:"lucide:Box",logoText:"serve.zone"},appBar:{showSearch:!0,breadcrumbs:"serve.zone",menuItems:[{name:"File",action:i(async()=>{},"action"),submenu:[{name:"New Service",shortcut:"Cmd+N",action:i(async()=>{console.log("New Service")},"action")},{name:"Import Configuration",action:i(async()=>{console.log("Import")},"action")},{name:"Export Configuration",action:i(async()=>{console.log("Export")},"action")},{divider:!0},{name:"Preferences",shortcut:"Cmd+,",action:i(async()=>{o.navigateToView("settings")},"action")}]},{name:"View",action:i(async()=>{},"action"),submenu:[{name:"Dashboard",shortcut:"Cmd+1",action:i(async()=>{o.navigateToView("dashboard")},"action")},{name:"Services",shortcut:"Cmd+2",action:i(async()=>{o.navigateToView("services")},"action")},{name:"Network",shortcut:"Cmd+3",action:i(async()=>{o.navigateToView("network")},"action")},{divider:!0},{name:"Activity Log",shortcut:"Cmd+Shift+A",action:i(async()=>{o.toggleActivityLog()},"action")},{name:"Toggle Sidebar",shortcut:"Cmd+B",action:i(async()=>{o.setMainMenuCollapsed(!o.mainmenuCollapsed)},"action")}]},{name:"Services",action:i(async()=>{},"action"),submenu:[{name:"Deploy New Service",action:i(async()=>{console.log("Deploy")},"action")},{name:"Start All",action:i(async()=>{console.log("Start all")},"action")},{name:"Stop All",action:i(async()=>{console.log("Stop all")},"action")},{divider:!0},{name:"Garbage Collect",action:i(async()=>{console.log("GC")},"action")}]},{name:"Help",action:i(async()=>{},"action"),submenu:[{name:"Documentation",action:i(async()=>{window.open("https://docs.serve.zone","_blank")},"action")},{name:"Release Notes",action:i(async()=>{console.log("Release notes")},"action")},{divider:!0},{name:"About serve.zone",action:i(async()=>{console.log("About")},"action")}]}]},views:[{id:"dashboard",name:"Dashboard",iconName:"lucide:LayoutDashboard",content:"sz-demo-view-dashboard"},{id:"services",name:"Services",iconName:"lucide:Server",content:"sz-demo-view-services"},{id:"network",name:"Network",iconName:"lucide:Network",content:"sz-demo-view-network"},{id:"registries",name:"Registries",iconName:"lucide:Archive",content:"sz-demo-view-registries"},{id:"tokens",name:"Tokens",iconName:"lucide:Key",content:"sz-demo-view-tokens"},{id:"mta",name:"Email / MTA",iconName:"lucide:Mail",content:"sz-demo-view-mta"},{id:"routes",name:"Routes",iconName:"lucide:Route",content:"sz-demo-view-routes"},{id:"settings",name:"Settings",iconName:"lucide:Settings",content:"sz-demo-view-settings"}],mainMenu:{sections:[{name:"Overview",views:["dashboard"]},{name:"Infrastructure",views:["services","network","registries","mta","routes"]},{name:"Administration",views:["tokens","settings"]}]},defaultView:"dashboard",onViewChange:i((l,h)=>{console.log("View changed to:",l,h)},"onViewChange")}),o.setUser({name:"Admin User",email:"admin@serve.zone",status:"online"}),o.setProfileMenuItems([{name:"Profile",iconName:"lucide:User",action:i(async()=>{console.log("Profile")},"action")},{name:"Preferences",iconName:"lucide:SlidersHorizontal",action:i(async()=>{console.log("Preferences")},"action")},{divider:!0},{name:"Sign Out",iconName:"lucide:LogOut",action:i(async()=>{console.log("Sign Out")},"action")}]))}static{Mva(r,a)}};return n=r})();var xu={};it(xu,{TypedSocket:()=>nqe});var sqe={};it(sqe,{sha256FromString:()=>Cva});var uH={};it(uH,{Smartenv:()=>iqe});var rqe=K1(Qaa(),1);var iqe=class{static{i(this,"Smartenv")}constructor(){this.loadedScripts=[]}async getEnvAwareModule(e){if(this.isNode)return await this.getSafeNodeModule(e.nodeModuleName);if(this.isBrowser)return await this.getSafeWebModule(e.webUrlArg,e.getFunction);console.error("platform for loading not supported by smartenv")}async getSafeNodeModule(e){if(!this.isNode){console.error(`You tried to load a node module in a wrong context: ${e}`);return}return new Function(`return import('${e}')`)()}async getSafeWebModule(e,a){if(!this.isBrowser){console.error("You tried to load a web module in a wrong context");return}if(this.loadedScripts.includes(e))return a();this.loadedScripts.push(e);let r=rqe.defer();if(globalThis.importScripts)globalThis.importScripts(e),r.resolve();else{let s=document.createElement("script");s.onload=()=>{r.resolve()},s.src=e,document.head.appendChild(s)}return await r.promise,a()}get runtimeEnv(){return typeof process<"u"?"node":"browser"}get isBrowser(){return!this.isNode}get userAgent(){return this.isBrowser?navigator.userAgent:"undefined"}get isNode(){return this.runtimeEnv==="node"}get nodeVersion(){return process.version}get isCI(){return this.isNode?!!process.env.CI:!1}async isMacAsync(){return this.isNode?(await this.getSafeNodeModule("os")).platform()==="darwin":!1}async isWindowsAsync(){return this.isNode?(await this.getSafeNodeModule("os")).platform()==="win32":!1}async isLinuxAsync(){return this.isNode?(await this.getSafeNodeModule("os")).platform()==="linux":!1}async printEnv(){this.isNode?(console.log("running on NODE"),console.log("node version is "+this.nodeVersion)):(console.log("running on BROWSER"),console.log("browser is "+this.userAgent))}};var Sva=i(t=>{let e=[],a=new DataView(t);for(let r=0;r<a.byteLength;r+=4){let n=a.getUint32(r).toString(16),o="00000000",l=(o+n).slice(-o.length);e.push(l)}return e.join("")},"hex"),Cva=i(async t=>{let e=new uH.Smartenv;if(e.isBrowser){let a=new TextEncoder().encode(t),r=await crypto.subtle.digest("SHA-256",a);return Sva(r)}else if(e.isNode)return await(await e.getSafeNodeModule("@pushrocks/smarthash")).sha256FromString(t)},"sha256FromString");J5();u5();O2();vse();cr();ns();a0();UW();var oqe="__typedsocket_tag__";function Tva(t){return{peer:t,async getTagById(e){if(!t.tags.has(e))return;let a=t.data.get(`${oqe}${e}`);return{id:e,payload:a}}}}i(Tva,"wrapSmartServePeer");var nqe=class t{static{i(this,"TypedSocket")}static async createClient(e,a,r={}){let n={...{autoReconnect:!0,maxRetries:100,initialBackoffMs:1e3,maxBackoffMs:6e4},...r},o=new t("client",e);return o.clientOptions=n,o.serverUrl=a,o.currentBackoff=n.initialBackoffMs,await o.connect(),o}static{this.useWindowLocationOriginUrl=()=>Hi.Smarturl.createFromUrl(globalThis.location.origin).toString()}static fromSmartServe(e,a){let r=new Map;t.registerTagHandlers(a);let s=new t("server",a);return s.smartServeRef=e,s.smartServeConnectionWrappers=r,s}static registerTagHandlers(e){e.addTypedHandler(new a1.TypedHandler("__typedsocket_setTag",async(a,r)=>{let s=r?.localData?.peer;return s?(s.tags.add(a.name),s.data.set(`${oqe}${a.name}`,a.payload),{success:!0}):(console.warn("setTag: No peer found in request context"),{success:!1})})),e.addTypedHandler(new a1.TypedHandler("__typedsocket_removeTag",async(a,r)=>{let s=r?.localData?.peer;return s?(s.tags.delete(a.name),s.data.delete(`${oqe}${a.name}`),{success:!0}):(console.warn("removeTag: No peer found in request context"),{success:!1})}))}constructor(e,a){this.statusSubject=new Wt.rxjs.Subject,this.connectionStatus="new",this.websocket=null,this.clientOptions=null,this.serverUrl="",this.retryCount=0,this.currentBackoff=1e3,this.pendingRequests=new Map,this.smartServeRef=null,this.smartServeConnectionWrappers=new Map,this.side=e,this.typedrouter=a}async connect(){let e=Je.defer();this.updateStatus("connecting");let a=this.toWebSocketUrl(this.serverUrl);console.log(`TypedSocket connecting to ${a}...`),this.websocket=new WebSocket(a);let r=setTimeout(()=>{this.connectionStatus!=="connected"&&(console.warn("TypedSocket connection timeout"),this.websocket?.close(),e.reject(new Error("Connection timeout")))},1e4);this.websocket.onopen=()=>{clearTimeout(r),console.log("TypedSocket connected!"),this.updateStatus("connected"),this.retryCount=0,this.currentBackoff=this.clientOptions?.initialBackoffMs??1e3,e.resolve()},this.websocket.onmessage=async s=>{await this.handleMessage(s.data)},this.websocket.onclose=()=>{clearTimeout(r),this.handleDisconnect()},this.websocket.onerror=s=>{console.error("TypedSocket WebSocket error:",s)};try{await e.promise}catch(s){if(clearTimeout(r),this.clientOptions?.autoReconnect)await this.scheduleReconnect();else throw s}}toWebSocketUrl(e){let a=new URL(e);return`${a.protocol==="https:"?"wss:":"ws:"}//${a.host}${a.pathname}`}async handleMessage(e){try{let a=typeof e=="string"?e:new TextDecoder().decode(e),r=ri.parse(a);if(r.correlation?.id&&this.pendingRequests.has(r.correlation.id)){let n=this.pendingRequests.get(r.correlation.id);this.pendingRequests.delete(r.correlation.id),n.resolve(r);return}let s=await this.typedrouter.routeAndAddResponse(r);s&&this.websocket?.readyState===WebSocket.OPEN&&this.websocket.send(ri.stringify(s))}catch(a){console.error("TypedSocket failed to process message:",a)}}handleDisconnect(){if(this.connectionStatus!=="disconnected"){this.updateStatus("disconnected");for(let[e,a]of this.pendingRequests)a.reject(new Error("TypedSocket disconnected"));this.pendingRequests.clear(),this.clientOptions?.autoReconnect&&this.retryCount<this.clientOptions.maxRetries&&this.scheduleReconnect()}}async scheduleReconnect(){if(!this.clientOptions)return;this.updateStatus("reconnecting"),this.retryCount++;let e=this.currentBackoff*.2*(Math.random()*2-1),a=Math.min(this.currentBackoff+e,this.clientOptions.maxBackoffMs);console.log(`TypedSocket reconnecting in ${Math.round(a)}ms (attempt ${this.retryCount}/${this.clientOptions.maxRetries})`),await St.delayFor(a),this.currentBackoff=Math.min(this.currentBackoff*2,this.clientOptions.maxBackoffMs);try{await this.connect()}catch(r){console.error("TypedSocket reconnection failed:",r)}}updateStatus(e){this.connectionStatus!==e&&(this.connectionStatus=e,this.statusSubject.next(e))}async sendRequest(e){if(!this.websocket||this.websocket.readyState!==WebSocket.OPEN)throw new Error("WebSocket not connected");return new Promise((a,r)=>{let s=setTimeout(()=>{this.pendingRequests.delete(e.correlation.id),r(new Error("Request timeout"))},3e4);this.pendingRequests.set(e.correlation.id,{resolve:i(n=>{clearTimeout(s),a(n)},"resolve"),reject:i(n=>{clearTimeout(s),r(n)},"reject")}),this.websocket.send(ri.stringify(e))})}createTypedRequest(e,a){let r=i(async s=>{if(this.side==="client")return this.sendRequest(s);if(!this.smartServeRef)throw new Error("Server not initialized");let n=a;if(!n){let l=this.smartServeRef.getWebSocketConnections();if(l.length===1){let h=l[0];n=this.getOrCreateWrapper(h)}else throw l.length===0?new Error("No WebSocket connections available"):new Error("Multiple connections available - specify targetConnection")}let o=await this.typedrouter.fireEventInterestMap.addInterest(s.correlation.id,s);return n.peer.send(ri.stringify(s)),await o.interestFullfilled},"postMethod");return new a1.TypedRequest(new a1.TypedTarget({postMethod:r}),e)}getStatus(){return this.connectionStatus}async stop(){this.side==="client"?(this.clientOptions&&(this.clientOptions.autoReconnect=!1),this.websocket&&(this.websocket.close(),this.websocket=null),this.pendingRequests.clear()):this.smartServeConnectionWrappers.clear()}async setTag(e,a){if(this.side!=="client")throw new Error("setTag is only available on clients");if(!(await this.createTypedRequest("__typedsocket_setTag").fire({name:e,payload:a})).success)throw new Error("Failed to set tag on server")}async removeTag(e){if(this.side!=="client")throw new Error("removeTag is only available on clients");if(!(await this.createTypedRequest("__typedsocket_removeTag").fire({name:e})).success)throw new Error("Failed to remove tag on server")}getOrCreateWrapper(e){let a=this.smartServeConnectionWrappers.get(e.id);return a||(a=Tva(e),this.smartServeConnectionWrappers.set(e.id,a)),a}async findAllTargetConnections(e){if(this.side!=="server"||!this.smartServeRef)throw new Error("findAllTargetConnections is only available on servers");let a=[];for(let r of this.smartServeRef.getWebSocketConnections()){let s=this.getOrCreateWrapper(r);await e(s)&&a.push(s)}return a}async findTargetConnection(e){return(await this.findAllTargetConnections(e))[0]}async findAllTargetConnectionsByTag(e,a){if(this.side!=="server"||!this.smartServeRef)throw new Error("findAllTargetConnectionsByTag is only available on servers");let r=this.smartServeRef.getWebSocketConnectionsByTag(e),s=[];for(let n of r){let o=this.getOrCreateWrapper(n);if(a!==void 0){let l=await o.getTagById(e);if(ri.stringify(l?.payload)!==ri.stringify(a))continue}s.push(o)}return s}async findTargetConnectionByTag(e,a){return(await this.findAllTargetConnectionsByTag(e,a))[0]}};de();Xa();var vt=gt;u5();var Jaa={};var e1a={};var Hs=new vt.plugins.smartstate.Smartstate,Ka=await Hs.getStatePart("login",{identity:null,isLoggedIn:!1},"persistent"),lr=await Hs.getStatePart("stats",{serverStats:null,emailStats:null,dnsStats:null,securityMetrics:null,lastUpdated:0,isLoading:!1,error:null},"soft"),H6=await Hs.getStatePart("config",{config:null,isLoading:!1,error:null}),Lva=i(()=>{let t=typeof window<"u"?window.location.pathname:"/",e=["overview","network","emails","logs","routes","apitokens","configuration","security","certificates","remoteingress"],r=t.split("/").filter(Boolean)[0];return e.includes(r)?r:"overview"},"getInitialView"),ci=await Hs.getStatePart("ui",{activeView:Lva(),sidebarCollapsed:!1,autoRefresh:!0,refreshInterval:1e3,theme:"light"}),jo=await Hs.getStatePart("logs",{recentLogs:[],isStreaming:!1,filters:{}},"soft"),$2=await Hs.getStatePart("network",{connections:[],connectionsByIP:{},throughputRate:{bytesInPerSecond:0,bytesOutPerSecond:0},totalBytes:{in:0,out:0},topIPs:[],throughputByIP:[],throughputHistory:[],requestsPerSecond:0,requestsTotal:0,backends:[],lastUpdated:0,isLoading:!1,error:null},"soft"),NX=await Hs.getStatePart("emailOps",{emails:[],isLoading:!1,error:null,lastUpdated:0},"soft"),di=await Hs.getStatePart("certificates",{certificates:[],summary:{total:0,valid:0,expiring:0,expired:0,failed:0,unknown:0},isLoading:!1,error:null,lastUpdated:0},"soft"),S1=await Hs.getStatePart("remoteIngress",{edges:[],statuses:[],selectedEdgeId:null,newEdgeId:null,isLoading:!1,error:null,lastUpdated:0},"soft"),pa=await Hs.getStatePart("routeManagement",{mergedRoutes:[],warnings:[],apiTokens:[],isLoading:!1,error:null,lastUpdated:0},"soft"),Jt=i(()=>{let t=Ka.getState().identity;return t&&t.expiresAt&&t.expiresAt<Date.now()?{identity:null}:{identity:t}},"getActionContext"),o1a=Ka.createAction(async(t,e)=>{let a=new vt.plugins.typedrequest.TypedRequest("/typedrequest","adminLoginWithUsernameAndPassword");try{let r=await a.fire({username:e.username,password:e.password});return r.identity?{identity:r.identity,isLoggedIn:!0}:t.getState()}catch(r){return console.error("Login failed:",r),t.getState()}}),wu=Ka.createAction(async t=>{let e=Jt();if(e.identity){let a=new vt.plugins.typedrequest.TypedRequest("/typedrequest","adminLogout");try{await a.fire({identity:e.identity})}catch(r){console.error("Logout error:",r)}}return{identity:null,isLoggedIn:!1}}),Mu=lr.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getCombinedMetrics").fire({identity:e.identity,sections:{server:!0,email:!0,dns:!0,security:!0,network:!1}});return{serverStats:s.metrics.server||a.serverStats,emailStats:s.metrics.email||a.emailStats,dnsStats:s.metrics.dns||a.dnsStats,securityMetrics:s.metrics.security||a.securityMetrics,lastUpdated:Date.now(),isLoading:!1,error:null}}catch(r){return{...a,isLoading:!1,error:r.message||"Failed to fetch statistics"}}}),IX=H6.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{return{config:(await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getConfiguration").fire({identity:e.identity})).config,isLoading:!1,error:null}}catch(r){return{...a,isLoading:!1,error:r.message||"Failed to fetch configuration"}}}),FX=jo.createAction(async(t,e)=>{let a=Jt();if(!a.identity)return t.getState();let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getRecentLogs").fire({identity:a.identity,limit:e.limit||100,level:e.level,category:e.category});return{...t.getState(),recentLogs:s.logs}}),Bji=ci.createAction(async t=>{let e=t.getState();return{...e,autoRefresh:!e.autoRefresh}}),Hji=ci.createAction(async(t,e)=>{let a=t.getState();return e==="network"&&a.activeView!=="network"&&setTimeout(()=>{$2.dispatchAction(pqe,null)},100),e==="certificates"&&a.activeView!=="certificates"&&setTimeout(()=>{di.dispatchAction(xl,null)},100),e==="routes"&&a.activeView!=="routes"&&setTimeout(()=>{pa.dispatchAction(D2,null)},100),e==="apitokens"&&a.activeView!=="apitokens"&&setTimeout(()=>{pa.dispatchAction(yl,null)},100),e==="remoteingress"&&a.activeView!=="remoteingress"&&setTimeout(()=>{S1.dispatchAction(vl,null)},100),{...a,activeView:e}}),pqe=$2.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getActiveConnections").fire({identity:e.identity}),o=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getNetworkStats").fire({identity:e.identity}),l={};return o.connectionsByIP&&Array.isArray(o.connectionsByIP)?o.connectionsByIP.forEach(h=>{l[h.ip]=h.count}):s.connections.forEach(h=>{let p=h.remoteAddress;l[p]=(l[p]||0)+1}),{connections:s.connections,connectionsByIP:l,throughputRate:o.throughputRate||{bytesInPerSecond:0,bytesOutPerSecond:0},totalBytes:o.totalDataTransferred?{in:o.totalDataTransferred.bytesIn,out:o.totalDataTransferred.bytesOut}:{in:0,out:0},topIPs:o.topIPs||[],throughputByIP:o.throughputByIP||[],throughputHistory:o.throughputHistory||[],requestsPerSecond:o.requestsPerSecond||0,requestsTotal:o.requestsTotal||0,backends:o.backends||[],lastUpdated:Date.now(),isLoading:!1,error:null}}catch(r){return console.error("Failed to fetch network stats:",r),{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch network stats"}}}),n1a=NX.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{return{emails:(await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getAllEmails").fire({identity:e.identity})).emails,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch emails"}}}),xl=di.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getCertificateOverview").fire({identity:e.identity});return{certificates:s.certificates,summary:s.summary,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch certificate overview"}}}),l1a=di.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","reprovisionCertificateDomain").fire({identity:r.identity,domain:e}),await a.dispatch(xl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to reprovision certificate"}}}),c1a=di.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","deleteCertificate").fire({identity:r.identity,domain:e}),await a.dispatch(xl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to delete certificate"}}}),d1a=di.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","importCertificate").fire({identity:r.identity,cert:e}),await a.dispatch(xl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to import certificate"}}});async function u1a(t){let e=Jt();return new vt.plugins.typedrequest.TypedRequest("/typedrequest","exportCertificate").fire({identity:e.identity,domain:t})}i(u1a,"fetchCertificateExport");async function fqe(t){let e=Jt();return new vt.plugins.typedrequest.TypedRequest("/typedrequest","getRemoteIngressConnectionToken").fire({identity:e.identity,edgeId:t})}i(fqe,"fetchConnectionToken");var vl=S1.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let r=new vt.plugins.typedrequest.TypedRequest("/typedrequest","getRemoteIngresses"),s=new vt.plugins.typedrequest.TypedRequest("/typedrequest","getRemoteIngressStatus"),[n,o]=await Promise.all([r.fire({identity:e.identity}),s.fire({identity:e.identity})]);return{...a,edges:n.edges,statuses:o.statuses,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch remote ingress data"}}}),p1a=S1.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{let o=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","createRemoteIngress").fire({identity:r.identity,name:e.name,listenPorts:e.listenPorts,autoDerivePorts:e.autoDerivePorts,tags:e.tags});return o.success?(await a.dispatch(vl,null),{...t.getState(),newEdgeId:o.edge.id}):s}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to create edge"}}}),f1a=S1.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","deleteRemoteIngress").fire({identity:r.identity,id:e}),await a.dispatch(vl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to delete edge"}}}),h1a=S1.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","updateRemoteIngress").fire({identity:r.identity,id:e.id,name:e.name,listenPorts:e.listenPorts,autoDerivePorts:e.autoDerivePorts,tags:e.tags}),await a.dispatch(vl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to update edge"}}}),m1a=S1.createAction(async(t,e)=>{let a=Jt(),r=t.getState();try{return(await new vt.plugins.typedrequest.TypedRequest("/typedrequest","regenerateRemoteIngressSecret").fire({identity:a.identity,id:e})).success?{...r,newEdgeId:e}:r}catch(s){return{...r,error:s instanceof Error?s.message:"Failed to regenerate secret"}}}),g1a=S1.createAction(async t=>({...t.getState(),newEdgeId:null})),hqe=S1.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","updateRemoteIngress").fire({identity:r.identity,id:e.id,enabled:e.enabled}),await a.dispatch(vl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to toggle edge"}}}),ui=await Hs.getStatePart("vpn",{clients:[],status:null,isLoading:!1,error:null,lastUpdated:0,newClientConfig:null},"soft"),yu=ui.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let r=new vt.plugins.typedrequest.TypedRequest("/typedrequest","getVpnClients"),s=new vt.plugins.typedrequest.TypedRequest("/typedrequest","getVpnStatus"),[n,o]=await Promise.all([r.fire({identity:e.identity}),s.fire({identity:e.identity})]);return{...a,clients:n.clients,status:o.status,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch VPN data"}}}),b1a=ui.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{let o=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","createVpnClient").fire({identity:r.identity,clientId:e.clientId,tags:e.tags,description:e.description});return o.success?{...await a.dispatch(yu,null),newClientConfig:o.wireguardConfig||null}:{...s,error:o.message||"Failed to create client"}}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to create VPN client"}}}),v1a=ui.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","deleteVpnClient").fire({identity:r.identity,clientId:e}),await a.dispatch(yu,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to delete VPN client"}}}),x1a=ui.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{let n=e.enabled?"enableVpnClient":"disableVpnClient";return await new vt.plugins.typedrequest.TypedRequest("/typedrequest",n).fire({identity:r.identity,clientId:e.clientId}),await a.dispatch(yu,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to toggle VPN client"}}}),y1a=ui.createAction(async t=>({...t.getState(),newClientConfig:null})),D2=pa.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getMergedRoutes").fire({identity:e.identity});return{...a,mergedRoutes:s.routes,warnings:s.warnings,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch routes"}}}),w1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","createRoute").fire({identity:r.identity,route:e.route,enabled:e.enabled}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to create route"}}}),M1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","deleteRoute").fire({identity:r.identity,id:e}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to delete route"}}}),k1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","toggleRoute").fire({identity:r.identity,id:e.id,enabled:e.enabled}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to toggle route"}}}),mqe=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","setRouteOverride").fire({identity:r.identity,routeName:e.routeName,enabled:e.enabled}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to set override"}}}),z1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","removeRouteOverride").fire({identity:r.identity,routeName:e}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to remove override"}}}),yl=pa.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","listApiTokens").fire({identity:e.identity});return{...a,apiTokens:s.tokens}}catch(r){return{...a,error:r instanceof Error?r.message:"Failed to fetch tokens"}}});async function S1a(t,e,a){let r=Jt();return new vt.plugins.typedrequest.TypedRequest("/typedrequest","createApiToken").fire({identity:r.identity,name:t,scopes:e,expiresInDays:a})}i(S1a,"createApiToken");async function C1a(t){let e=Jt();return new vt.plugins.typedrequest.TypedRequest("/typedrequest","rollApiToken").fire({identity:e.identity,id:t})}i(C1a,"rollApiToken");var _1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","revokeApiToken").fire({identity:r.identity,id:e}),await a.dispatch(yl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to revoke token"}}}),gqe=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","toggleApiToken").fire({identity:r.identity,id:e.id,enabled:e.enabled}),await a.dispatch(yl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to toggle token"}}}),O6=null,t1a=new vt.plugins.typedrequest.TypedRouter,EX=[],lqe=!1;function $va(){if(lqe=!1,EX.length===0)return;let t=jo.getState(),e=[...t.recentLogs,...EX];EX=[],e.length>2e3&&e.splice(0,e.length-2e3),jo.setState({...t,recentLogs:e})}i($va,"flushLogEntries"),t1a.addTypedHandler(new vt.plugins.typedrequest.TypedHandler("pushLogEntry",async t=>(EX.push(t.entry),lqe||(lqe=!0,requestAnimationFrame($va)),{})));async function cqe(){if(!O6)try{O6=await xu.TypedSocket.createClient(t1a,xu.TypedSocket.useWindowLocationOriginUrl()),await O6.setTag("role","ops_dashboard")}catch(t){console.error("TypedSocket connection failed:",t),O6=null}}i(cqe,"connectSocket");async function a1a(){if(O6){try{await O6.stop()}catch{}O6=null}}i(a1a,"disconnectSocket");var dqe=!1;async function Dva(){if(!dqe){dqe=!0;try{await Ava()}finally{dqe=!1}}}i(Dva,"dispatchCombinedRefreshAction");async function Ava(){let t=Jt();if(!t.identity)return;let e=ci.getState().activeView;try{let r=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getCombinedMetrics").fire({identity:t.identity,sections:{server:!0,email:!0,dns:!0,security:!0,network:e==="network"}}),s=lr.getState();if(lr.setState({...s,serverStats:r.metrics.server||s.serverStats,emailStats:r.metrics.email||s.emailStats,dnsStats:r.metrics.dns||s.dnsStats,securityMetrics:r.metrics.security||s.securityMetrics,lastUpdated:Date.now(),isLoading:!1,error:null}),r.metrics.network&&e==="network"){let n=r.metrics.network,o={};n.connectionDetails.forEach(l=>{o[l.remoteAddress]=(o[l.remoteAddress]||0)+1});try{let h=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getActiveConnections").fire({identity:t.identity});$2.setState({...$2.getState(),connections:h.connections,connectionsByIP:o,throughputRate:{bytesInPerSecond:n.totalBandwidth.in,bytesOutPerSecond:n.totalBandwidth.out},totalBytes:n.totalBytes||{in:0,out:0},topIPs:n.topEndpoints.map(p=>({ip:p.endpoint,count:p.requests})),throughputByIP:n.topEndpoints.map(p=>({ip:p.endpoint,in:p.bandwidth?.in||0,out:p.bandwidth?.out||0})),throughputHistory:n.throughputHistory||[],requestsPerSecond:n.requestsPerSecond||0,requestsTotal:n.requestsTotal||0,backends:n.backends||[],lastUpdated:Date.now(),isLoading:!1,error:null})}catch(l){console.error("Failed to fetch connections:",l),$2.setState({...$2.getState(),connections:[],connectionsByIP:o,throughputRate:{bytesInPerSecond:n.totalBandwidth.in,bytesOutPerSecond:n.totalBandwidth.out},totalBytes:n.totalBytes||{in:0,out:0},topIPs:n.topEndpoints.map(h=>({ip:h.endpoint,count:h.requests})),throughputByIP:n.topEndpoints.map(h=>({ip:h.endpoint,in:h.bandwidth?.in||0,out:h.bandwidth?.out||0})),throughputHistory:n.throughputHistory||[],requestsPerSecond:n.requestsPerSecond||0,requestsTotal:n.requestsTotal||0,backends:n.backends||[],lastUpdated:Date.now(),isLoading:!1,error:null})}}if(e==="certificates")try{await di.dispatchAction(xl,null)}catch(n){console.error("Certificate refresh failed:",n)}if(e==="remoteingress")try{await S1.dispatchAction(vl,null)}catch(n){console.error("Remote ingress refresh failed:",n)}if(e==="vpn")try{await ui.dispatchAction(yu,null)}catch(n){console.error("VPN refresh failed:",n)}}catch(a){console.error("Combined refresh failed:",a);let r=String(a);(r.includes("invalid")||r.includes("unauthorized")||r.includes("401"))&&(await Ka.dispatchAction(wu,null),window.location.reload())}}i(Ava,"dispatchCombinedRefreshActionInner");var Pva=lr.createAction(async t=>(await Dva(),t.getState())),B6=null,uqe=i(()=>{let t=ci.getState(),e=Ka.getState();t.autoRefresh&&e.isLoggedIn?(B6&&(B6.dispose(),B6=null),B6=lr.createScheduledAction({action:Pva,payload:void 0,intervalMs:t.refreshInterval,autoPause:"visibility"})):B6&&(B6.dispose(),B6=null)},"startAutoRefresh"),r1a=ci.getState().autoRefresh,i1a=ci.getState().refreshInterval,s1a=Ka.getState().isLoggedIn;ci.select(t=>({autoRefresh:t.autoRefresh,refreshInterval:t.refreshInterval})).subscribe(t=>{(t.autoRefresh!==r1a||t.refreshInterval!==i1a)&&(r1a=t.autoRefresh,i1a=t.refreshInterval,uqe())}),Ka.select(t=>t.isLoggedIn).subscribe(t=>{t!==s1a&&(s1a=t,uqe(),t?cqe():a1a())}),document.addEventListener("visibilitychange",()=>{document.hidden?a1a():Ka.getState().isLoggedIn&&cqe()}),uqe(),Ka.getState().isLoggedIn&&cqe();var Eva=vt.plugins.smartrouter.SmartRouter,bqe=["overview","network","emails","logs","routes","apitokens","configuration","security","certificates","remoteingress"],vqe=class{static{i(this,"AppRouter")}router;initialized=!1;suppressStateUpdate=!1;constructor(){this.router=new Eva({debug:!1})}init(){this.initialized||(this.setupRoutes(),this.setupStateSync(),this.handleInitialRoute(),this.initialized=!0)}setupRoutes(){for(let e of bqe)this.router.on(`/${e}`,async()=>{this.updateViewState(e)});this.router.on("/",async()=>{this.navigateTo("/overview")})}setupStateSync(){ci.select().subscribe(e=>{if(this.suppressStateUpdate)return;let a=window.location.pathname,r=`/${e.activeView}`;a!==r&&(this.suppressStateUpdate=!0,this.router.pushUrl(r),this.suppressStateUpdate=!1)})}handleInitialRoute(){let e=window.location.pathname;if(!e||e==="/")this.router.pushUrl("/overview");else{let r=e.split("/").filter(Boolean)[0];bqe.includes(r)?this.updateViewState(r):this.router.pushUrl("/overview")}}updateViewState(e){this.suppressStateUpdate=!0;let a=ci.getState();a.activeView!==e&&ci.setState({...a,activeView:e}),this.suppressStateUpdate=!1}navigateTo(e){this.router.pushUrl(e)}navigateToView(e){bqe.includes(e)?this.navigateTo(`/${e}`):this.navigateTo("/overview")}getCurrentView(){return ci.getState().activeView}destroy(){this.router.destroy(),this.initialized=!1}},ku=new vqe;de();var n1=Z`
|
|
40177
|
+
`}async firstUpdated(){let o=this.shadowRoot?.querySelector("dees-appui");o&&(o.configure({branding:{logoIcon:"lucide:Box",logoText:"serve.zone"},appBar:{showSearch:!0,breadcrumbs:"serve.zone",menuItems:[{name:"File",action:i(async()=>{},"action"),submenu:[{name:"New Service",shortcut:"Cmd+N",action:i(async()=>{console.log("New Service")},"action")},{name:"Import Configuration",action:i(async()=>{console.log("Import")},"action")},{name:"Export Configuration",action:i(async()=>{console.log("Export")},"action")},{divider:!0},{name:"Preferences",shortcut:"Cmd+,",action:i(async()=>{o.navigateToView("settings")},"action")}]},{name:"View",action:i(async()=>{},"action"),submenu:[{name:"Dashboard",shortcut:"Cmd+1",action:i(async()=>{o.navigateToView("dashboard")},"action")},{name:"Services",shortcut:"Cmd+2",action:i(async()=>{o.navigateToView("services")},"action")},{name:"Network",shortcut:"Cmd+3",action:i(async()=>{o.navigateToView("network")},"action")},{divider:!0},{name:"Activity Log",shortcut:"Cmd+Shift+A",action:i(async()=>{o.toggleActivityLog()},"action")},{name:"Toggle Sidebar",shortcut:"Cmd+B",action:i(async()=>{o.setMainMenuCollapsed(!o.mainmenuCollapsed)},"action")}]},{name:"Services",action:i(async()=>{},"action"),submenu:[{name:"Deploy New Service",action:i(async()=>{console.log("Deploy")},"action")},{name:"Start All",action:i(async()=>{console.log("Start all")},"action")},{name:"Stop All",action:i(async()=>{console.log("Stop all")},"action")},{divider:!0},{name:"Garbage Collect",action:i(async()=>{console.log("GC")},"action")}]},{name:"Help",action:i(async()=>{},"action"),submenu:[{name:"Documentation",action:i(async()=>{window.open("https://docs.serve.zone","_blank")},"action")},{name:"Release Notes",action:i(async()=>{console.log("Release notes")},"action")},{divider:!0},{name:"About serve.zone",action:i(async()=>{console.log("About")},"action")}]}]},views:[{id:"dashboard",name:"Dashboard",iconName:"lucide:LayoutDashboard",content:"sz-demo-view-dashboard"},{id:"services",name:"Services",iconName:"lucide:Server",content:"sz-demo-view-services"},{id:"network",name:"Network",iconName:"lucide:Network",content:"sz-demo-view-network"},{id:"registries",name:"Registries",iconName:"lucide:Archive",content:"sz-demo-view-registries"},{id:"tokens",name:"Tokens",iconName:"lucide:Key",content:"sz-demo-view-tokens"},{id:"mta",name:"Email / MTA",iconName:"lucide:Mail",content:"sz-demo-view-mta"},{id:"routes",name:"Routes",iconName:"lucide:Route",content:"sz-demo-view-routes"},{id:"settings",name:"Settings",iconName:"lucide:Settings",content:"sz-demo-view-settings"}],mainMenu:{sections:[{name:"Overview",views:["dashboard"]},{name:"Infrastructure",views:["services","network","registries","mta","routes"]},{name:"Administration",views:["tokens","settings"]}]},defaultView:"dashboard",onViewChange:i((l,h)=>{console.log("View changed to:",l,h)},"onViewChange")}),o.setUser({name:"Admin User",email:"admin@serve.zone",status:"online"}),o.setProfileMenuItems([{name:"Profile",iconName:"lucide:User",action:i(async()=>{console.log("Profile")},"action")},{name:"Preferences",iconName:"lucide:SlidersHorizontal",action:i(async()=>{console.log("Preferences")},"action")},{divider:!0},{name:"Sign Out",iconName:"lucide:LogOut",action:i(async()=>{console.log("Sign Out")},"action")}]))}static{Mva(r,a)}};return n=r})();var xu={};it(xu,{TypedSocket:()=>nqe});var sqe={};it(sqe,{sha256FromString:()=>Cva});var uH={};it(uH,{Smartenv:()=>iqe});var rqe=K1(Qaa(),1);var iqe=class{static{i(this,"Smartenv")}constructor(){this.loadedScripts=[]}async getEnvAwareModule(e){if(this.isNode)return await this.getSafeNodeModule(e.nodeModuleName);if(this.isBrowser)return await this.getSafeWebModule(e.webUrlArg,e.getFunction);console.error("platform for loading not supported by smartenv")}async getSafeNodeModule(e){if(!this.isNode){console.error(`You tried to load a node module in a wrong context: ${e}`);return}return new Function(`return import('${e}')`)()}async getSafeWebModule(e,a){if(!this.isBrowser){console.error("You tried to load a web module in a wrong context");return}if(this.loadedScripts.includes(e))return a();this.loadedScripts.push(e);let r=rqe.defer();if(globalThis.importScripts)globalThis.importScripts(e),r.resolve();else{let s=document.createElement("script");s.onload=()=>{r.resolve()},s.src=e,document.head.appendChild(s)}return await r.promise,a()}get runtimeEnv(){return typeof process<"u"?"node":"browser"}get isBrowser(){return!this.isNode}get userAgent(){return this.isBrowser?navigator.userAgent:"undefined"}get isNode(){return this.runtimeEnv==="node"}get nodeVersion(){return process.version}get isCI(){return this.isNode?!!process.env.CI:!1}async isMacAsync(){return this.isNode?(await this.getSafeNodeModule("os")).platform()==="darwin":!1}async isWindowsAsync(){return this.isNode?(await this.getSafeNodeModule("os")).platform()==="win32":!1}async isLinuxAsync(){return this.isNode?(await this.getSafeNodeModule("os")).platform()==="linux":!1}async printEnv(){this.isNode?(console.log("running on NODE"),console.log("node version is "+this.nodeVersion)):(console.log("running on BROWSER"),console.log("browser is "+this.userAgent))}};var Sva=i(t=>{let e=[],a=new DataView(t);for(let r=0;r<a.byteLength;r+=4){let n=a.getUint32(r).toString(16),o="00000000",l=(o+n).slice(-o.length);e.push(l)}return e.join("")},"hex"),Cva=i(async t=>{let e=new uH.Smartenv;if(e.isBrowser){let a=new TextEncoder().encode(t),r=await crypto.subtle.digest("SHA-256",a);return Sva(r)}else if(e.isNode)return await(await e.getSafeNodeModule("@pushrocks/smarthash")).sha256FromString(t)},"sha256FromString");J5();u5();O2();vse();cr();ns();a0();UW();var oqe="__typedsocket_tag__";function Tva(t){return{peer:t,async getTagById(e){if(!t.tags.has(e))return;let a=t.data.get(`${oqe}${e}`);return{id:e,payload:a}}}}i(Tva,"wrapSmartServePeer");var nqe=class t{static{i(this,"TypedSocket")}static async createClient(e,a,r={}){let n={...{autoReconnect:!0,maxRetries:100,initialBackoffMs:1e3,maxBackoffMs:6e4},...r},o=new t("client",e);return o.clientOptions=n,o.serverUrl=a,o.currentBackoff=n.initialBackoffMs,await o.connect(),o}static{this.useWindowLocationOriginUrl=()=>Hi.Smarturl.createFromUrl(globalThis.location.origin).toString()}static fromSmartServe(e,a){let r=new Map;t.registerTagHandlers(a);let s=new t("server",a);return s.smartServeRef=e,s.smartServeConnectionWrappers=r,s}static registerTagHandlers(e){e.addTypedHandler(new a1.TypedHandler("__typedsocket_setTag",async(a,r)=>{let s=r?.localData?.peer;return s?(s.tags.add(a.name),s.data.set(`${oqe}${a.name}`,a.payload),{success:!0}):(console.warn("setTag: No peer found in request context"),{success:!1})})),e.addTypedHandler(new a1.TypedHandler("__typedsocket_removeTag",async(a,r)=>{let s=r?.localData?.peer;return s?(s.tags.delete(a.name),s.data.delete(`${oqe}${a.name}`),{success:!0}):(console.warn("removeTag: No peer found in request context"),{success:!1})}))}constructor(e,a){this.statusSubject=new Wt.rxjs.Subject,this.connectionStatus="new",this.websocket=null,this.clientOptions=null,this.serverUrl="",this.retryCount=0,this.currentBackoff=1e3,this.pendingRequests=new Map,this.smartServeRef=null,this.smartServeConnectionWrappers=new Map,this.side=e,this.typedrouter=a}async connect(){let e=Je.defer();this.updateStatus("connecting");let a=this.toWebSocketUrl(this.serverUrl);console.log(`TypedSocket connecting to ${a}...`),this.websocket=new WebSocket(a);let r=setTimeout(()=>{this.connectionStatus!=="connected"&&(console.warn("TypedSocket connection timeout"),this.websocket?.close(),e.reject(new Error("Connection timeout")))},1e4);this.websocket.onopen=()=>{clearTimeout(r),console.log("TypedSocket connected!"),this.updateStatus("connected"),this.retryCount=0,this.currentBackoff=this.clientOptions?.initialBackoffMs??1e3,e.resolve()},this.websocket.onmessage=async s=>{await this.handleMessage(s.data)},this.websocket.onclose=()=>{clearTimeout(r),this.handleDisconnect()},this.websocket.onerror=s=>{console.error("TypedSocket WebSocket error:",s)};try{await e.promise}catch(s){if(clearTimeout(r),this.clientOptions?.autoReconnect)await this.scheduleReconnect();else throw s}}toWebSocketUrl(e){let a=new URL(e);return`${a.protocol==="https:"?"wss:":"ws:"}//${a.host}${a.pathname}`}async handleMessage(e){try{let a=typeof e=="string"?e:new TextDecoder().decode(e),r=ri.parse(a);if(r.correlation?.id&&this.pendingRequests.has(r.correlation.id)){let n=this.pendingRequests.get(r.correlation.id);this.pendingRequests.delete(r.correlation.id),n.resolve(r);return}let s=await this.typedrouter.routeAndAddResponse(r);s&&this.websocket?.readyState===WebSocket.OPEN&&this.websocket.send(ri.stringify(s))}catch(a){console.error("TypedSocket failed to process message:",a)}}handleDisconnect(){if(this.connectionStatus!=="disconnected"){this.updateStatus("disconnected");for(let[e,a]of this.pendingRequests)a.reject(new Error("TypedSocket disconnected"));this.pendingRequests.clear(),this.clientOptions?.autoReconnect&&this.retryCount<this.clientOptions.maxRetries&&this.scheduleReconnect()}}async scheduleReconnect(){if(!this.clientOptions)return;this.updateStatus("reconnecting"),this.retryCount++;let e=this.currentBackoff*.2*(Math.random()*2-1),a=Math.min(this.currentBackoff+e,this.clientOptions.maxBackoffMs);console.log(`TypedSocket reconnecting in ${Math.round(a)}ms (attempt ${this.retryCount}/${this.clientOptions.maxRetries})`),await St.delayFor(a),this.currentBackoff=Math.min(this.currentBackoff*2,this.clientOptions.maxBackoffMs);try{await this.connect()}catch(r){console.error("TypedSocket reconnection failed:",r)}}updateStatus(e){this.connectionStatus!==e&&(this.connectionStatus=e,this.statusSubject.next(e))}async sendRequest(e){if(!this.websocket||this.websocket.readyState!==WebSocket.OPEN)throw new Error("WebSocket not connected");return new Promise((a,r)=>{let s=setTimeout(()=>{this.pendingRequests.delete(e.correlation.id),r(new Error("Request timeout"))},3e4);this.pendingRequests.set(e.correlation.id,{resolve:i(n=>{clearTimeout(s),a(n)},"resolve"),reject:i(n=>{clearTimeout(s),r(n)},"reject")}),this.websocket.send(ri.stringify(e))})}createTypedRequest(e,a){let r=i(async s=>{if(this.side==="client")return this.sendRequest(s);if(!this.smartServeRef)throw new Error("Server not initialized");let n=a;if(!n){let l=this.smartServeRef.getWebSocketConnections();if(l.length===1){let h=l[0];n=this.getOrCreateWrapper(h)}else throw l.length===0?new Error("No WebSocket connections available"):new Error("Multiple connections available - specify targetConnection")}let o=await this.typedrouter.fireEventInterestMap.addInterest(s.correlation.id,s);return n.peer.send(ri.stringify(s)),await o.interestFullfilled},"postMethod");return new a1.TypedRequest(new a1.TypedTarget({postMethod:r}),e)}getStatus(){return this.connectionStatus}async stop(){this.side==="client"?(this.clientOptions&&(this.clientOptions.autoReconnect=!1),this.websocket&&(this.websocket.close(),this.websocket=null),this.pendingRequests.clear()):this.smartServeConnectionWrappers.clear()}async setTag(e,a){if(this.side!=="client")throw new Error("setTag is only available on clients");if(!(await this.createTypedRequest("__typedsocket_setTag").fire({name:e,payload:a})).success)throw new Error("Failed to set tag on server")}async removeTag(e){if(this.side!=="client")throw new Error("removeTag is only available on clients");if(!(await this.createTypedRequest("__typedsocket_removeTag").fire({name:e})).success)throw new Error("Failed to remove tag on server")}getOrCreateWrapper(e){let a=this.smartServeConnectionWrappers.get(e.id);return a||(a=Tva(e),this.smartServeConnectionWrappers.set(e.id,a)),a}async findAllTargetConnections(e){if(this.side!=="server"||!this.smartServeRef)throw new Error("findAllTargetConnections is only available on servers");let a=[];for(let r of this.smartServeRef.getWebSocketConnections()){let s=this.getOrCreateWrapper(r);await e(s)&&a.push(s)}return a}async findTargetConnection(e){return(await this.findAllTargetConnections(e))[0]}async findAllTargetConnectionsByTag(e,a){if(this.side!=="server"||!this.smartServeRef)throw new Error("findAllTargetConnectionsByTag is only available on servers");let r=this.smartServeRef.getWebSocketConnectionsByTag(e),s=[];for(let n of r){let o=this.getOrCreateWrapper(n);if(a!==void 0){let l=await o.getTagById(e);if(ri.stringify(l?.payload)!==ri.stringify(a))continue}s.push(o)}return s}async findTargetConnectionByTag(e,a){return(await this.findAllTargetConnectionsByTag(e,a))[0]}};de();Xa();var vt=gt;u5();var Jaa={};var e1a={};var Hs=new vt.plugins.smartstate.Smartstate,Ka=await Hs.getStatePart("login",{identity:null,isLoggedIn:!1},"persistent"),lr=await Hs.getStatePart("stats",{serverStats:null,emailStats:null,dnsStats:null,securityMetrics:null,lastUpdated:0,isLoading:!1,error:null},"soft"),H6=await Hs.getStatePart("config",{config:null,isLoading:!1,error:null}),Lva=i(()=>{let t=typeof window<"u"?window.location.pathname:"/",e=["overview","network","emails","logs","routes","apitokens","configuration","security","certificates","remoteingress"],r=t.split("/").filter(Boolean)[0];return e.includes(r)?r:"overview"},"getInitialView"),ci=await Hs.getStatePart("ui",{activeView:Lva(),sidebarCollapsed:!1,autoRefresh:!0,refreshInterval:1e3,theme:"light"}),jo=await Hs.getStatePart("logs",{recentLogs:[],isStreaming:!1,filters:{}},"soft"),$2=await Hs.getStatePart("network",{connections:[],connectionsByIP:{},throughputRate:{bytesInPerSecond:0,bytesOutPerSecond:0},totalBytes:{in:0,out:0},topIPs:[],throughputByIP:[],throughputHistory:[],requestsPerSecond:0,requestsTotal:0,backends:[],lastUpdated:0,isLoading:!1,error:null},"soft"),NX=await Hs.getStatePart("emailOps",{emails:[],isLoading:!1,error:null,lastUpdated:0},"soft"),di=await Hs.getStatePart("certificates",{certificates:[],summary:{total:0,valid:0,expiring:0,expired:0,failed:0,unknown:0},isLoading:!1,error:null,lastUpdated:0},"soft"),S1=await Hs.getStatePart("remoteIngress",{edges:[],statuses:[],selectedEdgeId:null,newEdgeId:null,isLoading:!1,error:null,lastUpdated:0},"soft"),pa=await Hs.getStatePart("routeManagement",{mergedRoutes:[],warnings:[],apiTokens:[],isLoading:!1,error:null,lastUpdated:0},"soft"),Jt=i(()=>{let t=Ka.getState().identity;return t&&t.expiresAt&&t.expiresAt<Date.now()?{identity:null}:{identity:t}},"getActionContext"),o1a=Ka.createAction(async(t,e)=>{let a=new vt.plugins.typedrequest.TypedRequest("/typedrequest","adminLoginWithUsernameAndPassword");try{let r=await a.fire({username:e.username,password:e.password});return r.identity?{identity:r.identity,isLoggedIn:!0}:t.getState()}catch(r){return console.error("Login failed:",r),t.getState()}}),wu=Ka.createAction(async t=>{let e=Jt();if(e.identity){let a=new vt.plugins.typedrequest.TypedRequest("/typedrequest","adminLogout");try{await a.fire({identity:e.identity})}catch(r){console.error("Logout error:",r)}}return{identity:null,isLoggedIn:!1}}),Mu=lr.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getCombinedMetrics").fire({identity:e.identity,sections:{server:!0,email:!0,dns:!0,security:!0,network:!1}});return{serverStats:s.metrics.server||a.serverStats,emailStats:s.metrics.email||a.emailStats,dnsStats:s.metrics.dns||a.dnsStats,securityMetrics:s.metrics.security||a.securityMetrics,lastUpdated:Date.now(),isLoading:!1,error:null}}catch(r){return{...a,isLoading:!1,error:r.message||"Failed to fetch statistics"}}}),IX=H6.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{return{config:(await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getConfiguration").fire({identity:e.identity})).config,isLoading:!1,error:null}}catch(r){return{...a,isLoading:!1,error:r.message||"Failed to fetch configuration"}}}),FX=jo.createAction(async(t,e)=>{let a=Jt();if(!a.identity)return t.getState();let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getRecentLogs").fire({identity:a.identity,limit:e.limit||100,level:e.level,category:e.category});return{...t.getState(),recentLogs:s.logs}}),Bji=ci.createAction(async t=>{let e=t.getState();return{...e,autoRefresh:!e.autoRefresh}}),Hji=ci.createAction(async(t,e)=>{let a=t.getState();return e==="network"&&a.activeView!=="network"&&setTimeout(()=>{$2.dispatchAction(pqe,null)},100),e==="certificates"&&a.activeView!=="certificates"&&setTimeout(()=>{di.dispatchAction(xl,null)},100),e==="routes"&&a.activeView!=="routes"&&setTimeout(()=>{pa.dispatchAction(D2,null)},100),e==="apitokens"&&a.activeView!=="apitokens"&&setTimeout(()=>{pa.dispatchAction(yl,null)},100),e==="remoteingress"&&a.activeView!=="remoteingress"&&setTimeout(()=>{S1.dispatchAction(vl,null)},100),{...a,activeView:e}}),pqe=$2.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getActiveConnections").fire({identity:e.identity}),o=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getNetworkStats").fire({identity:e.identity}),l={};return o.connectionsByIP&&Array.isArray(o.connectionsByIP)?o.connectionsByIP.forEach(h=>{l[h.ip]=h.count}):s.connections.forEach(h=>{let p=h.remoteAddress;l[p]=(l[p]||0)+1}),{connections:s.connections,connectionsByIP:l,throughputRate:o.throughputRate||{bytesInPerSecond:0,bytesOutPerSecond:0},totalBytes:o.totalDataTransferred?{in:o.totalDataTransferred.bytesIn,out:o.totalDataTransferred.bytesOut}:{in:0,out:0},topIPs:o.topIPs||[],throughputByIP:o.throughputByIP||[],throughputHistory:o.throughputHistory||[],requestsPerSecond:o.requestsPerSecond||0,requestsTotal:o.requestsTotal||0,backends:o.backends||[],lastUpdated:Date.now(),isLoading:!1,error:null}}catch(r){return console.error("Failed to fetch network stats:",r),{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch network stats"}}}),n1a=NX.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{return{emails:(await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getAllEmails").fire({identity:e.identity})).emails,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch emails"}}}),xl=di.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getCertificateOverview").fire({identity:e.identity});return{certificates:s.certificates,summary:s.summary,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch certificate overview"}}}),l1a=di.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","reprovisionCertificateDomain").fire({identity:r.identity,domain:e}),await a.dispatch(xl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to reprovision certificate"}}}),c1a=di.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","deleteCertificate").fire({identity:r.identity,domain:e}),await a.dispatch(xl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to delete certificate"}}}),d1a=di.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","importCertificate").fire({identity:r.identity,cert:e}),await a.dispatch(xl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to import certificate"}}});async function u1a(t){let e=Jt();return new vt.plugins.typedrequest.TypedRequest("/typedrequest","exportCertificate").fire({identity:e.identity,domain:t})}i(u1a,"fetchCertificateExport");async function fqe(t){let e=Jt();return new vt.plugins.typedrequest.TypedRequest("/typedrequest","getRemoteIngressConnectionToken").fire({identity:e.identity,edgeId:t})}i(fqe,"fetchConnectionToken");var vl=S1.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let r=new vt.plugins.typedrequest.TypedRequest("/typedrequest","getRemoteIngresses"),s=new vt.plugins.typedrequest.TypedRequest("/typedrequest","getRemoteIngressStatus"),[n,o]=await Promise.all([r.fire({identity:e.identity}),s.fire({identity:e.identity})]);return{...a,edges:n.edges,statuses:o.statuses,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch remote ingress data"}}}),p1a=S1.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{let o=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","createRemoteIngress").fire({identity:r.identity,name:e.name,listenPorts:e.listenPorts,autoDerivePorts:e.autoDerivePorts,tags:e.tags});return o.success?(await a.dispatch(vl,null),{...t.getState(),newEdgeId:o.edge.id}):s}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to create edge"}}}),f1a=S1.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","deleteRemoteIngress").fire({identity:r.identity,id:e}),await a.dispatch(vl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to delete edge"}}}),h1a=S1.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","updateRemoteIngress").fire({identity:r.identity,id:e.id,name:e.name,listenPorts:e.listenPorts,autoDerivePorts:e.autoDerivePorts,tags:e.tags}),await a.dispatch(vl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to update edge"}}}),m1a=S1.createAction(async(t,e)=>{let a=Jt(),r=t.getState();try{return(await new vt.plugins.typedrequest.TypedRequest("/typedrequest","regenerateRemoteIngressSecret").fire({identity:a.identity,id:e})).success?{...r,newEdgeId:e}:r}catch(s){return{...r,error:s instanceof Error?s.message:"Failed to regenerate secret"}}}),g1a=S1.createAction(async t=>({...t.getState(),newEdgeId:null})),hqe=S1.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","updateRemoteIngress").fire({identity:r.identity,id:e.id,enabled:e.enabled}),await a.dispatch(vl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to toggle edge"}}}),ui=await Hs.getStatePart("vpn",{clients:[],status:null,isLoading:!1,error:null,lastUpdated:0,newClientConfig:null},"soft"),yu=ui.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let r=new vt.plugins.typedrequest.TypedRequest("/typedrequest","getVpnClients"),s=new vt.plugins.typedrequest.TypedRequest("/typedrequest","getVpnStatus"),[n,o]=await Promise.all([r.fire({identity:e.identity}),s.fire({identity:e.identity})]);return{...a,clients:n.clients,status:o.status,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch VPN data"}}}),b1a=ui.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{let o=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","createVpnClient").fire({identity:r.identity,clientId:e.clientId,tags:e.tags,description:e.description});return o.success?{...await a.dispatch(yu,null),newClientConfig:o.wireguardConfig||null}:{...s,error:o.message||"Failed to create client"}}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to create VPN client"}}}),v1a=ui.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","deleteVpnClient").fire({identity:r.identity,clientId:e}),await a.dispatch(yu,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to delete VPN client"}}}),x1a=ui.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{let n=e.enabled?"enableVpnClient":"disableVpnClient";return await new vt.plugins.typedrequest.TypedRequest("/typedrequest",n).fire({identity:r.identity,clientId:e.clientId}),await a.dispatch(yu,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to toggle VPN client"}}}),y1a=ui.createAction(async t=>({...t.getState(),newClientConfig:null})),D2=pa.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getMergedRoutes").fire({identity:e.identity});return{...a,mergedRoutes:s.routes,warnings:s.warnings,isLoading:!1,error:null,lastUpdated:Date.now()}}catch(r){return{...a,isLoading:!1,error:r instanceof Error?r.message:"Failed to fetch routes"}}}),w1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","createRoute").fire({identity:r.identity,route:e.route,enabled:e.enabled}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to create route"}}}),M1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","deleteRoute").fire({identity:r.identity,id:e}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to delete route"}}}),k1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","toggleRoute").fire({identity:r.identity,id:e.id,enabled:e.enabled}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to toggle route"}}}),mqe=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","setRouteOverride").fire({identity:r.identity,routeName:e.routeName,enabled:e.enabled}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to set override"}}}),z1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","removeRouteOverride").fire({identity:r.identity,routeName:e}),await a.dispatch(D2,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to remove override"}}}),yl=pa.createAction(async t=>{let e=Jt(),a=t.getState();if(!e.identity)return a;try{let s=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","listApiTokens").fire({identity:e.identity});return{...a,apiTokens:s.tokens}}catch(r){return{...a,error:r instanceof Error?r.message:"Failed to fetch tokens"}}});async function S1a(t,e,a){let r=Jt();return new vt.plugins.typedrequest.TypedRequest("/typedrequest","createApiToken").fire({identity:r.identity,name:t,scopes:e,expiresInDays:a})}i(S1a,"createApiToken");async function C1a(t){let e=Jt();return new vt.plugins.typedrequest.TypedRequest("/typedrequest","rollApiToken").fire({identity:e.identity,id:t})}i(C1a,"rollApiToken");var _1a=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","revokeApiToken").fire({identity:r.identity,id:e}),await a.dispatch(yl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to revoke token"}}}),gqe=pa.createAction(async(t,e,a)=>{let r=Jt(),s=t.getState();try{return await new vt.plugins.typedrequest.TypedRequest("/typedrequest","toggleApiToken").fire({identity:r.identity,id:e.id,enabled:e.enabled}),await a.dispatch(yl,null)}catch(n){return{...s,error:n instanceof Error?n.message:"Failed to toggle token"}}}),O6=null,t1a=new vt.plugins.typedrequest.TypedRouter,EX=[],lqe=!1;function $va(){if(lqe=!1,EX.length===0)return;let t=jo.getState(),e=[...t.recentLogs,...EX];EX=[],e.length>2e3&&e.splice(0,e.length-2e3),jo.setState({...t,recentLogs:e})}i($va,"flushLogEntries"),t1a.addTypedHandler(new vt.plugins.typedrequest.TypedHandler("pushLogEntry",async t=>(EX.push(t.entry),lqe||(lqe=!0,requestAnimationFrame($va)),{})));async function cqe(){if(!O6)try{O6=await xu.TypedSocket.createClient(t1a,xu.TypedSocket.useWindowLocationOriginUrl()),await O6.setTag("role","ops_dashboard")}catch(t){console.error("TypedSocket connection failed:",t),O6=null}}i(cqe,"connectSocket");async function a1a(){if(O6){try{await O6.stop()}catch{}O6=null}}i(a1a,"disconnectSocket");var dqe=!1;async function Dva(){if(!dqe){dqe=!0;try{await Ava()}finally{dqe=!1}}}i(Dva,"dispatchCombinedRefreshAction");async function Ava(){let t=Jt();if(!t.identity)return;let e=ci.getState().activeView;try{let r=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getCombinedMetrics").fire({identity:t.identity,sections:{server:!0,email:!0,dns:!0,security:!0,network:e==="network"}}),s=lr.getState();if(lr.setState({...s,serverStats:r.metrics.server||s.serverStats,emailStats:r.metrics.email||s.emailStats,dnsStats:r.metrics.dns||s.dnsStats,securityMetrics:r.metrics.security||s.securityMetrics,lastUpdated:Date.now(),isLoading:!1,error:null}),r.metrics.network&&e==="network"){let n=r.metrics.network,o={};n.connectionDetails.forEach(l=>{o[l.remoteAddress]=(o[l.remoteAddress]||0)+1});try{let h=await new vt.plugins.typedrequest.TypedRequest("/typedrequest","getActiveConnections").fire({identity:t.identity});$2.setState({...$2.getState(),connections:h.connections,connectionsByIP:o,throughputRate:{bytesInPerSecond:n.totalBandwidth.in,bytesOutPerSecond:n.totalBandwidth.out},totalBytes:n.totalBytes||{in:0,out:0},topIPs:n.topEndpoints.map(p=>({ip:p.endpoint,count:p.requests})),throughputByIP:n.topEndpoints.map(p=>({ip:p.endpoint,in:p.bandwidth?.in||0,out:p.bandwidth?.out||0})),throughputHistory:n.throughputHistory||[],requestsPerSecond:n.requestsPerSecond||0,requestsTotal:n.requestsTotal||0,backends:n.backends||[],lastUpdated:Date.now(),isLoading:!1,error:null})}catch(l){console.error("Failed to fetch connections:",l),$2.setState({...$2.getState(),connections:[],connectionsByIP:o,throughputRate:{bytesInPerSecond:n.totalBandwidth.in,bytesOutPerSecond:n.totalBandwidth.out},totalBytes:n.totalBytes||{in:0,out:0},topIPs:n.topEndpoints.map(h=>({ip:h.endpoint,count:h.requests})),throughputByIP:n.topEndpoints.map(h=>({ip:h.endpoint,in:h.bandwidth?.in||0,out:h.bandwidth?.out||0})),throughputHistory:n.throughputHistory||[],requestsPerSecond:n.requestsPerSecond||0,requestsTotal:n.requestsTotal||0,backends:n.backends||[],lastUpdated:Date.now(),isLoading:!1,error:null})}}if(e==="certificates")try{await di.dispatchAction(xl,null)}catch(n){console.error("Certificate refresh failed:",n)}if(e==="remoteingress")try{await S1.dispatchAction(vl,null)}catch(n){console.error("Remote ingress refresh failed:",n)}if(e==="vpn")try{await ui.dispatchAction(yu,null)}catch(n){console.error("VPN refresh failed:",n)}}catch(a){console.error("Combined refresh failed:",a);let r=String(a);(r.includes("invalid")||r.includes("unauthorized")||r.includes("401"))&&(await Ka.dispatchAction(wu,null),window.location.reload())}}i(Ava,"dispatchCombinedRefreshActionInner");var Pva=lr.createAction(async t=>(await Dva(),t.getState())),B6=null,uqe=i(()=>{let t=ci.getState(),e=Ka.getState();t.autoRefresh&&e.isLoggedIn?(B6&&(B6.dispose(),B6=null),B6=lr.createScheduledAction({action:Pva,payload:void 0,intervalMs:t.refreshInterval,autoPause:"visibility"})):B6&&(B6.dispose(),B6=null)},"startAutoRefresh"),r1a=ci.getState().autoRefresh,i1a=ci.getState().refreshInterval,s1a=Ka.getState().isLoggedIn;ci.select(t=>({autoRefresh:t.autoRefresh,refreshInterval:t.refreshInterval})).subscribe(t=>{(t.autoRefresh!==r1a||t.refreshInterval!==i1a)&&(r1a=t.autoRefresh,i1a=t.refreshInterval,uqe())}),Ka.select(t=>t.isLoggedIn).subscribe(t=>{t!==s1a&&(s1a=t,uqe(),t?cqe():a1a())}),document.addEventListener("visibilitychange",()=>{document.hidden?a1a():Ka.getState().isLoggedIn&&cqe()}),uqe(),Ka.getState().isLoggedIn&&cqe();var Eva=vt.plugins.smartrouter.SmartRouter,bqe=["overview","network","emails","logs","routes","apitokens","configuration","security","certificates","remoteingress","vpn"],vqe=class{static{i(this,"AppRouter")}router;initialized=!1;suppressStateUpdate=!1;constructor(){this.router=new Eva({debug:!1})}init(){this.initialized||(this.setupRoutes(),this.setupStateSync(),this.handleInitialRoute(),this.initialized=!0)}setupRoutes(){for(let e of bqe)this.router.on(`/${e}`,async()=>{this.updateViewState(e)});this.router.on("/",async()=>{this.navigateTo("/overview")})}setupStateSync(){ci.select().subscribe(e=>{if(this.suppressStateUpdate)return;let a=window.location.pathname,r=`/${e.activeView}`;a!==r&&(this.suppressStateUpdate=!0,this.router.pushUrl(r),this.suppressStateUpdate=!1)})}handleInitialRoute(){let e=window.location.pathname;if(!e||e==="/")this.router.pushUrl("/overview");else{let r=e.split("/").filter(Boolean)[0];bqe.includes(r)?this.updateViewState(r):this.router.pushUrl("/overview")}}updateViewState(e){this.suppressStateUpdate=!0;let a=ci.getState();a.activeView!==e&&ci.setState({...a,activeView:e}),this.suppressStateUpdate=!1}navigateTo(e){this.router.pushUrl(e)}navigateToView(e){bqe.includes(e)?this.navigateTo(`/${e}`):this.navigateTo("/overview")}getCurrentView(){return ci.getState().activeView}destroy(){this.router.destroy(),this.initialized=!1}},ku=new vqe;de();var n1=Z`
|
|
40178
40178
|
:host {
|
|
40179
40179
|
display: block;
|
|
40180
40180
|
margin: auto;
|
|
@@ -43364,4 +43364,4 @@ ibantools/jsnext/ibantools.js:
|
|
|
43364
43364
|
* @preferred
|
|
43365
43365
|
*)
|
|
43366
43366
|
*/
|
|
43367
|
-
//# sourceMappingURL=bundle-
|
|
43367
|
+
//# sourceMappingURL=bundle-1774859083330.js.map
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**dcrouter: The all-in-one gateway for your datacenter.** 🚀
|
|
6
6
|
|
|
7
|
-
A comprehensive traffic routing solution that provides unified gateway capabilities for HTTP/HTTPS, TCP/SNI, email (SMTP), DNS, RADIUS, and remote edge ingress — all from a single process. Designed for enterprises requiring robust traffic management, automatic TLS certificate provisioning, distributed edge networking, and enterprise-grade email infrastructure.
|
|
7
|
+
A comprehensive traffic routing solution that provides unified gateway capabilities for HTTP/HTTPS, TCP/SNI, email (SMTP), DNS, RADIUS, VPN, and remote edge ingress — all from a single process. Designed for enterprises requiring robust traffic management, automatic TLS certificate provisioning, VPN-based access control, distributed edge networking, and enterprise-grade email infrastructure.
|
|
8
8
|
|
|
9
9
|
## Issue Reporting and Security
|
|
10
10
|
|
|
@@ -23,6 +23,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
23
23
|
- [DNS Server](#dns-server)
|
|
24
24
|
- [RADIUS Server](#radius-server)
|
|
25
25
|
- [Remote Ingress](#remote-ingress)
|
|
26
|
+
- [VPN Access Control](#vpn-access-control)
|
|
26
27
|
- [Certificate Management](#certificate-management)
|
|
27
28
|
- [Storage & Caching](#storage--caching)
|
|
28
29
|
- [Security Features](#security-features)
|
|
@@ -73,6 +74,14 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
73
74
|
- **Real-time status monitoring** — connected/disconnected state, public IP, active tunnels, heartbeat tracking
|
|
74
75
|
- **OpsServer dashboard** with enable/disable, edit, secret regeneration, token copy, and delete actions
|
|
75
76
|
|
|
77
|
+
### 🔐 VPN Access Control (powered by [smartvpn](https://code.foss.global/push.rocks/smartvpn))
|
|
78
|
+
- **WireGuard + native transports** — standard WireGuard clients (iOS, Android, macOS, Windows, Linux) plus custom WebSocket/QUIC tunnels
|
|
79
|
+
- **Route-level VPN gating** — mark any route with `vpn: { required: true }` to restrict access to VPN clients only
|
|
80
|
+
- **Rootless operation** — auto-detects privileges: kernel TUN when running as root, userspace NAT (smoltcp) when not
|
|
81
|
+
- **Client management** — create, enable, disable, rotate keys, export WireGuard `.conf` files via OpsServer API
|
|
82
|
+
- **IP-based enforcement** — VPN clients get IPs from a configurable subnet; SmartProxy enforces `ipAllowList` per route
|
|
83
|
+
- **PROXY protocol v2** — in socket mode, the NAT engine sends PP v2 on outbound connections to preserve VPN client identity
|
|
84
|
+
|
|
76
85
|
### ⚡ High Performance
|
|
77
86
|
- **Rust-powered proxy engine** via SmartProxy for maximum throughput
|
|
78
87
|
- **Rust-powered MTA engine** via smartmta (TypeScript + Rust hybrid) for reliable email delivery
|
|
@@ -89,7 +98,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
89
98
|
### 🖥️ OpsServer Dashboard
|
|
90
99
|
- **Web-based management interface** with real-time monitoring
|
|
91
100
|
- **JWT authentication** with session persistence
|
|
92
|
-
- **Live views** for connections, email queues, DNS queries, RADIUS sessions, certificates, remote ingress edges, and security events
|
|
101
|
+
- **Live views** for connections, email queues, DNS queries, RADIUS sessions, certificates, remote ingress edges, VPN clients, and security events
|
|
93
102
|
- **Domain-centric certificate overview** with backoff status and one-click reprovisioning
|
|
94
103
|
- **Remote ingress management** with connection token generation and one-click copy
|
|
95
104
|
- **Read-only configuration display** — DcRouter is configured through code
|
|
@@ -248,6 +257,13 @@ const router = new DcRouter({
|
|
|
248
257
|
hubDomain: 'hub.example.com',
|
|
249
258
|
},
|
|
250
259
|
|
|
260
|
+
// VPN — restrict sensitive routes to VPN clients
|
|
261
|
+
vpnConfig: {
|
|
262
|
+
enabled: true,
|
|
263
|
+
serverEndpoint: 'vpn.example.com',
|
|
264
|
+
wgListenPort: 51820,
|
|
265
|
+
},
|
|
266
|
+
|
|
251
267
|
// Persistent storage
|
|
252
268
|
storage: { fsPath: '/var/lib/dcrouter/data' },
|
|
253
269
|
|
|
@@ -276,6 +292,7 @@ graph TB
|
|
|
276
292
|
DNS[DNS Queries]
|
|
277
293
|
RAD[RADIUS Clients]
|
|
278
294
|
EDGE[Edge Nodes]
|
|
295
|
+
VPN[VPN Clients]
|
|
279
296
|
end
|
|
280
297
|
|
|
281
298
|
subgraph "DcRouter Core"
|
|
@@ -285,6 +302,7 @@ graph TB
|
|
|
285
302
|
DS[SmartDNS Server<br/><i>Rust-powered</i>]
|
|
286
303
|
RS[SmartRadius Server]
|
|
287
304
|
RI[RemoteIngress Hub<br/><i>Rust data plane</i>]
|
|
305
|
+
VS[SmartVPN Server<br/><i>Rust data plane</i>]
|
|
288
306
|
CM[Certificate Manager<br/><i>smartacme v9</i>]
|
|
289
307
|
OS[OpsServer Dashboard]
|
|
290
308
|
MM[Metrics Manager]
|
|
@@ -305,12 +323,14 @@ graph TB
|
|
|
305
323
|
DNS --> DS
|
|
306
324
|
RAD --> RS
|
|
307
325
|
EDGE --> RI
|
|
326
|
+
VPN --> VS
|
|
308
327
|
|
|
309
328
|
DC --> SP
|
|
310
329
|
DC --> ES
|
|
311
330
|
DC --> DS
|
|
312
331
|
DC --> RS
|
|
313
332
|
DC --> RI
|
|
333
|
+
DC --> VS
|
|
314
334
|
DC --> CM
|
|
315
335
|
DC --> OS
|
|
316
336
|
DC --> MM
|
|
@@ -428,6 +448,17 @@ interface IDcRouterOptions {
|
|
|
428
448
|
};
|
|
429
449
|
};
|
|
430
450
|
|
|
451
|
+
// ── VPN ───────────────────────────────────────────────────────
|
|
452
|
+
/** VPN server for route-level access control */
|
|
453
|
+
vpnConfig?: {
|
|
454
|
+
enabled?: boolean; // default: false
|
|
455
|
+
subnet?: string; // default: '10.8.0.0/24'
|
|
456
|
+
wgListenPort?: number; // default: 51820
|
|
457
|
+
dns?: string[]; // DNS servers pushed to VPN clients
|
|
458
|
+
serverEndpoint?: string; // Hostname in generated client configs
|
|
459
|
+
forwardingMode?: 'tun' | 'socket'; // default: auto-detect (root → tun, else socket)
|
|
460
|
+
};
|
|
461
|
+
|
|
431
462
|
// ── HTTP/3 (QUIC) ────────────────────────────────────────────
|
|
432
463
|
/** HTTP/3 config — enabled by default on qualifying HTTPS routes */
|
|
433
464
|
http3?: {
|
|
@@ -975,6 +1006,78 @@ The OpsServer Remote Ingress view provides:
|
|
|
975
1006
|
| **Copy Token** | Generate and copy a base64url connection token to clipboard |
|
|
976
1007
|
| **Delete** | Remove the edge registration |
|
|
977
1008
|
|
|
1009
|
+
## VPN Access Control
|
|
1010
|
+
|
|
1011
|
+
DcRouter integrates [`@push.rocks/smartvpn`](https://code.foss.global/push.rocks/smartvpn) to provide VPN-based route access control. VPN clients connect via standard WireGuard or native WebSocket/QUIC transports, receive an IP from a configurable subnet, and can then access routes that are restricted to VPN-only traffic.
|
|
1012
|
+
|
|
1013
|
+
### How It Works
|
|
1014
|
+
|
|
1015
|
+
1. **SmartVPN daemon** runs inside dcrouter with a Rust data plane (WireGuard via `boringtun`, custom protocol via Noise IK)
|
|
1016
|
+
2. Clients connect and get assigned an IP from the VPN subnet (e.g. `10.8.0.0/24`)
|
|
1017
|
+
3. Routes with `vpn: { required: true }` get `security.ipAllowList` automatically injected with the VPN subnet
|
|
1018
|
+
4. SmartProxy enforces the allowlist — only VPN-sourced traffic is accepted on those routes
|
|
1019
|
+
|
|
1020
|
+
### Two Operating Modes
|
|
1021
|
+
|
|
1022
|
+
| Mode | Root Required? | How It Works |
|
|
1023
|
+
|------|---------------|-------------|
|
|
1024
|
+
| **TUN** (`forwardingMode: 'tun'`) | Yes | Kernel TUN device — VPN traffic enters the network stack with real VPN IPs |
|
|
1025
|
+
| **Socket** (`forwardingMode: 'socket'`) | No | Userspace NAT via smoltcp — outbound connections send PROXY protocol v2 to preserve VPN client IPs |
|
|
1026
|
+
|
|
1027
|
+
DcRouter auto-detects: if running as root, it uses TUN mode; otherwise, it falls back to socket mode. You can override this with the `forwardingMode` option.
|
|
1028
|
+
|
|
1029
|
+
### Configuration
|
|
1030
|
+
|
|
1031
|
+
```typescript
|
|
1032
|
+
const router = new DcRouter({
|
|
1033
|
+
vpnConfig: {
|
|
1034
|
+
enabled: true,
|
|
1035
|
+
subnet: '10.8.0.0/24', // VPN client IP pool (default)
|
|
1036
|
+
wgListenPort: 51820, // WireGuard UDP port (default)
|
|
1037
|
+
serverEndpoint: 'vpn.example.com', // Hostname in generated client configs
|
|
1038
|
+
dns: ['1.1.1.1', '8.8.8.8'], // DNS servers pushed to clients
|
|
1039
|
+
// forwardingMode: 'socket', // Override auto-detection
|
|
1040
|
+
},
|
|
1041
|
+
smartProxyConfig: {
|
|
1042
|
+
routes: [
|
|
1043
|
+
// This route is VPN-only — non-VPN clients are blocked
|
|
1044
|
+
{
|
|
1045
|
+
name: 'admin-panel',
|
|
1046
|
+
match: { domains: ['admin.example.com'], ports: [443] },
|
|
1047
|
+
action: {
|
|
1048
|
+
type: 'forward',
|
|
1049
|
+
targets: [{ host: '192.168.1.50', port: 8080 }],
|
|
1050
|
+
tls: { mode: 'terminate', certificate: 'auto' },
|
|
1051
|
+
},
|
|
1052
|
+
vpn: { required: true }, // 🔐 Only VPN clients can access this
|
|
1053
|
+
},
|
|
1054
|
+
// This route is public — anyone can access it
|
|
1055
|
+
{
|
|
1056
|
+
name: 'public-site',
|
|
1057
|
+
match: { domains: ['example.com'], ports: [443] },
|
|
1058
|
+
action: {
|
|
1059
|
+
type: 'forward',
|
|
1060
|
+
targets: [{ host: '192.168.1.10', port: 80 }],
|
|
1061
|
+
tls: { mode: 'terminate', certificate: 'auto' },
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
],
|
|
1065
|
+
},
|
|
1066
|
+
});
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
### Client Management via OpsServer API
|
|
1070
|
+
|
|
1071
|
+
Once the VPN server is running, you can manage clients through the OpsServer dashboard or API:
|
|
1072
|
+
|
|
1073
|
+
- **Create client** — generates WireGuard keypairs, assigns IP, returns a ready-to-use `.conf` file
|
|
1074
|
+
- **Enable / Disable** — toggle client access without deleting
|
|
1075
|
+
- **Rotate keys** — generate fresh keypairs (invalidates old ones)
|
|
1076
|
+
- **Export config** — re-export in WireGuard or SmartVPN format
|
|
1077
|
+
- **Telemetry** — per-client bytes sent/received, keepalives, rate limiting
|
|
1078
|
+
|
|
1079
|
+
Standard WireGuard clients on any platform (iOS, Android, macOS, Windows, Linux) can connect using the generated `.conf` file or QR code — no custom VPN software needed.
|
|
1080
|
+
|
|
978
1081
|
## Certificate Management
|
|
979
1082
|
|
|
980
1083
|
DcRouter uses [`@push.rocks/smartacme`](https://code.foss.global/push.rocks/smartacme) v9 for ACME certificate provisioning. smartacme v9 brings significant improvements over previous versions:
|
|
@@ -1458,6 +1561,7 @@ The container exposes all service ports:
|
|
|
1458
1561
|
| 1812, 1813 | UDP | RADIUS auth/acct |
|
|
1459
1562
|
| 3000 | TCP | OpsServer dashboard |
|
|
1460
1563
|
| 8443 | TCP | Remote ingress tunnels |
|
|
1564
|
+
| 51820 | UDP | WireGuard VPN |
|
|
1461
1565
|
| 29000–30000 | TCP | Dynamic port range |
|
|
1462
1566
|
|
|
1463
1567
|
### Building the Image
|
|
@@ -1471,7 +1575,7 @@ The Docker build supports multi-platform (`linux/amd64`, `linux/arm64`) via [tsd
|
|
|
1471
1575
|
|
|
1472
1576
|
## License and Legal Information
|
|
1473
1577
|
|
|
1474
|
-
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [
|
|
1578
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
|
|
1475
1579
|
|
|
1476
1580
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
1477
1581
|
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts_web/readme.md
CHANGED
|
@@ -50,6 +50,13 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
50
50
|
- **Connection token generation** — one-click "Copy Token" for easy edge provisioning
|
|
51
51
|
- Enable/disable, edit, secret regeneration, and delete actions
|
|
52
52
|
|
|
53
|
+
### 🔐 VPN Management
|
|
54
|
+
- VPN server status with forwarding mode, subnet, and WireGuard port
|
|
55
|
+
- Client registration table with create, enable/disable, and delete actions
|
|
56
|
+
- WireGuard config download and clipboard copy on client creation
|
|
57
|
+
- Per-client telemetry (bytes sent/received, keepalives)
|
|
58
|
+
- Server public key display for manual client configuration
|
|
59
|
+
|
|
53
60
|
### 📜 Log Viewer
|
|
54
61
|
- Real-time log streaming
|
|
55
62
|
- Filter by log level (error, warning, info, debug)
|
|
@@ -100,6 +107,7 @@ ts_web/
|
|
|
100
107
|
├── ops-view-emails.ts # Email queue management
|
|
101
108
|
├── ops-view-certificates.ts # Certificate overview & reprovisioning
|
|
102
109
|
├── ops-view-remoteingress.ts # Remote ingress edge management
|
|
110
|
+
├── ops-view-vpn.ts # VPN client management
|
|
103
111
|
├── ops-view-logs.ts # Log viewer
|
|
104
112
|
├── ops-view-routes.ts # Route & API token management
|
|
105
113
|
├── ops-view-config.ts # Configuration display
|
|
@@ -124,6 +132,7 @@ The app uses `@push.rocks/smartstate` v2.3+ with multiple state parts, scheduled
|
|
|
124
132
|
| `emailOpsStatePart` | Soft | Email queues, bounces, suppression list |
|
|
125
133
|
| `certificateStatePart` | Soft | Certificate list, summary, loading state |
|
|
126
134
|
| `remoteIngressStatePart` | Soft | Edge list, statuses, new edge secret |
|
|
135
|
+
| `vpnStatePart` | Soft | VPN clients, server status, new client config |
|
|
127
136
|
|
|
128
137
|
### Tab Visibility Optimization
|
|
129
138
|
|
|
@@ -173,6 +182,13 @@ regenerateRemoteIngressSecretAction(id) // New secret
|
|
|
173
182
|
toggleRemoteIngressAction(id, enabled) // Enable/disable
|
|
174
183
|
clearNewEdgeSecretAction() // Dismiss secret banner
|
|
175
184
|
fetchConnectionToken(edgeId) // Get connection token (standalone function)
|
|
185
|
+
|
|
186
|
+
// VPN
|
|
187
|
+
fetchVpnAction() // Clients + server status
|
|
188
|
+
createVpnClientAction(data) // Create new VPN client
|
|
189
|
+
deleteVpnClientAction(clientId) // Remove VPN client
|
|
190
|
+
toggleVpnClientAction(id, enabled) // Enable/disable
|
|
191
|
+
clearNewClientConfigAction() // Dismiss config banner
|
|
176
192
|
```
|
|
177
193
|
|
|
178
194
|
### Client-Side Routing
|
|
@@ -187,6 +203,7 @@ fetchConnectionToken(edgeId) // Get connection token (standalone function)
|
|
|
187
203
|
/emails/security → Security incidents
|
|
188
204
|
/certificates → Certificate management
|
|
189
205
|
/remoteingress → Remote ingress edge management
|
|
206
|
+
/vpn → VPN client management
|
|
190
207
|
/routes → Route & API token management
|
|
191
208
|
/logs → Log viewer
|
|
192
209
|
/configuration → System configuration
|
package/ts_web/router.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as appstate from './appstate.js';
|
|
|
3
3
|
|
|
4
4
|
const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter;
|
|
5
5
|
|
|
6
|
-
export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress'] as const;
|
|
6
|
+
export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'vpn'] as const;
|
|
7
7
|
|
|
8
8
|
export type TValidView = typeof validViews[number];
|
|
9
9
|
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* autocreated commitinfo by @push.rocks/commitinfo
|
|
3
|
-
*/
|
|
4
|
-
export const commitinfo = {
|
|
5
|
-
name: '@serve.zone/dcrouter',
|
|
6
|
-
version: '11.13.0',
|
|
7
|
-
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLDBFQUEwRTtDQUN4RixDQUFBIn0=
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { CacheDb } from './classes.cachedb.js';
|
|
2
|
-
/**
|
|
3
|
-
* Configuration for the cache cleaner
|
|
4
|
-
*/
|
|
5
|
-
export interface ICacheCleanerOptions {
|
|
6
|
-
/** Cleanup interval in milliseconds (default: 1 hour) */
|
|
7
|
-
intervalMs?: number;
|
|
8
|
-
/** Enable verbose logging */
|
|
9
|
-
verbose?: boolean;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* CacheCleaner - Periodically removes expired documents from the cache
|
|
13
|
-
*
|
|
14
|
-
* Runs on a configurable interval (default: hourly) and queries each
|
|
15
|
-
* collection for documents where expiresAt < now(), then deletes them.
|
|
16
|
-
*/
|
|
17
|
-
export declare class CacheCleaner {
|
|
18
|
-
private cleanupInterval;
|
|
19
|
-
private isRunning;
|
|
20
|
-
private options;
|
|
21
|
-
private cacheDb;
|
|
22
|
-
constructor(cacheDb: CacheDb, options?: ICacheCleanerOptions);
|
|
23
|
-
/**
|
|
24
|
-
* Start the periodic cleanup process
|
|
25
|
-
*/
|
|
26
|
-
start(): void;
|
|
27
|
-
/**
|
|
28
|
-
* Stop the periodic cleanup process
|
|
29
|
-
*/
|
|
30
|
-
stop(): void;
|
|
31
|
-
/**
|
|
32
|
-
* Run a single cleanup cycle
|
|
33
|
-
*/
|
|
34
|
-
runCleanup(): Promise<void>;
|
|
35
|
-
/**
|
|
36
|
-
* Clean expired documents from a specific collection using smartdata API
|
|
37
|
-
*/
|
|
38
|
-
private cleanExpiredDocuments;
|
|
39
|
-
/**
|
|
40
|
-
* Check if the cleaner is running
|
|
41
|
-
*/
|
|
42
|
-
isActive(): boolean;
|
|
43
|
-
/**
|
|
44
|
-
* Get the cleanup interval in milliseconds
|
|
45
|
-
*/
|
|
46
|
-
getIntervalMs(): number;
|
|
47
|
-
}
|