-
Notifications
You must be signed in to change notification settings - Fork 0
feat-superadmin-set-org-plan #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## Project Overview | ||
|
|
||
| Obiente Cloud is a distributed Platform-as-a-Service (PaaS) built as an Nx monorepo with 14 Go microservices and a Nuxt 4 dashboard. Services communicate via ConnectRPC (protocol buffers). Deployed on Docker Swarm. | ||
|
|
||
| ## Build & Development Commands | ||
|
|
||
| ### Package Management | ||
| ```bash | ||
| pnpm install # Install all JS/TS dependencies | ||
| ``` | ||
|
|
||
| ### Dashboard (Nuxt 4 frontend) | ||
| ```bash | ||
| nx serve dashboard # Dev server on port 3000 | ||
| nx nuxt:build dashboard # Production build | ||
| nx lint dashboard # ESLint | ||
| nx typecheck dashboard # Type checking | ||
| ``` | ||
|
|
||
| ### Go Services | ||
| ```bash | ||
| cd apps/<service-name> | ||
| go run main.go # Run locally | ||
| go build # Build binary | ||
| go test ./... # Run tests | ||
| ``` | ||
|
|
||
| ### Protocol Buffers | ||
| ```bash | ||
| cd packages/proto | ||
| pnpm build # Regenerate all proto code (buf generate) | ||
| ``` | ||
| Generated Go code goes to `apps/shared/proto/`, TypeScript to `packages/proto/src/generated/`. | ||
|
|
||
| ### Docker | ||
| ```bash | ||
| docker compose up -d # Local dev (all services) | ||
| docker build -f apps/<svc>/Dockerfile -t ghcr.io/obiente/cloud-<svc>:latest . # Build image | ||
| ./scripts/deploy-swarm-dev.sh # Swarm dev deploy | ||
| ./scripts/deploy-swarm-dev.sh -b # Build + deploy | ||
| ``` | ||
|
|
||
| ### Nx | ||
| Always prefer running tasks through `nx` rather than underlying tooling directly. Use `nx run`, `nx run-many`, `nx affected`. | ||
|
|
||
| ## Architecture | ||
|
|
||
| ### Service Ports | ||
| | Service | Port | | ||
| |---------|------| | ||
| | Dashboard | 3000 | | ||
| | API Gateway | 3001 | | ||
| | Auth | 3002 | | ||
| | Organizations | 3003 | | ||
| | Billing | 3004 | | ||
| | Deployments | 3005 | | ||
| | GameServers | 3006 | | ||
| | Orchestrator | 3007 | | ||
| | VPS | 3008 | | ||
| | Support | 3009 | | ||
| | Audit | 3010 | | ||
| | Superadmin | 3011 | | ||
| | Notifications | 3012 | | ||
| | DNS | 8053 | | ||
|
|
||
| ### Key Architectural Patterns | ||
|
|
||
| - **API Gateway** routes all external requests to backend services. Supports both direct service routing and Traefik-based routing. | ||
| - **ConnectRPC** is used for all inter-service communication. Proto definitions live in `packages/proto/proto/obiente/cloud/`. Buf generates both Go and TypeScript clients. | ||
| - **Go workspace** (`go.work`) links all 15 Go modules. Shared code is in `apps/shared/` with packages for auth, database, docker, middleware, orchestrator, quota, etc. | ||
| - **Auth** is handled via Zitadel integration with RBAC. The auth-service validates tokens and manages roles/permissions. | ||
| - **Orchestrator** handles intelligent node selection and load balancing across the Docker Swarm cluster. | ||
| - **Database**: PostgreSQL (primary), TimescaleDB (metrics/audit), Redis (cache, build logs). | ||
| - **Dashboard** uses Nuxt 4, Vue 3, Tailwind CSS v4, Pinia for state, Ark UI for components, and `@connectrpc/connect-web` for API calls. | ||
|
|
||
| ### Monorepo Structure | ||
| - `apps/` - All microservices + dashboard | ||
| - `packages/proto/` - Protobuf definitions and generated code | ||
| - `packages/database/` - Drizzle ORM schemas and migrations | ||
| - `packages/config/` - Shared ESLint, Prettier, TypeScript configs | ||
| - `packages/types/` - Shared TypeScript types | ||
| - `tools/nxsh/` - Custom Nx shell executor | ||
| - `monitoring/` - Prometheus & Grafana configs | ||
| - `scripts/` - Deployment and operational scripts | ||
|
|
||
| ### Docker Compose Files | ||
| - `docker-compose.yml` - Local development | ||
| - `docker-compose.base.yml` - Shared env vars (YAML anchors) | ||
| - `docker-compose.swarm.yml` - Production swarm | ||
| - `docker-compose.swarm.dev.yml` - Dev swarm (must use `docker stack deploy`, not `docker compose`) | ||
| - `docker-compose.swarm.ha.yml` - HA production with PostgreSQL cluster | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -130,6 +130,61 @@ | |||||||||||||||||||||||||||||
| </OuiFlex> | ||||||||||||||||||||||||||||||
| </OuiStack> | ||||||||||||||||||||||||||||||
| </OuiDialog> | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| <!-- Set Plan Dialog --> | ||||||||||||||||||||||||||||||
| <OuiDialog v-model:open="setPlanDialogOpen" title="Set Plan"> | ||||||||||||||||||||||||||||||
| <OuiStack gap="lg"> | ||||||||||||||||||||||||||||||
| <OuiStack gap="xs"> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" color="muted">Organization</OuiText> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" weight="medium">{{ selectedOrgName }}</OuiText> | ||||||||||||||||||||||||||||||
| </OuiStack> | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| <OuiStack gap="xs"> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" color="muted">Current Plan</OuiText> | ||||||||||||||||||||||||||||||
| <OuiBadge :variant="selectedOrgCurrentPlan ? 'primary' : 'secondary'" size="sm"> | ||||||||||||||||||||||||||||||
| {{ prettyPlan(selectedOrgCurrentPlan) || 'None' }} | ||||||||||||||||||||||||||||||
| </OuiBadge> | ||||||||||||||||||||||||||||||
| </OuiStack> | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| <OuiSelect | ||||||||||||||||||||||||||||||
| v-model="selectedPlanId" | ||||||||||||||||||||||||||||||
| label="New Plan" | ||||||||||||||||||||||||||||||
| :items="planSelectItems" | ||||||||||||||||||||||||||||||
| placeholder="Choose a plan..." | ||||||||||||||||||||||||||||||
| clearable | ||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| <template v-if="selectedPlanInfo"> | ||||||||||||||||||||||||||||||
| <OuiStack gap="sm" class="rounded-lg border border-border-muted p-3 bg-surface-muted/50"> | ||||||||||||||||||||||||||||||
| <OuiText size="xs" weight="medium" color="muted" class="uppercase tracking-wide">Plan Resources</OuiText> | ||||||||||||||||||||||||||||||
| <div class="grid grid-cols-2 gap-x-4 gap-y-1"> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" color="secondary">CPU</OuiText> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.cpuCores || '∞' }} cores</OuiText> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" color="secondary">Memory</OuiText> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.memoryBytes ? formatBytes(Number(selectedPlanInfo.memoryBytes)) : '∞' }}</OuiText> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" color="secondary">Deployments</OuiText> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.deploymentsMax || '∞' }}</OuiText> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" color="secondary">VPS Instances</OuiText> | ||||||||||||||||||||||||||||||
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.maxVpsInstances || '∞' }}</OuiText> | ||||||||||||||||||||||||||||||
|
Comment on lines
+162
to
+168
|
||||||||||||||||||||||||||||||
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.cpuCores || '∞' }} cores</OuiText> | |
| <OuiText size="sm" color="secondary">Memory</OuiText> | |
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.memoryBytes ? formatBytes(Number(selectedPlanInfo.memoryBytes)) : '∞' }}</OuiText> | |
| <OuiText size="sm" color="secondary">Deployments</OuiText> | |
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.deploymentsMax || '∞' }}</OuiText> | |
| <OuiText size="sm" color="secondary">VPS Instances</OuiText> | |
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.maxVpsInstances || '∞' }}</OuiText> | |
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.cpuCores === 0 || selectedPlanInfo.cpuCores == null ? '∞' : selectedPlanInfo.cpuCores }} cores</OuiText> | |
| <OuiText size="sm" color="secondary">Memory</OuiText> | |
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.memoryBytes === 0 || selectedPlanInfo.memoryBytes == null ? '∞' : formatBytes(Number(selectedPlanInfo.memoryBytes)) }}</OuiText> | |
| <OuiText size="sm" color="secondary">Deployments</OuiText> | |
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.deploymentsMax === 0 || selectedPlanInfo.deploymentsMax == null ? '∞' : selectedPlanInfo.deploymentsMax }}</OuiText> | |
| <OuiText size="sm" color="secondary">VPS Instances</OuiText> | |
| <OuiText size="sm" class="font-mono text-right">{{ selectedPlanInfo.maxVpsInstances === 0 || selectedPlanInfo.maxVpsInstances == null ? '∞' : selectedPlanInfo.maxVpsInstances }}</OuiText> |
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing cleanup of selectedOrgId on dialog close: When the dialog is closed via the Cancel button (line 178), selectedOrgId is not reset to null. This could cause issues if the dialog is reopened for a different organization - the selectedOrgName and selectedOrgCurrentPlan computed properties would still reference the old organization until a new action is clicked.
Consider adding cleanup when the dialog closes:
<OuiButton variant="ghost" @click="setPlanDialogOpen = false; selectedOrgId.value = null; selectedPlanId.value = null">Cancel</OuiButton>
Or better yet, watch the setPlanDialogOpen value and reset on close.
| <OuiButton variant="ghost" @click="setPlanDialogOpen = false">Cancel</OuiButton> | |
| <OuiButton variant="ghost" @click="setPlanDialogOpen = false; selectedOrgId = null; selectedPlanId = null">Cancel</OuiButton> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -168,7 +168,7 @@ export default defineNuxtConfig({ | |
| // Use API Gateway for all requests (routes to microservices) | ||
| // When running locally (not in Docker), use localhost with Traefik port | ||
| // When running in Docker, use api-gateway service name | ||
| apiHostInternal: process.env.NUXT_API_HOST_INTERNAL || process.env.NUXT_PUBLIC_API_HOST || "http://localhost:80", | ||
| apiHostInternal: process.env.NUXT_API_HOST_INTERNAL || process.env.NUXT_PUBLIC_API_HOST || "http://api.localhost", | ||
|
||
| githubClientSecret: process.env.GITHUB_CLIENT_SECRET || "", // Server-side only - never expose to client | ||
| session: { | ||
| password: "changeme_" + crypto.randomUUID(), // CHANGE THIS IN PRODUCTION, should be at least 32 characters | ||
|
|
@@ -196,6 +196,9 @@ export default defineNuxtConfig({ | |
| port: 3000, | ||
| host: "0.0.0.0", | ||
| }, | ||
| future: { | ||
| compatibilityVersion: 4 | ||
| }, | ||
|
Comment on lines
+199
to
+201
|
||
|
|
||
| app: { | ||
| head: { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entire CLAUDE.md file addition appears unrelated to this PR's stated purpose of adding superadmin set-plan functionality. This is a comprehensive documentation file about the project structure and development practices.
While this documentation may be valuable, it should be added in a separate PR dedicated to documentation improvements, not bundled with a feature implementation PR. This makes the PR harder to review and mixes unrelated concerns.