Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"customizations": {
"vscode": {
"extensions": [
"Orta.vscode-jest",
"Prisma.prisma",
"pflannery.vscode-versionlens",
"github.vscode-github-actions",
Expand All @@ -20,10 +19,14 @@
"mhutchie.git-graph",
"donjayamanne.githistory",
"qwtel.sqlite-viewer",
"ms-kubernetes-tools.vscode-kubernetes-tools"
"ms-kubernetes-tools.vscode-kubernetes-tools",
"vitest.explorer"
]
}
},
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
},
"postStartCommand": "bash .devcontainer/start-k3s.sh",
"remoteUser": "node"
}
4 changes: 2 additions & 2 deletions .devcontainer/docker.compose.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
version: "3"
services:
vscode-container:
image: mcr.microsoft.com/devcontainers/typescript-node:22
command: /bin/sh -c "while sleep 1000; do :; done"
image: mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm
command: sleep infinity
volumes:
- ..:/workspace
- ~/.ssh:/home/node/.ssh
Expand Down
160 changes: 40 additions & 120 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,130 +9,13 @@ QuickStack is a self-hosted PaaS built with Next.js 14 (App Router) that manages
- **`src/server/`** - Backend services that interact with Kubernetes and database
- **`src/shared/`** - Shared models, utils, and Zod schemas (used by both frontend and server)

### Key Adapters (`src/server/adapter/`)
Adapters provide abstraction over external APIs:
- `kubernetes-api.adapter.ts` - Wraps `@kubernetes/client-node` APIs (`k3s.core`, `k3s.apps`, etc.)
- `db.client.ts` - Prisma singleton (`dataAccess.client`) with custom transaction helpers
- `longhorn-api.adapter.ts` - Longhorn storage API
- `aws-s3.adapter.ts` - S3-compatible storage operations

### Service Pattern
Services are singleton classes exported as default instances:
```typescript
class AppService {
async buildAndDeploy(appId: string) { /* ... */ }
}
const appService = new AppService();
export default appService;
```

**Standalone Services** (`src/server/services/standalone-services/`): Can run outside Next.js request context (e.g., at app startup, scheduled tasks). See `00_info.md` for details.

## Server Actions Pattern

All server actions use wrappers from `src/server/utils/action-wrapper.utils.ts`:

```typescript
// For form submissions with Zod validation
export const saveApp = async (data: AppModel) =>
saveFormAction(data, AppModelSchema, async (validated) => {
await appService.save(validated);
return new SuccessActionResult(undefined, 'App saved');
}) as Promise<ServerActionResult<any, void>>;

// For simple actions without form validation
export const deleteApp = async (id: string) =>
simpleAction(async () => {
await isAuthorizedWriteForApp(id); // Auth check
await appService.deleteById(id);
return new SuccessActionResult(undefined, 'App deleted');
});
```

### Authorization Helpers
- `getAuthUserSession()` - Requires authenticated user, redirects to `/auth` if not
- `getAdminUserSession()` - Requires admin role
- `isAuthorizedReadForApp(appId)` - Checks read permissions for specific app
- `isAuthorizedWriteForApp(appId)` - Checks write permissions for specific app
- `isAuthorizedForBackups()` - Checks backup permissions

## Database & Prisma

- **SQLite** database at `storage/db/data.db` (using `@prisma/adapter-better-sqlite3`)
- Schema: `prisma/schema.prisma`
- Zod schemas auto-generated to `src/shared/model/generated-zod/`
- After schema changes: **`yarn prisma-migrate`** (runs `prisma migrate dev` + fixes Zod imports via `fix-wrong-zod-imports.js`)
- Access via `dataAccess.client` for queries
- Supports transactions: `dataAccess.client.$transaction(async (tx) => { ... })`
- Custom batch update helpers: `dataAccess.updateManyItems()` and `dataAccess.updateManyItemsWithExistingTransaction()`

**Critical**: After Prisma schema changes, `yarn prisma-migrate` automatically fixes incorrect Zod imports that `zod-prisma` generator produces.

## Kubernetes Naming Conventions

Use `KubeObjectNameUtils` (`src/server/utils/kube-object-name.utils.ts`) for consistent k8s object names:
- `toProjectId(name)` → `proj-{name}-{hash}` (max 30 chars + prefix)
- `toAppId(name)` → `app-{name}-{hash}`
- `toJobName(appId)` → `build-{appId}`
- `toServiceName(appId)` → `svc-{appId}`
- `toPvcName(volumeId)` → `pvc-{volumeId}`
- `addRandomSuffix(str)` → `{str}-{8-char-hex}`

All names are snake_case → kebab-case, lowercased, with non-alphanumeric chars removed.

## Caching & Revalidation

Next.js `unstable_cache` with tag-based invalidation using `Tags` utility (`src/server/utils/cache-tag-generator.utils.ts`):

```typescript
// Reading with cache
await unstable_cache(
async () => dataAccess.client.app.findMany({ where: { projectId } }),
[Tags.apps(projectId)],
{ tags: [Tags.apps(projectId)] }
)();

// Invalidating after mutations
revalidateTag(Tags.apps(projectId));
revalidateTag(Tags.app(appId));
```

**Available Tags**: `users()`, `userGroups()`, `projects()`, `apps(projectId)`, `app(appId)`, `appBuilds(appId)`, `s3Targets()`, `volumeBackups()`, `parameter()`, `nodeInfos()`
See `backend-services.instructions.md` for service patterns, adapters, server actions, authorization, database/Prisma, k8s naming, and caching details.

## Frontend Patterns

### State Management
Zustand stores in `src/frontend/states/zustand.states.ts`:
- `useConfirmDialog()` - Promise-based confirmation dialogs
- `useInputDialog()` - Promise-based input dialogs
- `useBreadcrumbs()` - Page breadcrumb navigation

### UI Components
- **shadcn/ui** components in `src/components/ui/`
- Custom components in `src/components/custom/`
- Forms use `react-hook-form` with `@hookform/resolvers` and Zod schemas

### Real-time Communication
- **Socket.IO** (`src/socket-io.server.ts`): `/pod-terminal` namespace for terminal streaming
- **WebSocket** (`src/websocket.server.ts`): Generic WebSocket server for live pod logs

## Custom Server Entry Point

`src/server.ts` wraps Next.js to handle:
1. WebSocket/Socket.IO initialization
2. Database migration on production startup (`npx prisma migrate deploy`)
3. QuickStack initialization (`quickStackService.initializeQuickStack()`)
4. Standalone services (backups, maintenance, password changes, app logs)

Run with `yarn dev-live` (builds TypeScript from `tsconfig.server.json` → `dist/server.js`)
All frontend rules are stored in `frontend-ui-patterns.instructions.md`, covering all relevant UI patterns.

## Testing

- Jest with jsdom environment (`jest.config.ts`)
- Tests in `src/__tests__/{frontend,server,shared}/`
- Path alias `@/` maps to `src/`
- Run: `yarn test`
- Coverage: Collected automatically, output to `coverage/`
All testing rules are stored in `backend-testing.instructions.md`, covering Vitest patterns, mocking, integration tests, and naming conventions.

## Development Setup

Expand All @@ -150,3 +33,40 @@ Run with `yarn dev-live` (builds TypeScript from `tsconfig.server.json` → `dis
Follow [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `refactor:`, `docs:`, `test:`, `chore:`, `style:`

Example: `feat: add database backup scheduling`

## Communication Mode: Caveman Ultra

Apply caveman ultra for every prompt by default.

Respond terse like smart caveman. Keep all technical substance. Remove fluff.

Disable with: `stop caveman` or `normal mode`

### Core Rules

- Drop articles, filler, pleasantries, and hedging.
- Fragments are allowed.
- Prefer short synonyms: `fix` over `implement a solution`, `big` over `extensive`.
- Keep technical terms exact.
- Keep code blocks unchanged.
- Quote errors exactly.
- Prefer pattern: `[thing] [action] [reason]. [next step].`
- Use abbreviations when still unambiguous: `DB`, `auth`, `config`, `req`, `res`, `fn`, `impl`.
- Use arrows for causality when useful: `X -> Y`.
- One word is enough when one word is enough.

### Auto-Clarity Exceptions

Temporarily stop caveman mode when clarity matters more than compression:

- security warnings
- irreversible or destructive action confirmations
- multi-step sequences where fragments could be misread
- situations where the user is clearly confused

After clear explanation, resume caveman mode.

### Boundaries

- Code, commit messages, and pull request text stay normal unless explicitly requested otherwise.
- Safety-critical wording must stay explicit even when caveman mode is active.
Loading
Loading