mirror of
https://github.com/biersoeckli/QuickStack.git
synced 2026-02-11 05:59:23 -06:00
3.7 KiB
3.7 KiB
QuickStack AI Coding Instructions
QuickStack is a self-hosted PaaS built with Next.js 14 (App Router) that manages Kubernetes (k3s) deployments. It uses a custom server (src/server.ts) that wraps Next.js to handle WebSockets for terminal streaming.
Architecture Overview
Three-Layer Structure
src/app/- Next.js App Router pages and Server Actions (all pages use'use server')src/server/- Backend services that interact with Kubernetes and databasesrc/shared/- Shared models, utils, and Zod schemas (used by both frontend and server)
Key Adapters (src/server/adapter/)
kubernetes-api.adapter.ts- Wraps@kubernetes/client-nodeAPIs (k3s.core,k3s.apps, etc.)db.client.ts- Prisma singleton (dataAccess.client)longhorn-api.adapter.ts- Longhorn storage API
Service Pattern
Services are singleton classes exported as default instances:
class AppService { /* methods */ }
const appService = new AppService();
export default appService;
Server Actions Pattern
All server actions use wrappers from src/server/utils/action-wrapper.utils.ts:
// 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');
});
Database & Prisma
- SQLite database at
storage/db/data.db - Schema:
prisma/schema.prisma - Zod schemas auto-generated to
src/shared/model/generated-zod/ - After schema changes:
yarn prisma-migrate(runsprisma migrate dev+ fixes Zod imports) - Use
dataAccess.clientfor queries, supports transactions via$transaction()
Kubernetes Naming Conventions
Use KubeObjectNameUtils for consistent k8s object names:
toProjectId(name)→proj-{name}-{hash}toAppId(name)→app-{name}-{hash}toPvcName(volumeId)→pvc-{volumeId}toServiceName(appId)→svc-{appId}
Frontend Patterns
State Management
Zustand stores in src/frontend/states/zustand.states.ts:
useConfirmDialog- Promise-based confirmation dialogsuseInputDialog- Promise-based input dialogsuseBreadcrumbs- Page breadcrumb navigation
UI Components
- shadcn/ui components in
src/components/ui/ - Custom components in
src/components/custom/ - Forms use
react-hook-formwith Zod resolvers
Real-time Updates
- Socket.IO server at
/pod-terminalnamespace for terminal streaming - WebSocket server for live pod logs
Caching
Next.js unstable_cache with tag-based invalidation:
// Reading with cache
await unstable_cache(
async () => dataAccess.client.app.findMany({ where: { projectId } }),
[Tags.apps(projectId)],
{ tags: [Tags.apps(projectId)] }
)(projectId);
// Invalidating after mutations
revalidateTag(Tags.apps(projectId));
Testing
- Jest with jsdom environment
- Tests in
src/__tests__/{frontend,server,shared}/ - Path alias
@/maps tosrc/ - Run:
yarn test
Development Setup
- Use provided devcontainer (includes Node, Bun, Prisma extension)
- Provide k3s credentials in
kube-config.configat project root yarn install→yarn devfor Next.js oryarn dev-livefor custom server
Commit Convention
Follow Conventional Commits: feat:, fix:, refactor:, docs:, test:, chore: