Compare commits

..
17 Commits
Author SHA1 Message Date
tben ca8acb0c0c Updates 2026-03-14 13:33:35 +01:00
tben 18bda1653c Updates 2026-03-14 11:45:39 +01:00
tben 53fbcd96dc Updates 2026-03-14 11:42:47 +01:00
tben 40f2577a8a Updates 2026-03-14 11:38:37 +01:00
tben f0cfd197a2 Updates 2026-03-14 11:27:34 +01:00
tben be78ce3bc3 Updates 2026-03-14 11:18:49 +01:00
tben a7bef6de2a Updates 2026-03-14 09:03:01 +01:00
tben c5bbe1361a Updates 2026-03-14 08:49:12 +01:00
tben 971695c6c2 Updates 2026-03-14 07:32:29 +01:00
tben cec584c177 Updates 2026-03-14 07:19:46 +01:00
tben 89975d96cb Updates 2026-03-12 20:32:03 +01:00
tben cd78596d2a Updates 2026-03-12 17:35:16 +01:00
tben ded38b731b Updates 2026-03-12 16:24:16 +00:00
tben 5af4e6c2ad Updates 2026-03-12 16:02:55 +01:00
tben 673a1ca024 Updates 2026-03-12 15:01:07 +00:00
tben 25f966250c Updates 2026-03-12 15:41:11 +01:00
tben b4aa34a109 Updates 2026-03-12 15:18:27 +01:00
106 changed files with 1157 additions and 411 deletions
+1 -1
View File
@@ -1 +1 @@
d1kxBLz09K-KfpyaxJE_y
LKG2ddR8D3SPWi2345mPf
+28 -16
View File
@@ -5,8 +5,8 @@
"devFiles": [],
"ampDevFiles": [],
"lowPriorityFiles": [
"static/d1kxBLz09K-KfpyaxJE_y/_buildManifest.js",
"static/d1kxBLz09K-KfpyaxJE_y/_ssgManifest.js"
"static/LKG2ddR8D3SPWi2345mPf/_buildManifest.js",
"static/LKG2ddR8D3SPWi2345mPf/_ssgManifest.js"
],
"rootMainFiles": [],
"pages": {
@@ -36,7 +36,7 @@
"static/chunks/framework-bfa817c72d812316.js",
"static/chunks/main-761b7faf59ba862f.js",
"static/css/08e6997802749ec3.css",
"static/chunks/pages/_app-0610d36404ed5f1e.js"
"static/chunks/pages/_app-12c3cc69366c709d.js"
],
"/_error": [
"static/chunks/webpack-38cee4c0e358b1a3.js",
@@ -54,9 +54,9 @@
"static/chunks/433-bb8bdacebff9ab53.js",
"static/chunks/7-f0121001f735b8fe.js",
"static/chunks/827-7975edcf8481ec69.js",
"static/chunks/910-215ca9612c9e2400.js",
"static/chunks/464-eda76a320462e5de.js",
"static/chunks/pages/admin-c975090d9a91c7e1.js"
"static/chunks/910-7de3855073c34589.js",
"static/chunks/926-c50746382fde484e.js",
"static/chunks/pages/admin-914586c03dfda5c1.js"
],
"/admin/services": [
"static/chunks/webpack-38cee4c0e358b1a3.js",
@@ -68,9 +68,9 @@
"static/chunks/433-bb8bdacebff9ab53.js",
"static/chunks/7-f0121001f735b8fe.js",
"static/chunks/827-7975edcf8481ec69.js",
"static/chunks/910-215ca9612c9e2400.js",
"static/chunks/464-eda76a320462e5de.js",
"static/chunks/pages/admin/services-68efb2b49e62c913.js"
"static/chunks/910-7de3855073c34589.js",
"static/chunks/926-c50746382fde484e.js",
"static/chunks/pages/admin/services-16971626ad193c95.js"
],
"/admin/services/[service_name]": [
"static/chunks/webpack-38cee4c0e358b1a3.js",
@@ -82,9 +82,9 @@
"static/chunks/433-bb8bdacebff9ab53.js",
"static/chunks/7-f0121001f735b8fe.js",
"static/chunks/827-7975edcf8481ec69.js",
"static/chunks/910-215ca9612c9e2400.js",
"static/chunks/464-eda76a320462e5de.js",
"static/chunks/pages/admin/services/[service_name]-0049c2898ea64cc0.js"
"static/chunks/910-7de3855073c34589.js",
"static/chunks/926-c50746382fde484e.js",
"static/chunks/pages/admin/services/[service_name]-e8b08e5c11b1b12c.js"
],
"/admin/settings": [
"static/chunks/webpack-38cee4c0e358b1a3.js",
@@ -96,9 +96,21 @@
"static/chunks/433-bb8bdacebff9ab53.js",
"static/chunks/7-f0121001f735b8fe.js",
"static/chunks/827-7975edcf8481ec69.js",
"static/chunks/910-215ca9612c9e2400.js",
"static/chunks/910-7de3855073c34589.js",
"static/chunks/pages/admin/settings-f58d63b2752f72a6.js"
],
"/admin/shell": [
"static/chunks/webpack-38cee4c0e358b1a3.js",
"static/chunks/framework-bfa817c72d812316.js",
"static/chunks/main-761b7faf59ba862f.js",
"static/chunks/29107295-2819ba6e51dbf961.js",
"static/chunks/e5d67320-bf1772fa71739690.js",
"static/chunks/664-e347d64e56042a02.js",
"static/chunks/433-bb8bdacebff9ab53.js",
"static/chunks/7-f0121001f735b8fe.js",
"static/chunks/910-7de3855073c34589.js",
"static/chunks/pages/admin/shell-a1da21d85fd3c30b.js"
],
"/admin/users": [
"static/chunks/webpack-38cee4c0e358b1a3.js",
"static/chunks/framework-bfa817c72d812316.js",
@@ -108,7 +120,7 @@
"static/chunks/664-e347d64e56042a02.js",
"static/chunks/433-bb8bdacebff9ab53.js",
"static/chunks/7-f0121001f735b8fe.js",
"static/chunks/910-215ca9612c9e2400.js",
"static/chunks/910-7de3855073c34589.js",
"static/chunks/pages/admin/users-73e9775838b56d95.js"
],
"/admin/users/[deployment_user_id]": [
@@ -124,7 +136,7 @@
"static/chunks/281-dcee0c6401936d5e.js",
"static/chunks/7-f0121001f735b8fe.js",
"static/chunks/827-7975edcf8481ec69.js",
"static/chunks/910-215ca9612c9e2400.js",
"static/chunks/910-7de3855073c34589.js",
"static/chunks/pages/admin/users/[deployment_user_id]-fb1c3c9148c518cd.js"
],
"/admin/users/add-user": [
@@ -137,7 +149,7 @@
"static/chunks/433-bb8bdacebff9ab53.js",
"static/chunks/7-f0121001f735b8fe.js",
"static/chunks/827-7975edcf8481ec69.js",
"static/chunks/910-215ca9612c9e2400.js",
"static/chunks/910-7de3855073c34589.js",
"static/chunks/pages/admin/users/add-user-1db7c9ee89830b5b.js"
],
"/auth/login": [
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
{"version":1,"files":["../../node_modules/styled-jsx/index.js","../../node_modules/styled-jsx/package.json","../../node_modules/styled-jsx/dist/index/index.js","../../node_modules/react/package.json","../../node_modules/react/index.js","../../node_modules/client-only/package.json","../../node_modules/react/cjs/react.production.js","../../node_modules/client-only/index.js","../../node_modules/styled-jsx/style.js","../../node_modules/next/dist/compiled/next-server/server.runtime.prod.js","../../node_modules/next/package.json","../../node_modules/next/dist/server/body-streams.js","../../node_modules/next/dist/lib/constants.js","../../node_modules/next/dist/lib/picocolors.js","../../node_modules/next/dist/shared/lib/constants.js","../../node_modules/next/dist/server/web/utils.js","../../node_modules/next/dist/client/components/app-router-headers.js","../../node_modules/next/dist/server/lib/trace/constants.js","../../node_modules/next/dist/server/lib/trace/tracer.js","../../node_modules/next/dist/shared/lib/modern-browserslist-target.js","../../node_modules/next/dist/shared/lib/runtime-config.external.js","../../node_modules/next/dist/compiled/lru-cache/package.json","../../node_modules/next/dist/compiled/ws/package.json","../../node_modules/next/dist/compiled/node-html-parser/package.json","../../node_modules/@swc/helpers/_/_interop_require_default/package.json","../../node_modules/next/dist/compiled/lru-cache/index.js","../../node_modules/next/dist/compiled/ws/index.js","../../node_modules/next/dist/compiled/node-html-parser/index.js","../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../node_modules/@swc/helpers/package.json","../../node_modules/next/dist/client/components/async-local-storage.js","../../node_modules/next/dist/shared/lib/error-source.js","../../node_modules/next/dist/compiled/jsonwebtoken/package.json","../../node_modules/@swc/helpers/cjs/_interop_require_default.cjs","../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../node_modules/next/dist/compiled/jsonwebtoken/index.js","../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../node_modules/next/dist/compiled/debug/package.json","../../node_modules/next/dist/lib/semver-noop.js","../../node_modules/next/dist/compiled/debug/index.js","../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/amp-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/app-router-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/entrypoints.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/head-manager-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/hooks-client-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/html-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/image-config-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/router-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/server-inserted-html.js","../../node_modules/next/dist/server/future/route-modules/app-page/module.compiled.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/amp-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/app-router-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/entrypoints.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/head-manager-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/hooks-client-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/html-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/image-config-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/router-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/server-inserted-html.js","../../node_modules/next/dist/server/future/route-modules/pages/module.compiled.js"]}
{"version":1,"files":["../../node_modules/styled-jsx/index.js","../../node_modules/styled-jsx/package.json","../../node_modules/styled-jsx/dist/index/index.js","../../node_modules/react/package.json","../../node_modules/react/index.js","../../node_modules/client-only/package.json","../../node_modules/react/cjs/react.production.js","../../node_modules/client-only/index.js","../../node_modules/styled-jsx/style.js","../../node_modules/next/dist/compiled/next-server/server.runtime.prod.js","../../node_modules/next/package.json","../../node_modules/next/dist/server/body-streams.js","../../node_modules/next/dist/lib/constants.js","../../node_modules/next/dist/lib/picocolors.js","../../node_modules/next/dist/shared/lib/constants.js","../../node_modules/next/dist/server/web/utils.js","../../node_modules/next/dist/client/components/app-router-headers.js","../../node_modules/next/dist/server/lib/trace/tracer.js","../../node_modules/next/dist/server/lib/trace/constants.js","../../node_modules/next/dist/shared/lib/modern-browserslist-target.js","../../node_modules/next/dist/shared/lib/runtime-config.external.js","../../node_modules/next/dist/compiled/ws/package.json","../../node_modules/next/dist/compiled/node-html-parser/package.json","../../node_modules/next/dist/compiled/lru-cache/package.json","../../node_modules/@swc/helpers/_/_interop_require_default/package.json","../../node_modules/next/dist/compiled/ws/index.js","../../node_modules/next/dist/compiled/node-html-parser/index.js","../../node_modules/next/dist/compiled/lru-cache/index.js","../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../node_modules/@swc/helpers/package.json","../../node_modules/next/dist/client/components/async-local-storage.js","../../node_modules/next/dist/shared/lib/error-source.js","../../node_modules/next/dist/compiled/jsonwebtoken/package.json","../../node_modules/@swc/helpers/cjs/_interop_require_default.cjs","../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../node_modules/next/dist/compiled/jsonwebtoken/index.js","../../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../../node_modules/next/dist/compiled/debug/package.json","../../node_modules/next/dist/lib/semver-noop.js","../../node_modules/next/dist/compiled/debug/index.js","../../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/amp-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/app-router-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/entrypoints.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/head-manager-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/hooks-client-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/html-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/image-config-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/router-context.js","../../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/server-inserted-html.js","../../node_modules/next/dist/server/future/route-modules/app-page/module.compiled.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/amp-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/app-router-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/entrypoints.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/head-manager-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/hooks-client-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/html-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/image-config-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/router-context.js","../../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/server-inserted-html.js","../../node_modules/next/dist/server/future/route-modules/pages/module.compiled.js"]}
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":4,"routes":{},"dynamicRoutes":{},"preview":{"previewModeId":"851a7f29195e01de449591fbd7af7600","previewModeSigningKey":"e3487b4cf86e342b5e9692378355f5cf86c110666f3d4412393f863609c6e697","previewModeEncryptionKey":"f76d266d7e81daef4e55d847c327e081e68d9755bbdf133565731b34875b866b"},"notFoundRoutes":[]}
{"version":4,"routes":{},"dynamicRoutes":{},"preview":{"previewModeId":"aab7a3f51a50153024a3aa8b3f5b7370","previewModeSigningKey":"d7c1d93239439add3e94818db5a5755b9a9f9f1056a8b7f9d05b0d84ca8c3a7f","previewModeEncryptionKey":"cba25b398203c36b1bc87969a4a47c5f5c368406c5930880371639954389b109"},"notFoundRoutes":[]}
+1 -1
View File
@@ -1 +1 @@
{"version":3,"pages404":true,"caseSensitive":false,"basePath":"","redirects":[{"source":"/:path+/","destination":"/:path+","internal":true,"statusCode":308,"regex":"^(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))/$"}],"headers":[],"dynamicRoutes":[{"page":"/admin/services/[service_name]","regex":"^/admin/services/([^/]+?)(?:/)?$","routeKeys":{"nxtPservice_name":"nxtPservice_name"},"namedRegex":"^/admin/services/(?<nxtPservice_name>[^/]+?)(?:/)?$"},{"page":"/admin/users/[deployment_user_id]","regex":"^/admin/users/([^/]+?)(?:/)?$","routeKeys":{"nxtPdeployment_user_id":"nxtPdeployment_user_id"},"namedRegex":"^/admin/users/(?<nxtPdeployment_user_id>[^/]+?)(?:/)?$"}],"staticRoutes":[{"page":"/","regex":"^/(?:/)?$","routeKeys":{},"namedRegex":"^/(?:/)?$"},{"page":"/404","regex":"^/404(?:/)?$","routeKeys":{},"namedRegex":"^/404(?:/)?$"},{"page":"/admin","regex":"^/admin(?:/)?$","routeKeys":{},"namedRegex":"^/admin(?:/)?$"},{"page":"/admin/services","regex":"^/admin/services(?:/)?$","routeKeys":{},"namedRegex":"^/admin/services(?:/)?$"},{"page":"/admin/settings","regex":"^/admin/settings(?:/)?$","routeKeys":{},"namedRegex":"^/admin/settings(?:/)?$"},{"page":"/admin/users","regex":"^/admin/users(?:/)?$","routeKeys":{},"namedRegex":"^/admin/users(?:/)?$"},{"page":"/admin/users/add-user","regex":"^/admin/users/add\\-user(?:/)?$","routeKeys":{},"namedRegex":"^/admin/users/add\\-user(?:/)?$"},{"page":"/auth/login","regex":"^/auth/login(?:/)?$","routeKeys":{},"namedRegex":"^/auth/login(?:/)?$"},{"page":"/auth/logout","regex":"^/auth/logout(?:/)?$","routeKeys":{},"namedRegex":"^/auth/logout(?:/)?$"},{"page":"/auth/signup","regex":"^/auth/signup(?:/)?$","routeKeys":{},"namedRegex":"^/auth/signup(?:/)?$"}],"dataRoutes":[{"page":"/","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/index.json$"},{"page":"/admin","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin.json$"},{"page":"/admin/services","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin/services.json$"},{"page":"/admin/services/[service_name]","routeKeys":{"nxtPservice_name":"nxtPservice_name"},"dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin/services/([^/]+?)\\.json$","namedDataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin/services/(?<nxtPservice_name>[^/]+?)\\.json$"},{"page":"/admin/settings","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin/settings.json$"},{"page":"/admin/users","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin/users.json$"},{"page":"/admin/users/add-user","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin/users/add-user.json$"},{"page":"/admin/users/[deployment_user_id]","routeKeys":{"nxtPdeployment_user_id":"nxtPdeployment_user_id"},"dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin/users/([^/]+?)\\.json$","namedDataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/admin/users/(?<nxtPdeployment_user_id>[^/]+?)\\.json$"},{"page":"/auth/login","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/auth/login.json$"},{"page":"/auth/logout","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/auth/logout.json$"},{"page":"/auth/signup","dataRouteRegex":"^/_next/data/d1kxBLz09K\\-KfpyaxJE_y/auth/signup.json$"}],"rsc":{"header":"RSC","varyHeader":"RSC, Next-Router-State-Tree, Next-Router-Prefetch","prefetchHeader":"Next-Router-Prefetch","didPostponeHeader":"x-nextjs-postponed","contentTypeHeader":"text/x-component","suffix":".rsc","prefetchSuffix":".prefetch.rsc"},"rewrites":[]}
{"version":3,"pages404":true,"caseSensitive":false,"basePath":"","redirects":[{"source":"/:path+/","destination":"/:path+","internal":true,"statusCode":308,"regex":"^(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))/$"}],"headers":[],"dynamicRoutes":[{"page":"/admin/services/[service_name]","regex":"^/admin/services/([^/]+?)(?:/)?$","routeKeys":{"nxtPservice_name":"nxtPservice_name"},"namedRegex":"^/admin/services/(?<nxtPservice_name>[^/]+?)(?:/)?$"},{"page":"/admin/users/[deployment_user_id]","regex":"^/admin/users/([^/]+?)(?:/)?$","routeKeys":{"nxtPdeployment_user_id":"nxtPdeployment_user_id"},"namedRegex":"^/admin/users/(?<nxtPdeployment_user_id>[^/]+?)(?:/)?$"}],"staticRoutes":[{"page":"/","regex":"^/(?:/)?$","routeKeys":{},"namedRegex":"^/(?:/)?$"},{"page":"/404","regex":"^/404(?:/)?$","routeKeys":{},"namedRegex":"^/404(?:/)?$"},{"page":"/admin","regex":"^/admin(?:/)?$","routeKeys":{},"namedRegex":"^/admin(?:/)?$"},{"page":"/admin/services","regex":"^/admin/services(?:/)?$","routeKeys":{},"namedRegex":"^/admin/services(?:/)?$"},{"page":"/admin/settings","regex":"^/admin/settings(?:/)?$","routeKeys":{},"namedRegex":"^/admin/settings(?:/)?$"},{"page":"/admin/shell","regex":"^/admin/shell(?:/)?$","routeKeys":{},"namedRegex":"^/admin/shell(?:/)?$"},{"page":"/admin/users","regex":"^/admin/users(?:/)?$","routeKeys":{},"namedRegex":"^/admin/users(?:/)?$"},{"page":"/admin/users/add-user","regex":"^/admin/users/add\\-user(?:/)?$","routeKeys":{},"namedRegex":"^/admin/users/add\\-user(?:/)?$"},{"page":"/auth/login","regex":"^/auth/login(?:/)?$","routeKeys":{},"namedRegex":"^/auth/login(?:/)?$"},{"page":"/auth/logout","regex":"^/auth/logout(?:/)?$","routeKeys":{},"namedRegex":"^/auth/logout(?:/)?$"},{"page":"/auth/signup","regex":"^/auth/signup(?:/)?$","routeKeys":{},"namedRegex":"^/auth/signup(?:/)?$"}],"dataRoutes":[{"page":"/","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/index.json$"},{"page":"/admin","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin.json$"},{"page":"/admin/services","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/services.json$"},{"page":"/admin/services/[service_name]","routeKeys":{"nxtPservice_name":"nxtPservice_name"},"dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/services/([^/]+?)\\.json$","namedDataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/services/(?<nxtPservice_name>[^/]+?)\\.json$"},{"page":"/admin/settings","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/settings.json$"},{"page":"/admin/shell","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/shell.json$"},{"page":"/admin/users","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/users.json$"},{"page":"/admin/users/add-user","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/users/add-user.json$"},{"page":"/admin/users/[deployment_user_id]","routeKeys":{"nxtPdeployment_user_id":"nxtPdeployment_user_id"},"dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/users/([^/]+?)\\.json$","namedDataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/admin/users/(?<nxtPdeployment_user_id>[^/]+?)\\.json$"},{"page":"/auth/login","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/auth/login.json$"},{"page":"/auth/logout","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/auth/logout.json$"},{"page":"/auth/signup","dataRouteRegex":"^/_next/data/LKG2ddR8D3SPWi2345mPf/auth/signup.json$"}],"rsc":{"header":"RSC","varyHeader":"RSC, Next-Router-State-Tree, Next-Router-Prefetch","prefetchHeader":"Next-Router-Prefetch","didPostponeHeader":"x-nextjs-postponed","contentTypeHeader":"text/x-component","suffix":".rsc","prefetchSuffix":".prefetch.rsc"},"rewrites":[]}
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":1,"functions":{"/admin":{},"/_app":{},"/admin/users/[deployment_user_id]":{},"/admin/settings":{},"/admin/services/[service_name]":{},"/admin/users/add-user":{},"/admin/users":{},"/auth/logout":{},"/admin/services":{},"/":{},"/auth/login":{},"/auth/signup":{}}}
{"version":1,"functions":{"/_app":{},"/admin/services/[service_name]":{},"/admin":{},"/admin/settings":{},"/admin/shell":{},"/admin/services":{},"/admin/users/[deployment_user_id]":{},"/admin/users/add-user":{},"/admin/users":{},"/auth/logout":{},"/auth/signup":{},"/":{},"/auth/login":{}}}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"/_error":"pages/_error.js","/_document":"pages/_document.js","/404":"pages/404.html","/api/admin/delete-user":"pages/api/admin/delete-user.js","/api/admin/download-private-ssh-key":"pages/api/admin/download-private-ssh-key.js","/api/admin/edit-user":"pages/api/admin/edit-user.js","/api/admin/settings":"pages/api/admin/settings.js","/api/auth/login":"pages/api/auth/login.js","/api/auth/signup":"pages/api/auth/signup.js","/api/hello":"pages/api/hello.js","/_app":"pages/_app.js","/admin":"pages/admin.js","/admin/services/[service_name]":"pages/admin/services/[service_name].js","/admin/services":"pages/admin/services.js","/admin/settings":"pages/admin/settings.js","/admin/users/add-user":"pages/admin/users/add-user.js","/admin/users":"pages/admin/users.js","/admin/users/[deployment_user_id]":"pages/admin/users/[deployment_user_id].js","/auth/login":"pages/auth/login.js","/auth/logout":"pages/auth/logout.js","/auth/signup":"pages/auth/signup.js","/":"pages/index.js"}
{"/_error":"pages/_error.js","/_document":"pages/_document.js","/404":"pages/404.html","/api/admin/delete-user":"pages/api/admin/delete-user.js","/api/admin/download-private-ssh-key":"pages/api/admin/download-private-ssh-key.js","/api/admin/edit-user":"pages/api/admin/edit-user.js","/api/admin/settings":"pages/api/admin/settings.js","/api/auth/login":"pages/api/auth/login.js","/api/auth/signup":"pages/api/auth/signup.js","/api/hello":"pages/api/hello.js","/_app":"pages/_app.js","/admin":"pages/admin.js","/admin/services":"pages/admin/services.js","/admin/settings":"pages/admin/settings.js","/admin/services/[service_name]":"pages/admin/services/[service_name].js","/admin/shell":"pages/admin/shell.js","/admin/users/add-user":"pages/admin/users/add-user.js","/admin/users":"pages/admin/users.js","/admin/users/[deployment_user_id]":"pages/admin/users/[deployment_user_id].js","/auth/logout":"pages/auth/logout.js","/auth/login":"pages/auth/login.js","/auth/signup":"pages/auth/signup.js","/":"pages/index.js"}
+1 -1
View File
@@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/_next/static/css/08e6997802749ec3.css" as="style"/><link rel="stylesheet" href="/_next/static/css/08e6997802749ec3.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-38cee4c0e358b1a3.js" defer=""></script><script src="/_next/static/chunks/framework-bfa817c72d812316.js" defer=""></script><script src="/_next/static/chunks/main-761b7faf59ba862f.js" defer=""></script><script src="/_next/static/chunks/pages/_app-0610d36404ed5f1e.js" defer=""></script><script src="/_next/static/chunks/29107295-2819ba6e51dbf961.js" defer=""></script><script src="/_next/static/chunks/664-e347d64e56042a02.js" defer=""></script><script src="/_next/static/chunks/pages/404-8292e1046538596b.js" defer=""></script><script src="/_next/static/d1kxBLz09K-KfpyaxJE_y/_buildManifest.js" defer=""></script><script src="/_next/static/d1kxBLz09K-KfpyaxJE_y/_ssgManifest.js" defer=""></script></head><body class="antialiased"><div id="__next"><main class="flex flex-col items-center twui-main w-screen h-screen overflow-hidden"><section class="flex flex-col items-center px-4 sm:px-10 py-10 twui-section w-full h-full"><div class="flex w-full max-w-container gap-4 justify-between flex-wrap flex-col xl:flex-row items-start xl:items-center twui-container grid-frame grid-cols-1 h-full"><div class="flex flex-col items-start gap-4 twui-stack w-full justify-between h-full grid-cell"><div class="flex flex-col items-start twui-stack gap-0"><div class="flex flex-row gap-2 flex-wrap items-center twui-row"><div class="flex flex-row gap-2 flex-wrap items-center twui-row p-6"><a aria-label="TurboCI home" class="inline-flex items-center gap-3" href="/"><svg viewBox="0 0 48 48" aria-hidden="true" class="h-10 w-10 shrink-0"><defs><linearGradient id="_R_1am_" x1="9" y1="9" x2="31" y2="34" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#9cf0c0"></stop><stop offset="100%" stop-color="#42d392"></stop></linearGradient><linearGradient id="_R_1amH1_" x1="22" y1="22" x2="36" y2="40" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#2bc67e"></stop><stop offset="100%" stop-color="#1f8458"></stop></linearGradient></defs><path d="M35 6H23.6c-2 0-3.8 1-4.8 2.7L7.5 28.3c-1.4 2.5.4 5.7 3.2 5.7h9.1c2 0 3.8-1 4.8-2.7l11.2-19.6C39.6 9.2 37.8 6 35 6Z" fill="url(#_R_1am_)"></path><path d="M23.9 24h13.4c2.8 0 4.6 3.1 3.2 5.6l-4.1 7.1c-1 1.7-2.8 2.7-4.8 2.7H18.2c-2.8 0-4.6-3.1-3.2-5.6l4.1-7.1c1-1.7 2.8-2.7 4.8-2.7Z" fill="url(#_R_1amH1_)"></path></svg><span class="font-display text-[1.02rem] font-semibold tracking-tight text-foreground">TurboCI</span></a></div><div class="border-slate-200 dark:border-white/10 border-0 border-l h-full min-h-5 twui-divider twui-divider-vertical border-solid"></div></div><div class="border-slate-200 dark:border-white/10 border-0 border-t w-full twui-divider twui-divider-horizontal border-solid"></div></div><div class="flex flex-col items-center justify-center gap-4 w-full h-full twui-center p-10"><h2 class="text-2xl md:text-3xl mb-4 twui-headings twui-heading twui-h2">Page Not Found!</h2></div><div class="flex flex-col items-start gap-4 twui-stack"><div class="border-slate-200 dark:border-white/10 border-0 border-t w-full twui-divider twui-divider-horizontal border-solid"></div><div class="twui-spacer h-20 w-full"></div></div></div></div></section></main></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/404","query":{},"buildId":"d1kxBLz09K-KfpyaxJE_y","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/_next/static/css/08e6997802749ec3.css" as="style"/><link rel="stylesheet" href="/_next/static/css/08e6997802749ec3.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-38cee4c0e358b1a3.js" defer=""></script><script src="/_next/static/chunks/framework-bfa817c72d812316.js" defer=""></script><script src="/_next/static/chunks/main-761b7faf59ba862f.js" defer=""></script><script src="/_next/static/chunks/pages/_app-12c3cc69366c709d.js" defer=""></script><script src="/_next/static/chunks/29107295-2819ba6e51dbf961.js" defer=""></script><script src="/_next/static/chunks/664-e347d64e56042a02.js" defer=""></script><script src="/_next/static/chunks/pages/404-8292e1046538596b.js" defer=""></script><script src="/_next/static/LKG2ddR8D3SPWi2345mPf/_buildManifest.js" defer=""></script><script src="/_next/static/LKG2ddR8D3SPWi2345mPf/_ssgManifest.js" defer=""></script></head><body class="antialiased"><div id="__next"><main class="flex flex-col items-center twui-main w-screen h-screen overflow-hidden"><section class="flex flex-col items-center px-4 sm:px-10 py-10 twui-section w-full h-full"><div class="flex w-full max-w-container gap-4 justify-between flex-wrap flex-col xl:flex-row items-start xl:items-center twui-container grid-frame grid-cols-1 h-full"><div class="flex flex-col items-start gap-4 twui-stack w-full justify-between h-full grid-cell"><div class="flex flex-col items-start twui-stack gap-0"><div class="flex flex-row gap-2 flex-wrap items-center twui-row"><div class="flex flex-row gap-2 flex-wrap items-center twui-row p-6"><a aria-label="TurboCI home" class="inline-flex items-center gap-3" href="/"><svg viewBox="0 0 48 48" aria-hidden="true" class="h-10 w-10 shrink-0"><defs><linearGradient id="_R_1am_" x1="9" y1="9" x2="31" y2="34" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#9cf0c0"></stop><stop offset="100%" stop-color="#42d392"></stop></linearGradient><linearGradient id="_R_1amH1_" x1="22" y1="22" x2="36" y2="40" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#2bc67e"></stop><stop offset="100%" stop-color="#1f8458"></stop></linearGradient></defs><path d="M35 6H23.6c-2 0-3.8 1-4.8 2.7L7.5 28.3c-1.4 2.5.4 5.7 3.2 5.7h9.1c2 0 3.8-1 4.8-2.7l11.2-19.6C39.6 9.2 37.8 6 35 6Z" fill="url(#_R_1am_)"></path><path d="M23.9 24h13.4c2.8 0 4.6 3.1 3.2 5.6l-4.1 7.1c-1 1.7-2.8 2.7-4.8 2.7H18.2c-2.8 0-4.6-3.1-3.2-5.6l4.1-7.1c1-1.7 2.8-2.7 4.8-2.7Z" fill="url(#_R_1amH1_)"></path></svg><span class="font-display text-[1.02rem] font-semibold tracking-tight text-foreground">TurboCI</span></a></div><div class="border-slate-200 dark:border-white/10 border-0 border-l h-full min-h-5 twui-divider twui-divider-vertical border-solid"></div></div><div class="border-slate-200 dark:border-white/10 border-0 border-t w-full twui-divider twui-divider-horizontal border-solid"></div></div><div class="flex flex-col items-center justify-center gap-4 w-full h-full twui-center p-10"><h2 class="text-2xl md:text-3xl mb-4 twui-headings twui-heading twui-h2">Page Not Found!</h2></div><div class="flex flex-col items-start gap-4 twui-stack"><div class="border-slate-200 dark:border-white/10 border-0 border-t w-full twui-divider twui-divider-horizontal border-solid"></div><div class="twui-spacer h-20 w-full"></div></div></div></div></section></main></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/404","query":{},"buildId":"LKG2ddR8D3SPWi2345mPf","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
+1 -1
View File
@@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><title>500: Internal Server Error</title><meta name="next-head-count" content="3"/><link rel="preload" href="/_next/static/css/08e6997802749ec3.css" as="style"/><link rel="stylesheet" href="/_next/static/css/08e6997802749ec3.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-38cee4c0e358b1a3.js" defer=""></script><script src="/_next/static/chunks/framework-bfa817c72d812316.js" defer=""></script><script src="/_next/static/chunks/main-761b7faf59ba862f.js" defer=""></script><script src="/_next/static/chunks/pages/_app-0610d36404ed5f1e.js" defer=""></script><script src="/_next/static/chunks/pages/_error-10090a8ee211d5fd.js" defer=""></script><script src="/_next/static/d1kxBLz09K-KfpyaxJE_y/_buildManifest.js" defer=""></script><script src="/_next/static/d1kxBLz09K-KfpyaxJE_y/_ssgManifest.js" defer=""></script></head><body class="antialiased"><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":500}},"page":"/_error","query":{},"buildId":"d1kxBLz09K-KfpyaxJE_y","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><title>500: Internal Server Error</title><meta name="next-head-count" content="3"/><link rel="preload" href="/_next/static/css/08e6997802749ec3.css" as="style"/><link rel="stylesheet" href="/_next/static/css/08e6997802749ec3.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-38cee4c0e358b1a3.js" defer=""></script><script src="/_next/static/chunks/framework-bfa817c72d812316.js" defer=""></script><script src="/_next/static/chunks/main-761b7faf59ba862f.js" defer=""></script><script src="/_next/static/chunks/pages/_app-12c3cc69366c709d.js" defer=""></script><script src="/_next/static/chunks/pages/_error-10090a8ee211d5fd.js" defer=""></script><script src="/_next/static/LKG2ddR8D3SPWi2345mPf/_buildManifest.js" defer=""></script><script src="/_next/static/LKG2ddR8D3SPWi2345mPf/_ssgManifest.js" defer=""></script></head><body class="antialiased"><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":500}},"page":"/_error","query":{},"buildId":"LKG2ddR8D3SPWi2345mPf","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
self.__BUILD_MANIFEST=function(s,e,a,c,t,i,n,d,u){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[s,a,e,c,u,t,i,"static/chunks/pages/index-13e31ea96205b999.js"],"/404":[s,e,"static/chunks/pages/404-8292e1046538596b.js"],"/_error":["static/chunks/pages/_error-10090a8ee211d5fd.js"],"/admin":[s,a,e,c,t,i,n,d,"static/chunks/pages/admin-914586c03dfda5c1.js"],"/admin/services":[s,a,e,c,t,i,n,d,"static/chunks/pages/admin/services-16971626ad193c95.js"],"/admin/services/[service_name]":[s,a,e,c,t,i,n,d,"static/chunks/pages/admin/services/[service_name]-e8b08e5c11b1b12c.js"],"/admin/settings":[s,a,e,c,t,i,n,"static/chunks/pages/admin/settings-f58d63b2752f72a6.js"],"/admin/shell":[s,a,e,c,t,n,"static/chunks/pages/admin/shell-a1da21d85fd3c30b.js"],"/admin/users":[s,a,e,c,t,n,"static/chunks/pages/admin/users-73e9775838b56d95.js"],"/admin/users/add-user":[s,a,e,c,t,i,n,"static/chunks/pages/admin/users/add-user-1db7c9ee89830b5b.js"],"/admin/users/[deployment_user_id]":[s,a,"static/chunks/ff39441c-88f593a46cb65964.js","static/chunks/d848df63-a470d32e7be4bfe3.js",e,c,"static/chunks/281-dcee0c6401936d5e.js",t,i,n,"static/chunks/pages/admin/users/[deployment_user_id]-fb1c3c9148c518cd.js"],"/auth/login":[s,a,e,c,u,t,i,"static/chunks/pages/auth/login-3f936f997e58438c.js"],"/auth/logout":[s,e,"static/chunks/pages/auth/logout-c8b207f96be4f684.js"],"/auth/signup":[s,a,e,c,t,i,"static/chunks/pages/auth/signup-5c0f6835749e755e.js"],sortedPages:["/","/404","/_app","/_error","/admin","/admin/services","/admin/services/[service_name]","/admin/settings","/admin/shell","/admin/users","/admin/users/add-user","/admin/users/[deployment_user_id]","/auth/login","/auth/logout","/auth/signup"]}}("static/chunks/29107295-2819ba6e51dbf961.js","static/chunks/664-e347d64e56042a02.js","static/chunks/e5d67320-bf1772fa71739690.js","static/chunks/433-bb8bdacebff9ab53.js","static/chunks/7-f0121001f735b8fe.js","static/chunks/827-7975edcf8481ec69.js","static/chunks/910-7de3855073c34589.js","static/chunks/926-c50746382fde484e.js","static/chunks/225-f257bb5bd6014d68.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[964],{64830:function(e,n,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin",function(){return s(9854)}])},49533:function(e,n,s){"use strict";s.d(n,{Z:function(){return _}});var r=s(85893),i=s(5606),t=s(60),l=s(67294),c=s(55412),a=s(86609),d=s(94655);function o(e){let{link:n,icon:s,...i}=e;return(0,r.jsx)(a.Z,{href:n.url,...i,...n.linkProps,children:(0,r.jsxs)(d.Z,{children:[(0,r.jsx)("span",{children:n.title}),s||(0,r.jsx)(c.Z,{size:17})]})})}var u=s(56211),v=s(1765),x=s(39483),f=s(25808),m=s(96486),p=s.n(m);function j(e){let{service:n}=e,{pageProps:s,ws:c}=(0,l.useContext)(i.AppContext),{deployment:a}=s,{ready:m,setReady:j}=(0,f.Z)(),_=null==a?void 0:a.services.filter(e=>e.parent_service_name==n.service_name),h=[n,..._||[]],[Z,g]=(0,l.useState)(null==h?void 0:h[0]);h.findIndex(e=>e.service_name==(null==Z?void 0:Z.service_name));let N=null==Z?void 0:Z.servers,[w,C]=(0,l.useState)(null==N?void 0:N[0]),k=(0,l.useRef)(void 0);return(0,l.useEffect)(()=>{var e;C(null==Z?void 0:null===(e=Z.servers)||void 0===e?void 0:e[0])},[Z]),(0,l.useEffect)(()=>{if(j(!1),w&&k.current){var e;e=k.current,c.sendData({event:"client:kill-port",server:w,service:p().omit(n,["servers"]),port:e})}setTimeout(()=>{j(!0)},2e3)},[w]),(0,r.jsxs)(t.Z,{className:"grid-cell",children:[(0,r.jsxs)(t.Z,{className:"grid-cell-content",children:[(0,r.jsxs)(d.Z,{className:"w-full justify-between",children:[(0,r.jsx)(u.Z,{children:n.service_name}),(0,r.jsx)(o,{link:{url:"/admin/services/".concat(n.service_name),title:"View"}})]}),(0,r.jsxs)("code",{children:[n.service_name," service"]})]}),(0,r.jsx)("hr",{}),(0,r.jsxs)(t.Z,{className:"gap-0",children:[(0,r.jsxs)(d.Z,{className:"p-4 grid md:grid-cols-2",children:[(0,r.jsx)(x.Z,{options:h.map((e,n)=>({value:e.service_name,title:"Cluster #".concat(n+1)})),changeHandler:e=>{g(h.find(n=>n.service_name==e))}}),N?(0,r.jsx)(x.Z,{options:N.map(e=>({value:e.private_ip,title:e.private_ip})),changeHandler:e=>{C(N.find(n=>n.private_ip==e))}}):void 0]}),m&&w?(0,r.jsx)(v.Z,{server:w,service:n,portRef:k}):void 0]})]})}function _(){let{pageProps:e}=(0,l.useContext)(i.AppContext),{deployment:n,children_services:s}=e,c=null==n?void 0:n.services.filter(e=>!e.parent_service_name);return(0,r.jsx)(t.Z,{className:"w-full nested-grid-frame xl:grid-cols-2",children:null==c?void 0:c.map((e,n)=>(0,r.jsx)(j,{service:e},n))})}},9854:function(e,n,s){"use strict";s.r(n),s.d(n,{__N_SSP:function(){return p},default:function(){return j}});var r=s(85893),i=s(5606),t=s(94655),l=s(3668),c=s(60),a=s(67294);function d(){let{pageProps:e}=(0,a.useContext)(i.AppContext),{user:n,deployment:s,deployment_id:d}=e;if(!s)return null;let o=s.services.filter(e=>!e.parent_service_name);return(0,r.jsx)(c.Z,{className:"w-full gap-0",children:(0,r.jsxs)(c.Z,{className:"grid-cell-content",children:[(0,r.jsxs)(l.Z,{children:[o.length," Services"]}),(0,r.jsx)(t.Z,{children:o.map((e,n)=>(0,r.jsx)("a",{href:"/admin/services/".concat(e.service_name),children:(0,r.jsx)("code",{children:e.service_name})},n))})]})})}var o=s(33028),u=s(38249),v=s(60705),x=s(49533);function f(){let{pageProps:e}=(0,a.useContext)(i.AppContext),{user:n,deployment:s,deployment_id:t}=e,l=null==s?void 0:s.deployment_name;return(0,r.jsxs)(a.Fragment,{children:[(0,r.jsx)(o.Z,{title:"".concat((0,u.Z)(l)," Deplyoment Dashboard"),description:(0,r.jsxs)(r.Fragment,{children:["Deployment"," ",(0,r.jsx)("code",{children:null==t?void 0:t.split("-").shift()})," > ",(0,r.jsx)("code",{children:null==s?void 0:s.deployment_name})]})}),(0,r.jsx)(v.Z,{}),(0,r.jsx)(d,{}),(0,r.jsx)(v.Z,{}),(0,r.jsx)(x.Z,{})]})}var m=s(89350),p=!0;function j(){return(0,r.jsx)(m.Z,{children:(0,r.jsx)(f,{})})}}},function(e){e.O(0,[662,75,664,433,7,827,910,926,888,774,179],function(){return e(e.s=64830)}),_N_E=e.O()}]);
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[964],{64830:function(e,n,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin",function(){return s(9854)}])},49533:function(e,n,s){"use strict";s.d(n,{Z:function(){return h}});var i=s(85893),r=s(5606),t=s(60),l=s(67294),c=s(55412),a=s(86609),d=s(94655);function o(e){let{link:n,icon:s,...r}=e;return(0,i.jsx)(a.Z,{href:n.url,...r,...n.linkProps,children:(0,i.jsxs)(d.Z,{children:[(0,i.jsx)("span",{children:n.title}),s||(0,i.jsx)(c.Z,{size:17})]})})}var u=s(56211),v=s(45377),x=s(39483),m=s(25808),p=s(96486),f=s.n(p);function j(e){let{service:n}=e,{pageProps:s,ws:c}=(0,l.useContext)(r.AppContext),{deployment:a}=s,{ready:p,setReady:j}=(0,m.Z)(),h=null==a?void 0:a.services.filter(e=>e.parent_service_name==n.service_name),_=[n,...h||[]],[Z,g]=(0,l.useState)(null==_?void 0:_[0]);_.findIndex(e=>e.service_name==(null==Z?void 0:Z.service_name));let N=null==Z?void 0:Z.servers,[w,C]=(0,l.useState)(null==N?void 0:N[0]),k=(0,l.useRef)(void 0);return(0,l.useEffect)(()=>{var e;C(null==Z?void 0:null===(e=Z.servers)||void 0===e?void 0:e[0])},[Z]),(0,l.useEffect)(()=>{if(j(!1),w&&k.current){var e;e=k.current,c.sendData({event:"client:kill-port",server:w,service:f().omit(n,["servers"]),port:e})}setTimeout(()=>{j(!0)},2e3)},[w]),(0,i.jsxs)(t.Z,{className:"grid-cell",children:[(0,i.jsxs)(t.Z,{className:"grid-cell-content",children:[(0,i.jsxs)(d.Z,{className:"w-full justify-between",children:[(0,i.jsx)(u.Z,{children:n.service_name}),(0,i.jsx)(o,{link:{url:"/admin/services/".concat(n.service_name),title:"View"}})]}),(0,i.jsxs)("code",{children:[n.service_name," service"]})]}),(0,i.jsx)("hr",{}),(0,i.jsxs)(t.Z,{className:"gap-0",children:[(0,i.jsxs)(d.Z,{className:"p-4 grid md:grid-cols-2",children:[(0,i.jsx)(x.Z,{options:_.map((e,n)=>({value:e.service_name,title:"Cluster #".concat(n+1)})),changeHandler:e=>{g(_.find(n=>n.service_name==e))}}),N?(0,i.jsx)(x.Z,{options:N.map(e=>({value:e.private_ip,title:e.private_ip})),changeHandler:e=>{C(N.find(n=>n.private_ip==e))}}):void 0]}),p&&w?(0,i.jsx)(v.Z,{server:w,service:n,portRef:k}):void 0]})]})}function h(){let{pageProps:e}=(0,l.useContext)(r.AppContext),{deployment:n,children_services:s}=e,c=null==n?void 0:n.services.filter(e=>!e.parent_service_name);return(0,i.jsx)(t.Z,{className:"w-full nested-grid-frame xl:grid-cols-2",children:null==c?void 0:c.map((e,n)=>(0,i.jsx)(j,{service:e},n))})}},9854:function(e,n,s){"use strict";s.r(n),s.d(n,{__N_SSP:function(){return f},default:function(){return j}});var i=s(85893),r=s(5606),t=s(94655),l=s(3668),c=s(60),a=s(67294);function d(){let{pageProps:e}=(0,a.useContext)(r.AppContext),{user:n,deployment:s,deployment_id:d}=e;return s?(0,i.jsx)(c.Z,{className:"w-full gap-0",children:(0,i.jsxs)(c.Z,{className:"grid-cell-content",children:[(0,i.jsxs)(l.Z,{children:[s.services.length," Services"]}),(0,i.jsx)(t.Z,{children:s.services.map((e,n)=>(0,i.jsx)("a",{href:"/admin/services/".concat(e.service_name),children:(0,i.jsx)("code",{children:e.service_name})},n))})]})}):null}var o=s(33028),u=s(38249),v=s(60705),x=s(49533);function m(){let{pageProps:e}=(0,a.useContext)(r.AppContext),{user:n,deployment:s,deployment_id:t}=e,l=null==s?void 0:s.deployment_name;return(0,i.jsxs)(a.Fragment,{children:[(0,i.jsx)(o.Z,{title:"".concat((0,u.Z)(l)," Deplyoment Dashboard"),description:(0,i.jsxs)(i.Fragment,{children:["Deployment"," ",(0,i.jsx)("code",{children:null==t?void 0:t.split("-").shift()})," > ",(0,i.jsx)("code",{children:null==s?void 0:s.deployment_name})]})}),(0,i.jsx)(v.Z,{}),(0,i.jsx)(d,{}),(0,i.jsx)(v.Z,{}),(0,i.jsx)(x.Z,{})]})}var p=s(89350),f=!0;function j(){return(0,i.jsx)(p.Z,{children:(0,i.jsx)(m,{})})}}},function(e){e.O(0,[662,75,664,433,7,827,910,464,888,774,179],function(){return e(e.s=64830)}),_N_E=e.O()}]);
@@ -0,0 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[148],{55634:function(e,n,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/services",function(){return s(78701)}])},49533:function(e,n,s){"use strict";s.d(n,{Z:function(){return j}});var i=s(85893),r=s(5606),t=s(60),l=s(67294),c=s(55412),a=s(86609),o=s(94655);function u(e){let{link:n,icon:s,...r}=e;return(0,i.jsx)(a.Z,{href:n.url,...r,...n.linkProps,children:(0,i.jsxs)(o.Z,{children:[(0,i.jsx)("span",{children:n.title}),s||(0,i.jsx)(c.Z,{size:17})]})})}var d=s(56211),v=s(1765),f=s(39483),p=s(25808),x=s(96486),m=s.n(x);function _(e){let{service:n}=e,{pageProps:s,ws:c}=(0,l.useContext)(r.AppContext),{deployment:a}=s,{ready:x,setReady:_}=(0,p.Z)(),j=null==a?void 0:a.services.filter(e=>e.parent_service_name==n.service_name),h=[n,...j||[]],[Z,N]=(0,l.useState)(null==h?void 0:h[0]);h.findIndex(e=>e.service_name==(null==Z?void 0:Z.service_name));let g=null==Z?void 0:Z.servers,[w,C]=(0,l.useState)(null==g?void 0:g[0]),k=(0,l.useRef)(void 0);return(0,l.useEffect)(()=>{var e;C(null==Z?void 0:null===(e=Z.servers)||void 0===e?void 0:e[0])},[Z]),(0,l.useEffect)(()=>{if(_(!1),w&&k.current){var e;e=k.current,c.sendData({event:"client:kill-port",server:w,service:m().omit(n,["servers"]),port:e})}setTimeout(()=>{_(!0)},2e3)},[w]),(0,i.jsxs)(t.Z,{className:"grid-cell",children:[(0,i.jsxs)(t.Z,{className:"grid-cell-content",children:[(0,i.jsxs)(o.Z,{className:"w-full justify-between",children:[(0,i.jsx)(d.Z,{children:n.service_name}),(0,i.jsx)(u,{link:{url:"/admin/services/".concat(n.service_name),title:"View"}})]}),(0,i.jsxs)("code",{children:[n.service_name," service"]})]}),(0,i.jsx)("hr",{}),(0,i.jsxs)(t.Z,{className:"gap-0",children:[(0,i.jsxs)(o.Z,{className:"p-4 grid md:grid-cols-2",children:[(0,i.jsx)(f.Z,{options:h.map((e,n)=>({value:e.service_name,title:"Cluster #".concat(n+1)})),changeHandler:e=>{N(h.find(n=>n.service_name==e))}}),g?(0,i.jsx)(f.Z,{options:g.map(e=>({value:e.private_ip,title:e.private_ip})),changeHandler:e=>{C(g.find(n=>n.private_ip==e))}}):void 0]}),x&&w?(0,i.jsx)(v.Z,{server:w,service:n,portRef:k}):void 0]})]})}function j(){let{pageProps:e}=(0,l.useContext)(r.AppContext),{deployment:n,children_services:s}=e,c=null==n?void 0:n.services.filter(e=>!e.parent_service_name);return(0,i.jsx)(t.Z,{className:"w-full nested-grid-frame xl:grid-cols-2",children:null==c?void 0:c.map((e,n)=>(0,i.jsx)(_,{service:e},n))})}},78701:function(e,n,s){"use strict";s.r(n),s.d(n,{__N_SSP:function(){return v},default:function(){return f}});var i=s(85893),r=s(67294),t=s(5606),l=s(38249),c=s(60705),a=s(33028),o=s(49533);function u(){let{pageProps:e}=(0,r.useContext)(t.AppContext),{user:n,deployment:s,deployment_id:u}=e,d=null==s?void 0:s.deployment_name;return(0,i.jsxs)(r.Fragment,{children:[(0,i.jsx)(a.Z,{title:"".concat((0,l.Z)(d)," Deplyoment Dashboard"),description:(0,i.jsxs)(i.Fragment,{children:["Deployment"," ",(0,i.jsx)("code",{children:null==u?void 0:u.split("-").shift()})," > ",(0,i.jsx)("code",{children:null==s?void 0:s.deployment_name})]})}),(0,i.jsx)(c.Z,{}),(0,i.jsx)(o.Z,{})]})}var d=s(89350),v=!0;function f(){return(0,i.jsx)(d.Z,{children:(0,i.jsx)(u,{})})}}},function(e){e.O(0,[662,75,664,433,7,827,910,926,888,774,179],function(){return e(e.s=55634)}),_N_E=e.O()}]);
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[148],{55634:function(e,n,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/services",function(){return s(78701)}])},49533:function(e,n,s){"use strict";s.d(n,{Z:function(){return j}});var i=s(85893),r=s(5606),t=s(60),l=s(67294),c=s(55412),a=s(86609),o=s(94655);function u(e){let{link:n,icon:s,...r}=e;return(0,i.jsx)(a.Z,{href:n.url,...r,...n.linkProps,children:(0,i.jsxs)(o.Z,{children:[(0,i.jsx)("span",{children:n.title}),s||(0,i.jsx)(c.Z,{size:17})]})})}var d=s(56211),v=s(45377),f=s(39483),p=s(25808),x=s(96486),m=s.n(x);function _(e){let{service:n}=e,{pageProps:s,ws:c}=(0,l.useContext)(r.AppContext),{deployment:a}=s,{ready:x,setReady:_}=(0,p.Z)(),j=null==a?void 0:a.services.filter(e=>e.parent_service_name==n.service_name),h=[n,...j||[]],[Z,N]=(0,l.useState)(null==h?void 0:h[0]);h.findIndex(e=>e.service_name==(null==Z?void 0:Z.service_name));let g=null==Z?void 0:Z.servers,[w,C]=(0,l.useState)(null==g?void 0:g[0]),k=(0,l.useRef)(void 0);return(0,l.useEffect)(()=>{var e;C(null==Z?void 0:null===(e=Z.servers)||void 0===e?void 0:e[0])},[Z]),(0,l.useEffect)(()=>{if(_(!1),w&&k.current){var e;e=k.current,c.sendData({event:"client:kill-port",server:w,service:m().omit(n,["servers"]),port:e})}setTimeout(()=>{_(!0)},2e3)},[w]),(0,i.jsxs)(t.Z,{className:"grid-cell",children:[(0,i.jsxs)(t.Z,{className:"grid-cell-content",children:[(0,i.jsxs)(o.Z,{className:"w-full justify-between",children:[(0,i.jsx)(d.Z,{children:n.service_name}),(0,i.jsx)(u,{link:{url:"/admin/services/".concat(n.service_name),title:"View"}})]}),(0,i.jsxs)("code",{children:[n.service_name," service"]})]}),(0,i.jsx)("hr",{}),(0,i.jsxs)(t.Z,{className:"gap-0",children:[(0,i.jsxs)(o.Z,{className:"p-4 grid md:grid-cols-2",children:[(0,i.jsx)(f.Z,{options:h.map((e,n)=>({value:e.service_name,title:"Cluster #".concat(n+1)})),changeHandler:e=>{N(h.find(n=>n.service_name==e))}}),g?(0,i.jsx)(f.Z,{options:g.map(e=>({value:e.private_ip,title:e.private_ip})),changeHandler:e=>{C(g.find(n=>n.private_ip==e))}}):void 0]}),x&&w?(0,i.jsx)(v.Z,{server:w,service:n,portRef:k}):void 0]})]})}function j(){let{pageProps:e}=(0,l.useContext)(r.AppContext),{deployment:n,children_services:s}=e,c=null==n?void 0:n.services.filter(e=>!e.parent_service_name);return(0,i.jsx)(t.Z,{className:"w-full nested-grid-frame xl:grid-cols-2",children:null==c?void 0:c.map((e,n)=>(0,i.jsx)(_,{service:e},n))})}},78701:function(e,n,s){"use strict";s.r(n),s.d(n,{__N_SSP:function(){return v},default:function(){return f}});var i=s(85893),r=s(67294),t=s(5606),l=s(38249),c=s(60705),a=s(33028),o=s(49533);function u(){let{pageProps:e}=(0,r.useContext)(t.AppContext),{user:n,deployment:s,deployment_id:u}=e,d=null==s?void 0:s.deployment_name;return(0,i.jsxs)(r.Fragment,{children:[(0,i.jsx)(a.Z,{title:"".concat((0,l.Z)(d)," Deplyoment Dashboard"),description:(0,i.jsxs)(i.Fragment,{children:["Deployment"," ",(0,i.jsx)("code",{children:null==u?void 0:u.split("-").shift()})," > ",(0,i.jsx)("code",{children:null==s?void 0:s.deployment_name})]})}),(0,i.jsx)(c.Z,{}),(0,i.jsx)(o.Z,{})]})}var d=s(89350),v=!0;function f(){return(0,i.jsx)(d.Z,{children:(0,i.jsx)(u,{})})}}},function(e){e.O(0,[662,75,664,433,7,827,910,464,888,774,179],function(){return e(e.s=55634)}),_N_E=e.O()}]);
@@ -1 +0,0 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[329],{89426:function(e,n,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/services/[service_name]",function(){return s(52963)}])},52963:function(e,n,s){"use strict";s.r(n),s.d(n,{__N_SSP:function(){return h},default:function(){return _}});var l=s(85893),r=s(67294),i=s(5606),t=s(38249),c=s(60705),a=s(33028),d=s(60),o=s(94655),u=s(45377);function x(e){let{service:n}=e,{pageProps:s}=(0,r.useContext)(i.AppContext),t=n.servers,[c,a]=(0,r.useState)(null==t?void 0:t[0]),x=null!=t&&!!t.length&&!!(t.length%2);return(0,l.jsx)(d.Z,{className:"w-full grid-cell col-span-1 gap-0",children:(0,l.jsxs)(o.Z,{className:"nested-grid-frame xl:grid-cols-2",children:[null==t?void 0:t.map((e,s)=>(0,l.jsx)(u.Z,{server:e,service:n,wrapperProps:{className:"grid-cell"}},s)),x?(0,l.jsx)("div",{className:"grid-cell"}):null]})})}var v=s(39483),p=s(56211);function m(){let{pageProps:e,ws:n}=(0,r.useContext)(i.AppContext),{service:s,deployment:t,children_services:c}=e,a=[s,...c||[]],[u,m]=(0,r.useState)(s||void 0),j=a.findIndex(e=>(null==e?void 0:e.service_name)==(null==u?void 0:u.service_name))+1;return null==u||u.servers,(0,l.jsx)(d.Z,{className:"w-full nested-grid-frame grid-cols-1",children:(0,l.jsxs)(d.Z,{className:"grid-cell gap-0",children:[(0,l.jsx)(o.Z,{className:"grid-cell-content flex-nowrap",children:(0,l.jsxs)(o.Z,{className:"w-full justify-between",children:[(0,l.jsx)(o.Z,{children:(0,l.jsxs)(p.Z,{children:["Cluster #",j]})}),(0,l.jsx)(o.Z,{children:(0,l.jsx)(v.Z,{options:a.map((e,n)=>({value:null==e?void 0:e.service_name,title:"Cluster #".concat(n+1)})),wrapperWrapperProps:{className:"max-w-[250px]"},changeHandler:e=>{m(void 0),n.sendData({event:"client:kill-all-ports"}),setTimeout(()=>{m(a.find(n=>(null==n?void 0:n.service_name)==e)||void 0)},200)}})})]})}),(0,l.jsx)("hr",{}),u?(0,l.jsx)(x,{service:u}):void 0]})})}function j(){let{pageProps:e}=(0,r.useContext)(i.AppContext),{service:n,deployment:s}=e,d=null==s?void 0:s.deployment_name,o=null==n?void 0:n.service_name;return(0,l.jsxs)(r.Fragment,{children:[(0,l.jsx)(a.Z,{title:"".concat((0,t.Z)(o)," Service"),description:(0,l.jsxs)(l.Fragment,{children:["Deployment ",(0,l.jsx)("code",{children:d})," > ",(0,l.jsx)("code",{children:o})]})}),(0,l.jsx)(c.Z,{}),(0,l.jsx)(m,{})]})}var f=s(89350),h=!0;function _(){return(0,l.jsx)(f.Z,{children:(0,l.jsx)(j,{})})}}},function(e){e.O(0,[662,75,664,433,7,827,910,464,888,774,179],function(){return e(e.s=89426)}),_N_E=e.O()}]);
@@ -0,0 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[329],{89426:function(e,n,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/services/[service_name]",function(){return s(52963)}])},52963:function(e,n,s){"use strict";s.r(n),s.d(n,{__N_SSP:function(){return h},default:function(){return _}});var l=s(85893),r=s(67294),i=s(5606),t=s(38249),c=s(60705),a=s(33028),d=s(60),o=s(94655),u=s(1765);function x(e){let{service:n}=e,{pageProps:s}=(0,r.useContext)(i.AppContext),t=n.servers,[c,a]=(0,r.useState)(null==t?void 0:t[0]),x=null!=t&&!!t.length&&!!(t.length%2);return(0,l.jsx)(d.Z,{className:"w-full grid-cell col-span-1 gap-0",children:(0,l.jsxs)(o.Z,{className:"nested-grid-frame xl:grid-cols-2",children:[null==t?void 0:t.map((e,s)=>(0,l.jsx)(u.Z,{server:e,service:n,wrapperProps:{className:"grid-cell"}},s)),x?(0,l.jsx)("div",{className:"grid-cell"}):null]})})}var v=s(39483),p=s(56211);function m(){let{pageProps:e,ws:n}=(0,r.useContext)(i.AppContext),{service:s,deployment:t,children_services:c}=e,a=[s,...c||[]],[u,m]=(0,r.useState)(s||void 0),j=a.findIndex(e=>(null==e?void 0:e.service_name)==(null==u?void 0:u.service_name))+1;return null==u||u.servers,(0,l.jsx)(d.Z,{className:"w-full nested-grid-frame grid-cols-1",children:(0,l.jsxs)(d.Z,{className:"grid-cell gap-0",children:[(0,l.jsx)(o.Z,{className:"grid-cell-content flex-nowrap",children:(0,l.jsxs)(o.Z,{className:"w-full justify-between",children:[(0,l.jsx)(o.Z,{children:(0,l.jsxs)(p.Z,{children:["Cluster #",j]})}),(0,l.jsx)(o.Z,{children:(0,l.jsx)(v.Z,{options:a.map((e,n)=>({value:null==e?void 0:e.service_name,title:"Cluster #".concat(n+1)})),wrapperWrapperProps:{className:"max-w-[250px]"},changeHandler:e=>{m(void 0),n.sendData({event:"client:kill-all-ports"}),setTimeout(()=>{m(a.find(n=>(null==n?void 0:n.service_name)==e)||void 0)},200)}})})]})}),(0,l.jsx)("hr",{}),u?(0,l.jsx)(x,{service:u}):void 0]})})}function j(){let{pageProps:e}=(0,r.useContext)(i.AppContext),{service:n,deployment:s}=e,d=null==s?void 0:s.deployment_name,o=null==n?void 0:n.service_name;return(0,l.jsxs)(r.Fragment,{children:[(0,l.jsx)(a.Z,{title:"".concat((0,t.Z)(o)," Service"),description:(0,l.jsxs)(l.Fragment,{children:["Deployment ",(0,l.jsx)("code",{children:d})," > ",(0,l.jsx)("code",{children:o})]})}),(0,l.jsx)(c.Z,{}),(0,l.jsx)(m,{})]})}var f=s(89350),h=!0;function _(){return(0,l.jsx)(f.Z,{children:(0,l.jsx)(j,{})})}}},function(e){e.O(0,[662,75,664,433,7,827,910,926,888,774,179],function(){return e(e.s=89426)}),_N_E=e.O()}]);
@@ -0,0 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[890],{25292:function(e,l,s){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/shell",function(){return s(30904)}])},40806:function(e,l,s){"use strict";s.d(l,{Z:function(){return h}});var n=s(85893),r=s(93452),t=s(76242),i=s(36969),a=s(25808),c=s(94495),u=s(57493),o=s(94655),d=s(3668),f=s(60),p=s(67294),m=s(98388);function h(e){let{url:l,wrapperProps:s,title:h,...x}=e,{loading:j,setLoading:g}=(0,a.Z)();return(0,n.jsx)(r.Z,{...s,className:(0,m.m6)("p-0",null==s?void 0:s.className),children:(0,n.jsxs)(f.Z,{className:"gap-0",children:[(0,n.jsxs)(o.Z,{className:"p-4 w-full justify-between",children:[(0,n.jsx)(o.Z,{children:h?(0,n.jsx)(p.Fragment,{children:(0,n.jsx)(d.Z,{size:"small",variant:"faded",children:h})}):null}),(0,n.jsxs)(o.Z,{children:[(0,n.jsx)(c.Z,{title:"Open Full Screen",variant:"ghost",className:"p-1!",href:l,target:"_blank",children:(0,n.jsx)(i.Z,{name:"ArrowUpRight",size:20})}),(0,n.jsx)(c.Z,{title:"Refresh Iframe",variant:"ghost",className:"p-1!",loading:j,onClick:()=>{g(!0),setTimeout(()=>{g(!1)},2e3)},loadingProps:{size:"smaller"},children:(0,n.jsx)(i.Z,{name:"RotateCcw",size:18})})]})]}),(0,n.jsx)("hr",{}),j?(0,n.jsx)(u.Z,{className:"w-full p-10 h-[400px]",children:(0,n.jsx)(t.Z,{})}):(0,n.jsx)("iframe",{...x,src:l,className:(0,m.m6)("w-full h-[400px]",x.className)})]})})}},30904:function(e,l,s){"use strict";s.r(l),s.d(l,{__N_SSP:function(){return g},default:function(){return v}});var n=s(85893),r=s(67294),t=s(5606),i=s(60705),a=s(33028),c=s(60),u=s(71077),o=s(76242),d=s(57493),f=s(40806),p=s(25808);function m(e){let{paradigm:l}=e,{pageProps:s,ws:i}=(0,r.useContext)(t.AppContext),{user:a}=s,m=a.super_admin?"root":a.username,h="pm2-logs"==l?"pm2 logs":a.super_admin?"bash":m?"su - ".concat(m,' -c "bash"'):void 0,{data:x}=(0,u.Z)(),[j,g]=(0,r.useState)(),{refresh:v,setRefresh:Z}=(0,p.Z)(),N=(0,r.useRef)(!1);function w(){(null==j?void 0:j.port)&&i.sendData({event:"client:kill-port",port:j.port})}return(0,r.useEffect)(()=>{if((null==i?void 0:i.socket)&&!N.current)return i.sendData({event:"pm2-logs"==l?"client:relay-pm2-logs":"client:relay-shell",cmd:h}),N.current=!0,function(){w()}},[i,v]),(0,r.useEffect)(()=>{N.current&&(w(),g(void 0),N.current=!1,Z(e=>e+1))},[]),(0,r.useEffect)(()=>{!j&&("shell"==l&&(null==x?void 0:x.event)=="server:relay-shell"&&(null==x?void 0:x.ttyd)||"pm2-logs"==l&&(null==x?void 0:x.event)=="server:relay-pm2-logs"&&(null==x?void 0:x.ttyd))&&setTimeout(()=>{g(x.ttyd?{...x.ttyd,href:"".concat(window.location.origin).concat(x.ttyd.url)}:void 0)},2e3)},[x]),(0,n.jsxs)(c.Z,{className:"gap-0 w-full",children:[(null==j?void 0:j.url)&&j.port?(0,n.jsx)(c.Z,{className:"gap-0",children:(0,n.jsx)(f.Z,{url:null==j?void 0:j.url,title:"pm2-logs"==l?"Admin Logs":"Relay Shell",wrapperProps:{className:"border-none"}})}):(0,n.jsx)(d.Z,{className:"p-10 h-[460px]",children:(0,n.jsx)(o.Z,{})}),(0,n.jsx)("hr",{})]})}function h(){let{pageProps:e}=(0,r.useContext)(t.AppContext),{user:l}=e;return(0,n.jsxs)(c.Z,{className:"w-full nested-grid-frame xl:grid-cols-2",children:[(0,n.jsx)(c.Z,{className:"grid-cell w-full",children:(0,n.jsx)(m,{paradigm:"shell"})}),l.super_admin?(0,n.jsx)(c.Z,{className:"grid-cell w-full",children:(0,n.jsx)(m,{paradigm:"pm2-logs"})}):null]})}function x(){let{pageProps:e}=(0,r.useContext)(t.AppContext);return(0,n.jsxs)(r.Fragment,{children:[(0,n.jsx)(a.Z,{title:"Shell",description:(0,n.jsx)(n.Fragment,{children:"Access the relay server shell"})}),(0,n.jsx)(i.Z,{}),(0,n.jsx)(h,{})]})}var j=s(89350),g=!0;function v(){return(0,n.jsx)(j.Z,{children:(0,n.jsx)(x,{})})}},93452:function(e,l,s){"use strict";s.d(l,{Z:function(){return t}});var n=s(85893),r=s(98388);function t(e){let{spacing:l,componentRef:s,...t}=e;return(0,n.jsx)("div",{...t,className:(0,r.m6)("relative flex items-center gap-2 border border-solid rounded-default","border-slate-200 dark:border-white/10",l?"normal"==l?"px-3 py-2":"tight"==l?"px-2 py-1":"":"px-3 py-2","twui-border",t.className),ref:s,children:t.children})}},57493:function(e,l,s){"use strict";s.d(l,{Z:function(){return t}});var n=s(85893),r=s(98388);function t(e){let{...l}=e;return(0,n.jsx)("div",{...l,className:(0,r.m6)("flex flex-col items-center justify-center gap-4 p-2 w-full","h-full twui-center",l.className),children:l.children})}},9008:function(e,l,s){e.exports=s(23867)}},function(e){e.O(0,[662,75,664,433,7,910,888,774,179],function(){return e(e.s=25292)}),_N_E=e.O()}]);
@@ -1 +0,0 @@
self.__BUILD_MANIFEST=function(s,a,e,c,t,i,n,d,u){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[s,e,a,c,u,t,i,"static/chunks/pages/index-13e31ea96205b999.js"],"/404":[s,a,"static/chunks/pages/404-8292e1046538596b.js"],"/_error":["static/chunks/pages/_error-10090a8ee211d5fd.js"],"/admin":[s,e,a,c,t,i,n,d,"static/chunks/pages/admin-c975090d9a91c7e1.js"],"/admin/services":[s,e,a,c,t,i,n,d,"static/chunks/pages/admin/services-68efb2b49e62c913.js"],"/admin/services/[service_name]":[s,e,a,c,t,i,n,d,"static/chunks/pages/admin/services/[service_name]-0049c2898ea64cc0.js"],"/admin/settings":[s,e,a,c,t,i,n,"static/chunks/pages/admin/settings-f58d63b2752f72a6.js"],"/admin/users":[s,e,a,c,t,n,"static/chunks/pages/admin/users-73e9775838b56d95.js"],"/admin/users/add-user":[s,e,a,c,t,i,n,"static/chunks/pages/admin/users/add-user-1db7c9ee89830b5b.js"],"/admin/users/[deployment_user_id]":[s,e,"static/chunks/ff39441c-88f593a46cb65964.js","static/chunks/d848df63-a470d32e7be4bfe3.js",a,c,"static/chunks/281-dcee0c6401936d5e.js",t,i,n,"static/chunks/pages/admin/users/[deployment_user_id]-fb1c3c9148c518cd.js"],"/auth/login":[s,e,a,c,u,t,i,"static/chunks/pages/auth/login-3f936f997e58438c.js"],"/auth/logout":[s,a,"static/chunks/pages/auth/logout-c8b207f96be4f684.js"],"/auth/signup":[s,e,a,c,t,i,"static/chunks/pages/auth/signup-5c0f6835749e755e.js"],sortedPages:["/","/404","/_app","/_error","/admin","/admin/services","/admin/services/[service_name]","/admin/settings","/admin/users","/admin/users/add-user","/admin/users/[deployment_user_id]","/auth/login","/auth/logout","/auth/signup"]}}("static/chunks/29107295-2819ba6e51dbf961.js","static/chunks/664-e347d64e56042a02.js","static/chunks/e5d67320-bf1772fa71739690.js","static/chunks/433-bb8bdacebff9ab53.js","static/chunks/7-f0121001f735b8fe.js","static/chunks/827-7975edcf8481ec69.js","static/chunks/910-215ca9612c9e2400.js","static/chunks/464-eda76a320462e5de.js","static/chunks/225-f257bb5bd6014d68.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
+3 -3
View File
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

+2 -3
View File
@@ -72,9 +72,8 @@ export default function TtydIframe({
title="Open Full Screen"
variant="ghost"
className="p-1!"
onClick={() => {
window.open(url, "__blank");
}}
href={url}
target="_blank"
>
<LucideIcon name="ArrowUpRight" size={20} />
</Button>
@@ -10,12 +10,16 @@ export default function AdminSummary() {
if (!deployment) return null;
const total_services = deployment.services.filter(
(srv) => !srv.parent_service_name,
);
return (
<Stack className="w-full gap-0">
<Stack className="grid-cell-content">
<Span>{deployment.services.length} Services</Span>
<Span>{total_services.length} Services</Span>
<Row>
{deployment.services.map((service, index) => {
{total_services.map((service, index) => {
return (
<a
key={index}
@@ -2,18 +2,15 @@ import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
} from "@/src/types/turboci";
import ArrowedLink from "@/twui/components/layout/ArrowedLink";
import Button from "@/twui/components/layout/Button";
import H2 from "@/twui/components/layout/H2";
import Row from "@/twui/components/layout/Row";
import Stack from "@/twui/components/layout/Stack";
import { useContext, useEffect, useRef, useState } from "react";
import ServiceClusterServer from "../service/(partials)/cluster-server";
import { twMerge } from "tailwind-merge";
import Select from "@/twui/components/form/Select";
import useStatus from "@/twui/components/hooks/useStatus";
import Loading from "@/twui/components/elements/Loading";
import _ from "lodash";
type Props = {
@@ -119,32 +116,6 @@ export default function DeploymentService({ service }: Props) {
}}
/>
) : undefined}
{/* {cluster_servers?.map((server, index) => {
const is_active =
server?.private_ip == targetServer?.private_ip;
return (
<Button
title={`Cluster ${index + 1}`}
key={index}
color="gray"
variant={is_active ? undefined : "outlined"}
size="small"
onClick={() => {
setTargetServer(server);
}}
>
<span
className={twMerge(
is_active ? "font-semibold" : "",
)}
>
{server.private_ip}
</span>
</Button>
);
})} */}
</Row>
{ready ? (
@@ -1,5 +1,5 @@
import { Dispatch, Fragment, SetStateAction, useContext, useRef } from "react";
import { ParsedDeploymentServiceConfig } from "@/src/types";
import { ParsedDeploymentServiceConfig } from "@/src/types/turboci";
import Select, { TWUISelectOptionObject } from "@/twui/components/form/Select";
import Row from "@/twui/components/layout/Row";
import Button from "@/twui/components/layout/Button";
@@ -18,7 +18,20 @@ export default function ServiceClusterServerLogSelectorSelectLog({
setLog,
log,
}: Props) {
const logs = service.logs;
let logs = service.logs;
if (service.type == "load_balancer") {
if (!logs?.[0]) {
logs = [];
}
logs.push(
...[
`tail -f /var/log/nginx/access.log`,
`tail -f /var/log/nginx/error.log`,
],
);
}
const log_strings = logs?.map((l) => (typeof l == "string" ? l : l.cmd));
const is_custom_log =
@@ -3,7 +3,7 @@ import { Dispatch, SetStateAction } from "react";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
} from "@/src/types/turboci";
import Row from "@/twui/components/layout/Row";
import ServiceClusterServerLogSelectorSetCustomLog from "./cluster-server-log-selector-set-custom-log";
import ServiceClusterServerLogSelectorSelectLog from "./cluster-server-log-selector-select-log";
@@ -2,8 +2,6 @@ import Stack from "@/twui/components/layout/Stack";
import { RefObject, useContext, useEffect, useRef, useState } from "react";
import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
ServerTerminalTargets,
TtydInfoObject,
WebSocketDataType,
@@ -15,6 +13,10 @@ import Center from "@/twui/components/layout/Center";
import TtydIframe from "@/src/components/general/ttyd-iframe";
import useIntersectionObserver from "@/twui/components/hooks/useIntersectionObserver";
import useStatus from "@/twui/components/hooks/useStatus";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types/turboci";
type Props = {
service: ParsedDeploymentServiceConfig;
@@ -4,8 +4,7 @@ import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
ServerTerminalTargets,
} from "@/src/types";
} from "@/src/types/turboci";
import useIntersectionObserver from "@/twui/components/hooks/useIntersectionObserver";
import Center from "@/twui/components/layout/Center";
import Loading from "@/twui/components/elements/Loading";
@@ -14,6 +13,7 @@ import Row from "@/twui/components/layout/Row";
import Button from "@/twui/components/layout/Button";
import ServiceClusterServerLogSelector from "./cluster-server-log-selector";
import { twMerge } from "tailwind-merge";
import { ServerTerminalTargets } from "@/src/types";
type Props = {
service: ParsedDeploymentServiceConfig;
@@ -33,11 +33,26 @@ export default function ServiceClusterServer({
const elementRef = useRef<HTMLDivElement>(undefined);
const { isIntersecting } = useIntersectionObserver({ elementRef });
let service_logs = service.logs;
if (service.type == "load_balancer") {
if (!service_logs?.[0]) {
service_logs = [];
}
service_logs.push(
...[
`tail -f /var/log/nginx/access.log`,
`tail -f /var/log/nginx/error.log`,
],
);
}
const [target, setTarget] = useState<
(typeof ServerTerminalTargets)[number]["name"]
>(service.logs?.[0] ? "logs" : "shell");
>(service_logs?.[0] ? "logs" : "shell");
const first_log = service.logs?.[0];
const first_log = service_logs?.[0];
const intial_logs =
typeof first_log == "string" ? first_log : first_log?.cmd;
@@ -51,6 +66,7 @@ export default function ServiceClusterServer({
<Row className="w-full justify-between p-4 -mb-6">
<Row>
<code>{server.private_ip}</code>
{server.public_ip ? <code>{server.public_ip}</code> : null}
</Row>
<Row className="">
@@ -4,7 +4,7 @@ import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
} from "@/src/types/turboci";
import Row from "@/twui/components/layout/Row";
import ServiceClusterServer from "./cluster-server";
@@ -2,15 +2,9 @@ import Stack from "@/twui/components/layout/Stack";
import { useContext, useState } from "react";
import { AppContext } from "@/src/pages/_app";
import ServiceCluster from "../(partials)/cluster";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
import Button from "@/twui/components/layout/Button";
import { ParsedDeploymentServiceConfig } from "@/src/types/turboci";
import Row from "@/twui/components/layout/Row";
import { twMerge } from "tailwind-merge";
import Select from "@/twui/components/form/Select";
import ServiceClusterServer from "../(partials)/cluster-server";
import H2 from "@/twui/components/layout/H2";
import _ from "lodash";
@@ -0,0 +1,122 @@
import Stack from "@/twui/components/layout/Stack";
import { useContext, useEffect, useRef, useState } from "react";
import { AppContext } from "@/src/pages/_app";
import { TtydInfoObject, WebSocketDataType } from "@/src/types";
import useWebSocketEventHandler from "@/twui/components/hooks/useWebSocketEventHandler";
import _ from "lodash";
import Loading from "@/twui/components/elements/Loading";
import Center from "@/twui/components/layout/Center";
import TtydIframe from "@/src/components/general/ttyd-iframe";
import useStatus from "@/twui/components/hooks/useStatus";
type Props = {
paradigm: "shell" | "pm2-logs";
};
export default function RelayShellView({ paradigm }: Props) {
const { pageProps, ws } = useContext(AppContext);
const { user } = pageProps;
const username = user.super_admin ? "root" : user.username;
const cmd =
paradigm == "pm2-logs"
? "pm2 logs"
: user.super_admin
? "bash"
: username
? `su - ${username} -c "bash"`
: undefined;
const { data } = useWebSocketEventHandler<WebSocketDataType>();
const [ttyd, setTtyd] = useState<TtydInfoObject>();
const { refresh, setRefresh } = useStatus();
const WsReqSentRef = useRef(false);
function sendKillPort() {
if (ttyd?.port) {
ws.sendData({
event: "client:kill-port",
port: ttyd.port,
});
}
}
useEffect(() => {
if (!ws?.socket || WsReqSentRef.current) {
return;
}
ws.sendData({
event:
paradigm == "pm2-logs"
? "client:relay-pm2-logs"
: "client:relay-shell",
cmd,
});
WsReqSentRef.current = true;
return function () {
sendKillPort();
};
}, [ws, refresh]);
useEffect(() => {
if (WsReqSentRef.current) {
sendKillPort();
setTtyd(undefined);
WsReqSentRef.current = false;
setRefresh((prev) => prev + 1);
}
}, []);
useEffect(() => {
if (ttyd) return;
if (
(paradigm == "shell" &&
data?.event == "server:relay-shell" &&
data?.ttyd) ||
(paradigm == "pm2-logs" &&
data?.event == "server:relay-pm2-logs" &&
data?.ttyd)
) {
setTimeout(() => {
setTtyd(
data.ttyd
? {
...data.ttyd,
href: `${window.location.origin}${data.ttyd.url}`,
}
: undefined,
);
}, 2000);
}
}, [data]);
const title = paradigm == "pm2-logs" ? `Admin Logs` : `Relay Shell`;
return (
<Stack className="gap-0 w-full">
{ttyd?.url && ttyd.port ? (
<Stack className="gap-0">
<TtydIframe
url={ttyd?.url}
title={title}
wrapperProps={{
className: "border-none",
}}
/>
</Stack>
) : (
<Center className="p-10 h-[460px]">
<Loading />
</Center>
)}
<hr />
</Stack>
);
}
@@ -0,0 +1,22 @@
import { AppContext } from "@/src/pages/_app";
import Stack from "@/twui/components/layout/Stack";
import { useContext } from "react";
import RelayShellView from "../(partials)/shell-view";
export default function RelayShellViewsSection() {
const { pageProps } = useContext(AppContext);
const { user } = pageProps;
return (
<Stack className="w-full nested-grid-frame xl:grid-cols-2">
<Stack className="grid-cell w-full">
<RelayShellView paradigm="shell" />
</Stack>
{user.super_admin ? (
<Stack className="grid-cell w-full">
<RelayShellView paradigm="pm2-logs" />
</Stack>
) : null}
</Stack>
);
}
@@ -0,0 +1,20 @@
import { Fragment, useContext } from "react";
import { AppContext } from "@/src/pages/_app";
import Divider from "@/twui/components/layout/Divider";
import AdminHero from "@/src/components/general/admin/hero";
import RelayShellViewsSection from "./(sections)/shell-views";
export default function Main() {
const { pageProps } = useContext(AppContext);
return (
<Fragment>
<AdminHero
title={`Shell`}
description={<>Access the relay server shell</>}
/>
<Divider />
<RelayShellViewsSection />
</Fragment>
);
}
-77
View File
@@ -1,77 +0,0 @@
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
} from "@/src/types";
import execSSH from "@/src/utils/exec-ssh";
import serviceFlight from "@/src/utils/flight";
import grabTurboCiConfig from "@/src/utils/grab-turboci-config";
export default async function cronCheckServices() {
const config = grabTurboCiConfig();
for (let i = 0; i < config.services.length; i++) {
const service = config.services[i];
if (!service.servers) {
continue;
}
for (let srv = 0; srv < service.servers.length; srv++) {
const server = service.servers[srv];
if (service.healthcheck) {
const test = await healthcheck({ server, service });
if (!test) {
console.log(
`Server ${server.private_ip} down. Restarting ...`,
);
const MAX_RETRIES = 5;
let retries = 0;
while (retries < MAX_RETRIES) {
console.log(`Retryig #${retries + 1} ...`);
await serviceFlight({
deployment: config,
servers: [server],
service,
});
await Bun.sleep(4000);
const retest = await healthcheck({ server, service });
if (retest) {
break;
} else {
retries++;
}
}
}
}
}
}
}
async function healthcheck({
server,
service,
}: {
service: ParsedDeploymentServiceConfig;
server: NormalizedServerObject;
}) {
if (!service.healthcheck?.cmd || !server.private_ip) {
return false;
}
const res = await execSSH({
cmd: service.healthcheck.cmd,
ip: server.private_ip,
});
const test = Boolean(res?.match(service.healthcheck.test));
return test;
}
+122
View File
@@ -0,0 +1,122 @@
import {
ParsedDeploymentServiceConfig,
ResponseObject,
TCIGlobalConfig,
} from "@/src/types/turboci";
import bunGrabBulkSyncScripts from "@/src/utils/bun-grab-bulk-sync-script";
import serviceFlight from "@/src/utils/flight";
import grabGitRepoName from "@/src/utils/grab-git-repo-name";
import relayExecSSH from "@/src/utils/relay-exec-ssh";
import turboCIPkgrabDirNames from "@/src/utils/turboci-pkg-grab-dir-names";
import _ from "lodash";
import path from "path";
const { relayServerRsyncDir } = turboCIPkgrabDirNames();
type Params = {
service: ParsedDeploymentServiceConfig;
deployment: TCIGlobalConfig;
};
export default async function cronCheckServicesGit(
params: Params,
): Promise<ResponseObject> {
const { service, deployment } = params;
if (!service.git || !service.servers?.[0]) {
return { success: true };
}
const servers_private_ips = service.servers
.map((srv) => srv.private_ip)
.filter((ip) => Boolean(ip)) as string[];
const git_array = Array.isArray(service.git) ? service.git : [service.git];
for (let i = 0; i < git_array.length; i++) {
const service_git = git_array[i];
if (!service_git) continue;
if (!service_git.keep_updated) continue;
const git_url = service_git.repo_url;
const repo_name = grabGitRepoName({ git_url });
if (!repo_name) continue;
console.log(`Checking Service \`${service.service_name}\` git ...`);
const relay_dst = path.join(
relayServerRsyncDir,
service.service_name,
"git",
repo_name,
);
let git_pull_cmd = ``;
git_pull_cmd += `cd ${relay_dst}\n`;
git_pull_cmd += `git pull\n`;
const git_pull_check = await relayExecSSH({
cmd: git_pull_cmd,
log_error: true,
});
if (git_pull_check?.match(/Already up to date./i)) {
continue;
}
console.log(`Updating \`${service.service_name}\` git ...`);
let cmd = ``;
const src = relay_dst + "/";
const dst = (service_git.work_dir || "/turboci/app") + "/";
const sync_cmd = await relayExecSSH({
cmd: bunGrabBulkSyncScripts({
dst,
src,
private_server_ips: servers_private_ips,
parrallel: true,
relay_ignore: [".git"],
}),
bun: true,
return_cmd_only: true,
});
cmd += `${sync_cmd}\n`;
cmd += `echo "Git Setup Success!"\n`;
const res = await relayExecSSH({
cmd,
log_error: true,
});
if (!res?.match(/Git Setup Success/)) {
console.error(
`\`${service.service_name}\` service git prep failed!`,
);
continue;
}
const restart = await serviceFlight({
deployment,
servers: service.servers,
service,
});
if (restart) {
console.log(
`Service \`${service.service_name}\` new git version deployed successfully!`,
);
}
}
return {
success: true,
};
}
@@ -0,0 +1,71 @@
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
TCIGlobalConfig,
} from "@/src/types/turboci";
import execSSH from "@/src/utils/exec-ssh";
import serviceFlight from "@/src/utils/flight";
type Params = {
service: ParsedDeploymentServiceConfig;
server: NormalizedServerObject;
config: TCIGlobalConfig;
};
export default async function cronCheckServicesHealtcheck({
config,
server,
service,
}: Params) {
if (service.healthcheck) {
const test = await healthcheck({ server, service });
if (!test) {
console.log(`Server ${server.private_ip} down. Restarting ...`);
const MAX_RETRIES = 5;
let retries = 0;
while (retries < MAX_RETRIES) {
console.log(`Retryig #${retries + 1} ...`);
await serviceFlight({
deployment: config,
servers: [server],
service,
});
await Bun.sleep(4000);
const retest = await healthcheck({ server, service });
if (retest) {
break;
} else {
retries++;
}
}
}
}
}
async function healthcheck({
server,
service,
}: {
service: ParsedDeploymentServiceConfig;
server: NormalizedServerObject;
}) {
if (!service.healthcheck?.cmd || !server.private_ip) {
return false;
}
const res = await execSSH({
cmd: service.healthcheck.cmd,
ip: server.private_ip,
});
const test = Boolean(res?.match(service.healthcheck.test));
return test;
}
@@ -0,0 +1,22 @@
import grabTurboCiConfig from "@/src/utils/grab-turboci-config";
import cronCheckServicesHealtcheck from "./healthcheck";
import cronCheckServicesGit from "./git";
export default async function cronCheckServices() {
const config = grabTurboCiConfig();
for (let i = 0; i < config.services.length; i++) {
const service = config.services[i];
if (!service.servers) {
continue;
}
await cronCheckServicesGit({ deployment: config, service });
for (let srv = 0; srv < service.servers.length; srv++) {
const server = service.servers[srv];
await cronCheckServicesHealtcheck({ config, server, service });
}
}
}
+51
View File
@@ -0,0 +1,51 @@
import {
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
NSQLiteTables,
} from "@/src/db/types";
import { TtydInfoObject, TtydParams, User } from "@/src/types";
import getNextAvailablePort from "@/src/utils/grab-next-available-port";
import grabTtydCmd from "@/src/utils/grab-ttyd-cmd";
import grabConnectedWebsocketUserdata from "@/src/websocket/(utils)/grab-connected-websocket-user-data";
import BunSQLite from "@moduletrace/bun-sqlite";
import { exec } from "child_process";
type Params = {
user: User;
cmd: string;
ttyd_options?: Omit<TtydParams, "cmd" | "port">;
};
export default async function grabTtydInfo({
user,
cmd,
ttyd_options,
}: Params): Promise<TtydInfoObject> {
const available_port = await getNextAvailablePort();
let url = `/ttyd/${available_port}`;
const ttyd_cmd = grabTtydCmd({
cmd,
port: available_port,
...ttyd_options,
});
const ttyd_exec = exec(ttyd_cmd.cmd);
await Bun.sleep(2000);
const connected_user_data = grabConnectedWebsocketUserdata({ user });
await BunSQLite.insert<
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
(typeof NSQLiteTables)[number]
>({
data: [{ user_id: user.id, port: available_port }],
table: "users_ports",
});
connected_user_data.child_processes.push(ttyd_exec);
connected_user_data.ports.push(available_port);
return { port: available_port, url };
}
+4 -48
View File
@@ -1,23 +1,10 @@
import {
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
NSQLiteTables,
} from "@/src/db/types";
import { PrivateServerTtydParadigms, TtydInfoObject, User } from "@/src/types";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
PrivateServerTtydParadigms,
TtydInfoObject,
User,
} from "@/src/types";
import grabDirNames from "@/src/utils/grab-dir-names";
import getNextAvailablePort from "@/src/utils/grab-next-available-port";
} from "@/src/types/turboci";
import grabSSHPrefix from "@/src/utils/grab-ssh-prefix";
import grabTtydCmd from "@/src/utils/grab-ttyd-cmd";
import grabConnectedWebsocketUserdata from "@/src/websocket/(utils)/grab-connected-websocket-user-data";
import BunSQLite from "@moduletrace/bun-sqlite";
import { exec } from "child_process";
const { TURBOCI_SSH_KEY_FILE } = grabDirNames();
import grabTtydInfo from "./grab-ttyd-info";
type Params = {
user: User;
@@ -46,46 +33,15 @@ export default async function grabTtydServerInfo({
);
}
const available_port = getNextAvailablePort();
let url = `/ttyd/${available_port}`;
let cmd = ``;
cmd += ` ${grabSSHPrefix()}`;
// if (paradigm == "logs") {
// } else {
// cmd += ` ssh -i ${TURBOCI_SSH_KEY_FILE}`;
// }
cmd += ` root@${server?.private_ip}`;
if (paradigm == "logs" && final_log) {
cmd += ` ${final_log}`;
}
const ttyd_cmd = grabTtydCmd({
cmd,
port: available_port,
});
const ttyd_exec = exec(ttyd_cmd.cmd);
await Bun.sleep(2000);
const connected_user_data = grabConnectedWebsocketUserdata({ user });
await BunSQLite.insert<
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
(typeof NSQLiteTables)[number]
>({
data: [{ user_id: user.id, port: available_port }],
table: "users_ports",
});
connected_user_data.child_processes.push(ttyd_exec);
connected_user_data.ports.push(available_port);
return { port: available_port, url };
return await grabTtydInfo({ cmd, user });
}
+9
View File
@@ -24,10 +24,19 @@ export function AdminAsideLinks({ user }: Params) {
url: "/admin/users",
}
: undefined,
{
title: "Shell",
url: "/admin/shell",
},
{
title: "Settings",
url: "/admin/settings",
},
{ divider: true },
{
title: "Logout",
url: "/auth/logout",
},
];
return links;
+18
View File
@@ -0,0 +1,18 @@
import Main from "@/src/components/pages/admin/shell";
import defaultAdminProps from "@/src/functions/pages/admin/default-admin-props";
import Layout from "@/src/layouts/admin";
import { GetServerSideProps } from "next";
export default function AdminDeploymentUsers() {
return (
<Layout>
<Main />
</Layout>
);
}
export const getServerSideProps: GetServerSideProps = async (ctx) => {
return await defaultAdminProps({
ctx,
});
};
+3 -3
View File
@@ -6,9 +6,9 @@ rm -rf /app/.next
bun install
npm rebuild better-sqlite3
bun twui:add || echo "TWUI Already Added"
bun twui:init || echo "TWUI Already Initialized"
bun twui:update
git submodule add https://git.tben.me/Moduletrace/tailwind-ui-library.git twui || echo "TWUI Already Added"
git submodule update --init twui || echo "TWUI Already Initialized"
git submodule update --remote
bunx nsqlite schema -t
+16 -167
View File
@@ -4,178 +4,16 @@ import useAppInit from "../hooks/use-app-init";
import { ServerWebSocket } from "bun";
import { ChildProcess } from "child_process";
import { NSQLITE_TURBOCI_ADMIN_USERS } from "../db/types";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
TCIGlobalConfig,
} from "./turboci";
export type User = DATASQUIREL_LoggedInUser & {
super_admin?: boolean;
};
export const CloudProviders = [
{
title: "Hetzner",
value: "hetzner",
},
{
title: "Amazon Web Services",
value: "aws",
},
{
title: "Google Cloud Platform",
value: "gcp",
},
{
title: "Microsoft Azure",
value: "azure",
},
] as const;
export type ParsedDeploymentServiceConfig = TCIConfigServiceConfig & {
service_name: string;
parent_service_name?: string;
servers?: NormalizedServerObject[];
};
export type NormalizedServerObject = {
public_ip?: string;
private_ip?: string;
};
export type TCIConfig = {
deployment_name: string;
duplicate_deployment_name?: string;
description?: string;
location?: string;
availability_zone?: string;
provider: (typeof CloudProviders)[number]["value"];
services: TCIConfigService;
env?: { [k: string]: string };
env_file?: string;
pre_deployment?: TCIRunObj;
};
export type TCIGlobalConfig = Omit<TCIConfig, "services"> & {
services: ParsedDeploymentServiceConfig[];
relay_server_ip?: string;
};
export type TCIConfigService = {
[k: string]: TCIConfigServiceConfig;
};
export const TCIServiceTypes = [
{
title: "Default Service",
value: "default",
},
{
title: "Docker",
value: "docker",
},
{
title: "Load Balancer",
value: "load_balancer",
},
] as const;
export const TCIServiceOS = [
{
title: "Debian 12 Bookworm",
value: "debian_12",
},
{
title: "Debian 13 Buster",
value: "debian_13",
},
{
title: "Ubuntu 23.0.4",
value: "ubuntu_23_0_4",
},
] as const;
export const TCIServiceDependecyTypes = [
{
title: "Debian APT",
value: "apt",
},
{
title: "Turbo CI",
value: "turboci",
},
] as const;
export type TCIConfigServiceConfig = {
type?: (typeof TCIServiceTypes)[number]["value"];
os?: string;
server_type?: string;
enable_public_ip?: boolean;
instances?: number;
clusters?: number;
dir_mappings?: TCIConfigServiceConfigDirMApping[];
dependencies?: {
[k in (typeof TCIServiceDependecyTypes)[number]["value"]]?: string[];
};
env?: { [k: string]: string };
env_file?: string;
target_services?: TCIConfigServiceConfigLBTarget[];
run?: TCIConfigServiceConfigRun;
ssl?: TCIConfigServiceSSL;
duplicate_service_name?: string;
healthcheck?: TCIConfigServiceHealthcheck;
/**
* Commoands to Run on first run
*/
init?: string[];
logs?: TCIConfigServiceConfigLog[];
};
export type TCIConfigServiceConfigLog =
| string
| {
cmd: string;
};
export type TCIConfigServiceHealthcheck = {
cmd: string;
test: string;
};
export type TCIConfigServiceDomain = {
domain_name: string;
};
export type TCIConfigServiceSSL = {
email: string;
};
export type TCIConfigServiceConfigLBTarget = {
service_name: string;
port: number;
weight?: number;
backup?: boolean;
domains?: (string | TCIConfigServiceDomain)[];
};
export type TCIConfigServiceConfigRun = {
preflight?: TCIRunObj;
start?: TCIRunObj;
postflight?: TCIRunObj;
work_dir?: string;
};
export type TCIRunObj = {
cmds?: string[];
work_dir?: string;
file?: string;
};
export type TCIConfigServiceConfigDirMApping = {
src: string;
dst: string;
ignore_file?: string;
ignore_patterns?: string[];
use_gitignore?: boolean;
relay_ignore?: string[];
};
export type ServiceScriptObject = {
sh: string;
service_name: string;
@@ -241,6 +79,8 @@ export const WebSocketEvents = [
"client:kill-port",
"client:kill-all-ports",
"client:service-server-shell",
"client:relay-shell",
"client:relay-pm2-logs",
"server:ping",
"server:error",
@@ -252,6 +92,8 @@ export const WebSocketEvents = [
"server:killed-port",
"server:killed-all-ports",
"server:service-server-shell",
"server:relay-shell",
"server:relay-pm2-logs",
] as const;
export type WebSocketDataType = {
@@ -309,3 +151,10 @@ export const ServerTerminalTargets = [
{ name: "logs" },
{ name: "shell" },
] as const;
export type TtydParams = {
cmd: string;
cwd?: string;
flags?: string[];
port: number;
};
+363
View File
@@ -0,0 +1,363 @@
import type { ExecSyncOptions } from "child_process";
export const TCICommands = [
{
name: "up",
description: "Deploy Stack",
},
{
name: "down",
description: "Destroy Stack",
},
] as const;
export interface PackageJson {
name?: string;
version?: string;
description?: string;
bin?: Record<string, string>;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
[key: string]: any;
}
export const CloudProviders = [
{
title: "Hetzner",
value: "hetzner",
},
{
title: "Amazon Web Services",
value: "aws",
},
{
title: "Google Cloud Platform",
value: "gcp",
},
{
title: "Microsoft Azure",
value: "azure",
},
] as const;
export type GrabConfigReturn = {
deployments: TCIGlobalConfig[];
envs?: string[];
};
export type TCIConfig =
| TCIConfigDeployment[]
| {
deployments: TCIConfigDeployment[];
envs?: string[];
};
export type TCIConfigDeployment = {
deployment_name: string;
duplicate_deployment_name?: string;
description?: string;
location?: string;
availability_zone?: string;
provider: (typeof CloudProviders)[number]["value"];
services: TCIConfigService;
env?: { [k: string]: string };
env_file?: string;
pre_deployment?: TCIRunObj;
relay_server_options?: TCIConfigRelayServerOptions;
};
export type TCIConfigRelayServerOptions = {
server_type?: string;
};
export type TCIGlobalConfig = Omit<TCIConfigDeployment, "services"> & {
services: ParsedDeploymentServiceConfig[];
relay_server_ip?: string;
};
export type TCIConfigService = {
[k: string]: TCIConfigServiceConfig;
};
export const TCIServiceTypes = [
{
title: "Default Service",
value: "default",
},
{
title: "Docker",
value: "docker",
},
{
title: "Load Balancer",
value: "load_balancer",
},
] as const;
export const TCIServiceOS = [
{
title: "Debian 12 Bookworm",
value: "debian_12",
},
{
title: "Debian 13 Buster",
value: "debian_13",
},
{
title: "Ubuntu 23.0.4",
value: "ubuntu_23_0_4",
},
] as const;
export const TCIServiceDependecyTypes = [
{
title: "Debian APT",
value: "apt",
},
{
title: "Turbo CI",
value: "turboci",
},
] as const;
export const TCIGitParadigms = [
{
title: "Github",
value: "github",
},
{
title: "Gitlab",
value: "gitlab",
},
{
title: "Gitea",
value: "gitea",
},
] as const;
export const TCIContainerRegistryParadigms = [
{
title: "Docker Hub",
value: "dockerhub",
},
{
title: "Github Container Registry",
value: "ghcr",
},
] as const;
export type TCIConfigServiceConfig = {
type?: (typeof TCIServiceTypes)[number]["value"];
os?: string;
server_type?: string;
enable_public_ip?: boolean;
instances?: number;
clusters?: number;
dir_mappings?: TCIConfigServiceConfigDirMApping[];
dependencies?: {
[k in (typeof TCIServiceDependecyTypes)[number]["value"]]?: string[];
};
env?: { [k: string]: string };
env_file?: string;
target_services?: TCIConfigServiceConfigLBTarget[];
run?: TCIConfigServiceConfigRun;
ssl?: TCIConfigServiceSSL;
duplicate_service_name?: string;
healthcheck?: TCIConfigServiceHealthcheck;
/**
* Commoands to Run on first run
*/
init?: string[];
logs?: TCIConfigServiceConfigLog[];
git?: TCIConfigServiceConfigGit | TCIConfigServiceConfigGit[];
};
export type TCIConfigServiceConfigGit = {
paradigm?: (typeof TCIGitParadigms)[number]["value"];
repo_url: string;
branch?: string;
/**
* Directory in target servers where the repo should
* live. Defaults to `/app`
*/
work_dir?: string;
public_repo?: boolean;
username?: string;
api_key?: string;
/**
* If true, this will continuosly pull from the git source
* and rerun the flight commands
*/
keep_updated?: boolean;
};
export type TCIConfigServiceConfigDocker = {
container_registry_paradigm?: (typeof TCIContainerRegistryParadigms)[number]["value"];
container_registry_url?: string;
compose?: { [k: string]: any };
/**
* Location of the dockerfile in the target servers
*/
docker_file_path?: { [k: string]: any };
/**
* Directory in target servers which is the docker
* reference for files like `docker-compose.yaml` or
* `Dockerfile`.
*/
work_dir?: string;
};
export type TCIConfigServiceConfigLog =
| string
| {
cmd: string;
};
export type TCIConfigServiceHealthcheck = {
cmd: string;
test: string;
};
export type TCIConfigServiceDomain = {
domain_name: string;
};
export type TCIConfigServiceSSL = {
email: string;
};
export type TCIConfigServiceConfigLBTarget = {
service_name: string;
port: number;
weight?: number;
backup?: boolean;
domains?: (string | TCIConfigServiceDomain)[];
};
export type TCIConfigServiceConfigRun = {
preflight?: TCIRunObj;
start?: TCIRunObj;
postflight?: TCIRunObj;
work_dir?: string;
};
export type TCIRunObj = {
cmds?: string[];
work_dir?: string;
file?: string;
};
export type TCIConfigServiceConfigDirMApping = {
src: string;
dst: string;
ignore_file?: string;
ignore_patterns?: string[];
use_gitignore?: boolean;
relay_ignore?: string[];
};
export type TCICommandOptions = {
file?: string;
};
export type TCIOptions = {
config?: TCIConfig;
};
export const TurboCIPreferedOS = ["debian", "ubuntu"] as const;
export const TurboCIOsPreferenceRegexp = new RegExp(
`${TurboCIPreferedOS.join("|")}`,
`i`,
);
export type SSHRelayServerReturn = {
ip: string;
private_ip: string;
};
export type NormalizedServerObject = {
public_ip?: string;
private_ip?: string;
};
export type SyncRemoteDirsParams = {
ip?: string;
ips?: string[];
user?: string;
src: string;
dst: string;
ignore_path?: string;
ignore_patterns?: string[];
use_gitignore?: boolean;
delete?: boolean;
debug?: boolean;
use_relay_server?: boolean;
deployment?: Omit<TCIConfigDeployment, "services">;
options?: ExecSyncOptions;
service?: TCIConfigServiceConfig;
service_name?: string;
relay_ignore?: string[];
};
export type DefaultPrepParams = {
service: ParsedDeploymentServiceConfig;
deployment: TCIGlobalConfig;
servers: NormalizedServerObject[];
};
export type ServiceScriptObject = {
sh: string;
service_name: string;
deployment_name: string;
work_dir?: string;
};
export type ParsedDeploymentServiceConfig = TCIConfigServiceConfig & {
service_name: string;
parent_service_name?: string;
servers?: NormalizedServerObject[];
};
export type DefaultDeploymentParams = {
service: ParsedDeploymentServiceConfig;
deployment: TCIGlobalConfig;
};
export type CommanderDefaultOptions = {
skip?: string[];
target?: string[];
};
export type TurbociControlServer = NormalizedServerObject & {
service_name?: "__relay" | (string & {});
deployment_name?: string;
};
export type TurbociControlReturn = {
servers?: TurbociControlServer[];
};
export const TurboCIDependencies = [
{
package_name: "bun",
},
{
package_name: "node",
},
{
package_name: "docker",
},
] as const;
export type DeploymentAndServicesToUpdate = {
deployment: TCIGlobalConfig;
services: ParsedDeploymentServiceConfig[];
skipped_services: ParsedDeploymentServiceConfig[];
};
export type ResponseObject = {
success: boolean;
msg?: string;
};
+86
View File
@@ -0,0 +1,86 @@
import { statSync } from "fs";
import _ from "lodash";
import path from "path";
import turboCIPkgrabDirNames from "./turboci-pkg-grab-dir-names";
import { AppData } from "../data/app-data";
type Params = {
private_server_ips: string[];
parrallel?: boolean;
/**
* Source on the relay server
*/
src: string;
/**
* Destination on private servers
*/
dst: string;
relay_ignore?: string[];
};
export default function bunGrabBulkSyncScripts({
private_server_ips,
parrallel,
src,
dst,
relay_ignore,
}: Params) {
const { relayServerSshPrivateKeyFile } = turboCIPkgrabDirNames();
// const srcStats = statSync(src);
// const isSrcFile = srcStats.isFile();
const dst_dir = src.match(/\.{1,5}$/)
? path.dirname(dst)
: path.normalize(dst);
let bunCmd = "";
bunCmd += `import _ from "lodash";\n`;
bunCmd += `import { execSync } from "child_process";\n`;
bunCmd += `\n`;
bunCmd += `const SSH_KEY = "${relayServerSshPrivateKeyFile}";\n`;
bunCmd += `const SSH_OPTS = \`-i \${SSH_KEY} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -C -c aes128-ctr\`;\n`;
bunCmd += `const REMOTE_HOSTS = [${private_server_ips
.map((h) => `"${h.replace(/\"/g, "")}"`)
.join(", ")}];\n`;
bunCmd += `const DEFAULT_SSH_USER = "root";\n`;
bunCmd += `const BATCH_SIZE = ${AppData["private_server_batch_exec_size"]};\n`;
bunCmd += `\n`;
bunCmd += `async function run(host: string) {\n`;
bunCmd += ` let execCmd = \`ssh \${SSH_OPTS} \${DEFAULT_SSH_USER}@\${host} mkdir -p ${dst_dir}\`;\n`;
bunCmd += ` execCmd += \` && rsync -avz -e 'ssh \${SSH_OPTS}' --delete\`;\n`;
if (relay_ignore) {
bunCmd += relay_ignore
.map((patt) => ` execCmd += \` --exclude='${patt}'\`;\n`)
.join("");
}
bunCmd += ` execCmd += \` ${src} \${DEFAULT_SSH_USER}@\${host}:${dst}\`;\n`;
bunCmd += ` try {\n`;
bunCmd += ` execSync(execCmd);\n`;
bunCmd += ` console.log("Sync Success!");\n`;
bunCmd += ` } catch (error) {\n`;
bunCmd += ` process.exit(1);\n`;
bunCmd += ` }\n`;
bunCmd += `}\n`;
bunCmd += `\n`;
if (parrallel) {
bunCmd += `const first_host = REMOTE_HOSTS.splice(0,1)[0];\n`;
bunCmd += `await run(first_host)\n`;
bunCmd += `\n`;
bunCmd += `const chunks = _.chunk(REMOTE_HOSTS, BATCH_SIZE);\n`;
bunCmd += `for (let i = 0; i < chunks.length; i++) {\n`;
bunCmd += ` const chunk = chunks[i];\n`;
bunCmd += ` const runChunk = await Promise.all(chunk.map(h => run(h)));\n`;
bunCmd += `}\n`;
} else {
bunCmd += `for (let i = 0; i < REMOTE_HOSTS.length; i++) {\n`;
bunCmd += ` const host = REMOTE_HOSTS[i];\n`;
bunCmd += ` const runHost = await run(host);\n`;
bunCmd += `}\n`;
}
return bunCmd;
}
+1 -1
View File
@@ -6,7 +6,7 @@ import {
ParsedDeploymentServiceConfig,
ServiceScriptObject,
TCIGlobalConfig,
} from "../types";
} from "@/src/types/turboci";
import { AppNames } from "./app-names";
import grabSHEnvs from "./grab-sh-env";
import bunGrabPrivateIPsBulkScripts from "./bun-grab-private-ips-bulk-scripts";
+12
View File
@@ -0,0 +1,12 @@
export default function grabGitRepoName({
git_url,
}: {
git_url: string;
}): string | undefined {
const git_arr = git_url.split("/");
const repo_name = git_arr.pop()?.replace(/\.git$/, "");
const repo_user_name = git_arr.pop();
return `${repo_user_name}/${repo_name}`;
}
+16 -9
View File
@@ -25,19 +25,26 @@ function makeSockaddr(port: number): Buffer {
return buf;
}
export default function getNextAvailablePort(
export default async function getNextAvailablePort(
startPort = AppData["DynamicPortStart"],
maxPort = 65535,
): number {
for (let port = startPort; port <= maxPort; port++) {
const fd = libc.symbols.socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) continue;
): Promise<number> {
const MAX_RETRIES = 5;
let retries = 0;
const addr = makeSockaddr(port);
const result = libc.symbols.bind(fd, ptr(addr), addr.byteLength);
libc.symbols.close(fd);
while (retries < MAX_RETRIES) {
for (let port = startPort; port <= maxPort; port++) {
const fd = libc.symbols.socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) continue;
if (result === 0) return port;
const addr = makeSockaddr(port);
const result = libc.symbols.bind(fd, ptr(addr), addr.byteLength);
libc.symbols.close(fd);
if (result === 0) return port;
await Bun.sleep(2000);
}
}
throw new Error(`No available port found in range ${startPort}-${maxPort}`);
+4 -1
View File
@@ -1,6 +1,9 @@
import _ from "lodash";
import path from "path";
import { ParsedDeploymentServiceConfig, TCIGlobalConfig } from "../types";
import {
ParsedDeploymentServiceConfig,
TCIGlobalConfig,
} from "@/src/types/turboci";
import parseEnv from "./parse-env";
type Params = {

Some files were not shown because too many files have changed in this diff Show More