├── .eslintrc.json ├── public ├── quickstack-icon-dark.png ├── quick-stack-logo-light.png ├── quickstack-repo-heading.png └── template-icons │ ├── wordpress.png │ └── mongodb.svg ├── prisma └── migrations │ ├── 20241229143025_migration │ └── migration.sql │ ├── 20250324085100_delete_roleprojectpermission │ └── migration.sql │ ├── migration_lock.toml │ ├── 20241229131352_migration │ └── migration.sql │ ├── 20241121151959_migration │ └── migration.sql │ ├── 20241025085330_migration │ └── migration.sql │ ├── 20241202170526_migration │ └── migration.sql │ ├── 20241231161704_migration │ └── migration.sql │ ├── 20250107081600_migration │ └── migration.sql │ ├── 20241202160004_migration │ └── migration.sql │ ├── 20241223140802_migration │ └── migration.sql │ ├── 20250102145143_migration │ └── migration.sql │ ├── 20250307150516_migration │ └── migration.sql │ ├── 20241026155705_migration │ └── migration.sql │ ├── 20241106181617_migration │ └── migration.sql │ ├── 20241017064349_migration │ └── migration.sql │ ├── 20241028143028_migration │ └── migration.sql │ ├── 20251209144504_migration │ └── migration.sql │ ├── 20251219105325_add_storage_class_to_app_volume │ └── migration.sql │ ├── 20241024113318_migration │ └── migration.sql │ ├── 20241222123831_migration │ └── migration.sql │ ├── 20241025144850_migration │ └── migration.sql │ ├── 20241024084212_migration │ └── migration.sql │ ├── 20241107155300_migration │ └── migration.sql │ └── 20241021100307_migration │ └── migration.sql ├── github-assets └── app-settings-general.png ├── .devcontainer ├── devcontainer.env_example ├── docker.compose.yml └── devcontainer.json ├── src ├── shared │ ├── model │ │ ├── downloadable-app-logs.model.ts │ │ ├── project-extended.model.ts │ │ ├── totp.model.ts │ │ ├── event-info.model.ts │ │ ├── generated-zod │ │ │ ├── verificationtoken.ts │ │ │ ├── parameter.ts │ │ │ ├── index.ts │ │ │ ├── appport.ts │ │ │ ├── appports.ts │ │ │ ├── session.ts │ │ │ ├── appbasicauth.ts │ │ │ ├── appfilemount.ts │ │ │ ├── appdomain.ts │ │ │ ├── project.ts │ │ │ ├── s3target.ts │ │ │ ├── authenticator.ts │ │ │ ├── role.ts │ │ │ ├── appvolume.ts │ │ │ ├── volumebackup.ts │ │ │ ├── usergroup.ts │ │ │ ├── account.ts │ │ │ ├── roleapppermission.ts │ │ │ ├── roleprojectpermission.ts │ │ │ └── user.ts │ │ ├── env-edit.model.ts │ │ ├── network-policy.model.ts │ │ ├── traefik-ip-propagation.model.ts │ │ ├── volume-upload.model.ts │ │ ├── qs-public-ipv4-settings.model.ts │ │ ├── file-mount-edit.model.ts │ │ ├── app-monitoring-usage.model.ts │ │ ├── default-port.model.ts │ │ ├── app-volume-monitoring-usage.model.ts │ │ ├── system-backup-location-settings.model.ts │ │ ├── service.exception.model.ts │ │ ├── pods-info.model.ts │ │ ├── qs-letsencrypt-settings.model.ts │ │ ├── qs-settings.model.ts │ │ ├── volume-backup-extended.model.ts │ │ ├── update-password.model.ts │ │ ├── user-extended.model.ts │ │ ├── pods-resource-info.model.ts │ │ ├── registry-storage-location-settings.model.ts │ │ ├── basic-auth-edit.model.ts │ │ ├── domain-edit.model.ts │ │ ├── user-edit.model.ts │ │ ├── role-extended.model.ts.ts │ │ ├── terminal-setup-info.model.ts │ │ ├── backup-info.model.ts │ │ ├── app-rate-limits.model.ts │ │ ├── s3-target-edit.model.ts │ │ ├── node-resource.model.ts │ │ ├── build-job.ts │ │ ├── backup-volume-edit.model.ts │ │ ├── volume-edit.model.ts │ │ ├── form-validation-exception.model.ts │ │ ├── auth-form.ts │ │ ├── database-template-info.model.ts │ │ ├── app-extended.model.ts │ │ ├── deployment-info.model.ts │ │ ├── sim-session.model.ts │ │ ├── node-info.model.ts │ │ ├── server-action-error-return.model.ts │ │ ├── role-edit.model.ts │ │ └── app-source-info.model.ts │ ├── utils │ │ ├── date.utils.ts │ │ ├── stream.utils.ts │ │ ├── fancy-console.utils.ts │ │ ├── react-node.utils.ts │ │ ├── constants.ts │ │ └── domain-dns-provider.utils.ts │ └── templates │ │ └── all.templates.ts ├── middleware.ts ├── app │ ├── error │ │ └── page.tsx │ ├── unauthorized │ │ └── page.tsx │ ├── api │ │ ├── auth │ │ │ └── [...nextauth] │ │ │ │ └── route.ts │ │ ├── print-schedules-jobs │ │ │ └── route.ts │ │ ├── v1 │ │ │ └── webhook │ │ │ │ └── deploy │ │ │ │ └── route.ts │ │ ├── volume-data-download │ │ │ └── route.ts │ │ ├── logs-download │ │ │ └── route.ts │ │ └── build-logs │ │ │ └── route.ts │ ├── page.tsx │ ├── projects │ │ ├── projects-breadcrumbs.tsx │ │ ├── edit-project-dialog.tsx │ │ ├── actions.ts │ │ └── project-page.tsx │ ├── project │ │ ├── [projectId] │ │ │ ├── project-breadcrumbs.tsx │ │ │ ├── edit-app-dialog.tsx │ │ │ ├── create-project-actions.tsx │ │ │ ├── project-overview.tsx │ │ │ └── page.tsx │ │ └── app │ │ │ └── [appId] │ │ │ ├── app-breadcrumbs.tsx │ │ │ ├── environment │ │ │ └── actions.ts │ │ │ ├── credentials │ │ │ └── db-tools.tsx │ │ │ ├── overview │ │ │ ├── terminal-overlay.tsx │ │ │ ├── build-logs-overlay.tsx │ │ │ └── deployment-status-badge.tsx │ │ │ ├── page.tsx │ │ │ ├── layout.tsx │ │ │ └── actions.ts │ ├── auth │ │ └── page.tsx │ ├── sidebar.tsx │ ├── settings │ │ ├── server │ │ │ ├── server-settings-tabs.tsx │ │ │ └── hostname-check.tsx │ │ ├── profile │ │ │ ├── totp-settings.tsx │ │ │ └── page.tsx │ │ ├── s3-targets │ │ │ ├── page.tsx │ │ │ └── actions.ts │ │ └── cluster │ │ │ ├── actions.ts │ │ │ └── page.tsx │ ├── global-error.tsx │ ├── backups │ │ └── backups-table.tsx │ ├── monitoring │ │ └── actions.ts │ └── sidebar-logout-button.tsx ├── frontend │ ├── sockets │ │ └── sockets.ts │ ├── utils │ │ ├── utils.ts │ │ ├── nextjs-actions.utils.ts │ │ ├── format.utils.ts │ │ ├── form.utilts.ts │ │ └── toast.utils.ts │ └── hooks │ │ └── use-mobile.tsx ├── server │ ├── services │ │ ├── standalone-services │ │ │ ├── 00_info.md │ │ │ ├── maintenance.service.ts │ │ │ └── schedule.service.ts │ │ ├── hostname-dns-provider.service.ts │ │ ├── setup-services │ │ │ └── ingress-setup.service.ts │ │ └── namespace.service.ts │ ├── utils │ │ ├── env-var.utils.ts │ │ ├── command-executor.utils.ts │ │ └── cache-tag-generator.utils.ts │ └── adapter │ │ ├── ip-adress-finder.adapter.ts │ │ └── aws-s3.adapter.ts ├── components │ ├── custom │ │ ├── navigate-back.tsx │ │ ├── short-commit-hash.tsx │ │ ├── page-title.tsx │ │ ├── submit-button.tsx │ │ ├── bottom-bar-menu.tsx │ │ ├── text-link.tsx │ │ ├── pods-status-polling-provider.tsx │ │ ├── code.tsx │ │ ├── loading-alert-dialog-action.tsx │ │ ├── hint-box-url.tsx │ │ ├── form-label-with-question.tsx │ │ ├── confirm-dialog.tsx │ │ ├── copy-input-field.tsx │ │ └── logs-overlay.tsx │ ├── ui │ │ ├── full-loading-spinnter.tsx │ │ ├── skeleton.tsx │ │ ├── collapsible.tsx │ │ ├── spinner.tsx │ │ ├── loading-spinner.tsx │ │ ├── label.tsx │ │ ├── textarea.tsx │ │ ├── input.tsx │ │ ├── separator.tsx │ │ ├── sonner.tsx │ │ ├── checkbox.tsx │ │ ├── switch.tsx │ │ ├── tooltip.tsx │ │ ├── progress.tsx │ │ ├── hover-card.tsx │ │ ├── popover.tsx │ │ ├── avatar.tsx │ │ ├── alert.tsx │ │ ├── scroll-area.tsx │ │ └── column-toggle.tsx │ └── breadcrumbs-setter.tsx ├── socket-io.server.ts ├── __tests__ │ └── shared │ │ └── utils │ │ ├── stream.utils.test.ts │ │ └── date.utils.test.ts └── websocket.server.ts ├── postcss.config.mjs ├── .dockerignore ├── next.config.mjs ├── prisma.config.ts ├── components.json ├── tsconfig.server.json ├── .vscode ├── settings.json └── launch.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── tests.yml ├── tsconfig.json ├── .gitignore ├── additional-containers ├── mariadb-backup │ ├── Dockerfile.amd64 │ ├── Dockerfile.arm64 │ └── docker-compose.yml ├── mongodb-backup │ ├── Dockerfile.amd64 │ ├── Dockerfile.arm64 │ └── docker-compose.yml └── postgres-backup │ ├── Dockerfile.amd64 │ ├── Dockerfile.arm64 │ └── docker-compose.yml ├── setup └── reset-password.sh ├── fix-wrong-zod-imports.js └── Dockerfile /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /public/quickstack-icon-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/biersoeckli/QuickStack/HEAD/public/quickstack-icon-dark.png -------------------------------------------------------------------------------- /prisma/migrations/20241229143025_migration/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "App" ADD COLUMN "webhookId" TEXT; 3 | -------------------------------------------------------------------------------- /public/quick-stack-logo-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/biersoeckli/QuickStack/HEAD/public/quick-stack-logo-light.png -------------------------------------------------------------------------------- /public/quickstack-repo-heading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/biersoeckli/QuickStack/HEAD/public/quickstack-repo-heading.png -------------------------------------------------------------------------------- /public/template-icons/wordpress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/biersoeckli/QuickStack/HEAD/public/template-icons/wordpress.png -------------------------------------------------------------------------------- /github-assets/app-settings-general.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/biersoeckli/QuickStack/HEAD/github-assets/app-settings-general.png -------------------------------------------------------------------------------- /.devcontainer/devcontainer.env_example: -------------------------------------------------------------------------------- 1 | NEXTAUTH_SECRET=SOME_TOKEN_FOR_NEXTJS_AUTHENTICATION 2 | DATABASE_URL="file:/workspace/storage/db/data.db" -------------------------------------------------------------------------------- /src/shared/model/downloadable-app-logs.model.ts: -------------------------------------------------------------------------------- 1 | export interface DownloadableAppLogsModel { 2 | appId: string; 3 | date: Date; 4 | } 5 | -------------------------------------------------------------------------------- /prisma/migrations/20250324085100_delete_roleprojectpermission/migration.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM RoleProjectPermission; 2 | DELETE FROM RoleAppPermission; 3 | -------------------------------------------------------------------------------- /src/middleware.ts: -------------------------------------------------------------------------------- 1 | export { default } from "next-auth/middleware" 2 | 3 | export const config = { 4 | matcher: ["/"], 5 | exclude: ["/auth"], 6 | 7 | } -------------------------------------------------------------------------------- /src/app/error/page.tsx: -------------------------------------------------------------------------------- 1 | 2 | export default function ErrorPage() { 3 | 4 | return ( 5 |
{shortHash});
10 | };
11 |
12 |
--------------------------------------------------------------------------------
/src/components/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4 |
5 | const Collapsible = CollapsiblePrimitive.Root
6 |
7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8 |
9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10 |
11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
12 |
--------------------------------------------------------------------------------
/src/shared/model/basic-auth-edit.model.ts:
--------------------------------------------------------------------------------
1 | import { stringToNumber } from "@/shared/utils/zod.utils";
2 | import { z } from "zod";
3 |
4 | export const basicAuthEditZodModel = z.object({
5 | id: z.string().nullish(),
6 | username: z.string().trim().min(1),
7 | password: z.string().trim().min(1),
8 | appId: z.string().min(1),
9 | });
10 |
11 | export type BasicAuthEditModel = z.infer{subtitle}
12 |{children}
9 | {
10 | if (!copieable) return;
11 | navigator.clipboard.writeText(copieableValue || children || '');
12 | toast.success('Copied to clipboard');
13 | }}>
14 | {children}
15 |
16 | )
17 | }
--------------------------------------------------------------------------------
/src/shared/model/deployment-info.model.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | export const deploymentStatusEnumZod = z.union([
4 | z.literal('UNKNOWN'),
5 | z.literal('BUILDING'),
6 | z.literal('ERROR'),
7 | z.literal('DEPLOYED'),
8 | z.literal('DEPLOYING'),
9 | z.literal('SHUTDOWN'),
10 | z.literal('SHUTTING_DOWN'),
11 | ]);
12 |
13 | export const deploymentInfoZodModel = z.object({
14 | replicasetName: z.string().optional(),
15 | buildJobName: z.string().optional(),
16 | createdAt: z.date(),
17 | status: deploymentStatusEnumZod,
18 | gitCommit: z.string().optional(),
19 | deploymentId: z.string(),
20 | });
21 |
22 | export type DeploymentInfoModel = z.inferAbsprung zu {uri.hostname}
23 |{hint}
17 |Could not find app with id {appId}
20 | } 21 | const session = await isAuthorizedReadForApp(appId); 22 | const role = UserGroupUtils.getRolePermissionForApp(session, appId); 23 | const [app, s3Targets, volumeBackups, nodesInfo] = await Promise.all([ 24 | appService.getExtendedById(appId), 25 | s3TargetService.getAll(), 26 | volumeBackupService.getForApp(appId), 27 | clusterService.getNodeInfo() 28 | ]); 29 | 30 | return (<> 31 |Could not find app with id {appId}
19 | } 20 | const session = await isAuthorizedReadForApp(appId); 21 | const app = await appService.getExtendedById(appId); 22 | 23 | const showIngressWarning = app.appDomains.length > 0 && app.ingressNetworkPolicy !== 'ALLOW_ALL' && app.ingressNetworkPolicy !== 'INTERNET_ONLY'; 24 | 25 | return ( 26 |33 | An unexpected error occurred. Please check if your authorized for this action and try again. 34 |
35 |36 | Digest: {error.digest} 37 |
38 |Could not find project with id {projectId}
25 | } 26 | const project = await projectService.getById(projectId); 27 | const data = await appService.getAllAppsByProjectID(projectId); 28 | const relevantApps = data.filter((app) => 29 | UserGroupUtils.sessionHasReadAccessForApp(session, app.id)); 30 | 31 | return ( 32 |