Initial extract: templates + methodology guide from Ideation
Public, version-controlled source of truth for TeamIDE's project templates, coding conventions, and the build-plan → PM scaffolding rules. Mounted read-only by TeamIDE's planner agent at runtime. Extracted from the personal Ideation repo as a precondition for workspace-projects-agent-spec.md §9.4. Personal Ideation stays private; this repo holds only the publishable methodology.
This commit is contained in:
@@ -0,0 +1,623 @@
|
||||
# Project Guidelines
|
||||
|
||||
## Protecting This File
|
||||
|
||||
**IMPORTANT**: This file (claude.md) is gitignored and can be lost during branch operations!
|
||||
|
||||
Git doesn't protect untracked/ignored files during `git checkout` or branch switches. Before major git operations:
|
||||
```bash
|
||||
cp claude.md claude.md.backup
|
||||
```
|
||||
|
||||
## Coding Best Practices (CRITICAL)
|
||||
|
||||
**Read `coding-best-practices.md` before writing any code.** It contains personal coding conventions that apply to every project — naming rules, file/function size limits, folder nesting patterns, persistence rules, and language-specific structure guides. Those rules override defaults and must be followed.
|
||||
|
||||
### File Header Check Rule
|
||||
|
||||
**Every time you open a file to work on it, check the file header first.** Look for outstanding `@todo` items, unresolved `@issues`, or outdated `@status` notes. If any are completed or resolved, clean them up before doing other work. If a TODO is relevant to the current task, address it. This ensures nothing falls through the cracks.
|
||||
|
||||
## Project Structure (CRITICAL)
|
||||
|
||||
This project follows a standardized structure for future modularity. The long-term goal is to enable drag-and-drop of components between projects. **This structure must be followed.**
|
||||
|
||||
```
|
||||
ProjectRoot/ # git root
|
||||
├── claude.md # (gitignored) AI assistant guidelines
|
||||
├── coding-best-practices.md # Coding conventions (read before Phase 1)
|
||||
├── build_spec.md # (gitignored) Full project specification
|
||||
├── build_plan.md # (gitignored) Phased task checklist
|
||||
├── .gitignore
|
||||
├── docs/ # Public-facing documentation (parsed to HTML for website)
|
||||
│ ├── README.md # Docs index with table of contents
|
||||
│ ├── getting-started.md
|
||||
│ ├── architecture.md
|
||||
│ ├── development.md
|
||||
│ ├── deployment.md
|
||||
│ └── feature-*.md # Feature docs (added per phase)
|
||||
├── InternalDocs/ # Internal development documentation (not public)
|
||||
│ └── ... # Design decisions, technical debt, internal API docs
|
||||
└── app/ # ALL project code lives here
|
||||
├── src/
|
||||
│ ├── assets/ # Static assets
|
||||
│ ├── boot/ # Quasar boot files (nhost init, etc.)
|
||||
│ ├── components/ # Vue components
|
||||
│ ├── composables/ # Vue composables
|
||||
│ ├── layouts/ # Page layouts
|
||||
│ ├── pages/ # Route pages
|
||||
│ ├── router/ # Vue Router config
|
||||
│ ├── stores/ # Pinia stores
|
||||
│ ├── services/ # API services
|
||||
│ └── types/ # TypeScript types
|
||||
├── nhost/ # Nhost configuration
|
||||
│ ├── functions/ # Serverless functions
|
||||
│ ├── migrations/ # Database migrations
|
||||
│ └── metadata/ # Hasura metadata
|
||||
├── public/ # Public static files
|
||||
├── docker-compose.yml
|
||||
├── .env # Dev environment (gitignored)
|
||||
└── .env.prod # Prod environment (gitignored)
|
||||
```
|
||||
|
||||
## Terminology
|
||||
|
||||
- **"build spec"** or **"spec"** refers to `build_spec.md` - the full project description
|
||||
- **"build plan"** or **"plan"** refers to `build_plan.md` - the phased task checklist
|
||||
|
||||
## Onboarding Questions (Ask at Project Start)
|
||||
|
||||
At the start of every new project conversation, gather these details before writing any code:
|
||||
|
||||
1. **Brand colors & fonts** - Primary, secondary, accent colors. Typography preferences. Any existing brand guidelines or color palette?
|
||||
2. **App personality & tone** - Formal or casual? Dark or light default theme? Dense or spacious layout? Any design inspiration or reference apps?
|
||||
3. **Target platform** - Desktop-first, mobile-first, or responsive? Specific OS targets (macOS, Windows, Linux)? Minimum screen sizes?
|
||||
4. **Auth requirements** - Login method (email/password, OAuth, magic link)? User roles and permissions? Public or private app?
|
||||
|
||||
Don't assume defaults for any of these. Ask, get answers, then document them in the Project-Specific Configuration section at the bottom of this file.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Frontend**: Quasar Framework (Vue 3) - SPA mode
|
||||
- **Desktop** (optional): Tauri (Rust) - Cross-platform builds (macOS, Windows, Linux)
|
||||
- **Backend**: Nhost (self-hosted via Docker)
|
||||
- PostgreSQL - Database
|
||||
- Hasura - GraphQL API
|
||||
- Auth - Authentication service
|
||||
- Storage - File storage
|
||||
- Functions - Serverless functions (Node.js)
|
||||
- **Containerization**: Docker / Docker Compose
|
||||
|
||||
### Tauri Desktop Apps
|
||||
|
||||
When building desktop apps with Tauri:
|
||||
- Add `src-tauri/` folder inside `app/`
|
||||
- Use the build-and-run approach (no hot reload) for consistent dev/prod builds
|
||||
- Separate bundle IDs for dev and prod (can run both simultaneously)
|
||||
|
||||
## Testing Changes (CRITICAL)
|
||||
|
||||
**After every code editing session, you MUST rebuild the app so the user can test your changes.**
|
||||
|
||||
```bash
|
||||
cd app
|
||||
./build.sh -dev -open
|
||||
```
|
||||
|
||||
### Why This Matters
|
||||
|
||||
- This uses a **build-and-run** approach, not hot reload
|
||||
- Your code changes are NOT visible until you rebuild
|
||||
- Always rebuild before asking the user to test
|
||||
- If the user reports issues, check the logs first: `./build.sh logs`
|
||||
|
||||
## Build Script
|
||||
|
||||
**Always use `build.sh` for building the app.** This ensures consistent builds and logging for collaborative debugging.
|
||||
|
||||
```bash
|
||||
cd app
|
||||
|
||||
./build.sh -dev -open # Build dev and open app
|
||||
./build.sh -prod # Build prod to build/prod/
|
||||
./build.sh -install # Build prod and open Finder for drag-to-Applications
|
||||
./build.sh logs # View latest console output (last 100 lines)
|
||||
./build.sh clean # Clean build artifacts (preserves logs)
|
||||
./build.sh clean-logs # Remove all logs
|
||||
```
|
||||
|
||||
### Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `-dev` | Development build. Bundle ID: `com.[app].dev` |
|
||||
| `-prod` | Production build. Builds to `build/prod/`. |
|
||||
| `-install` | Build prod and open Finder for drag-to-Applications. |
|
||||
| `-open` | Open the app after building (use with `-dev` or `-prod`) |
|
||||
| `logs` | Show last 100 lines of console output |
|
||||
| `clean` | Remove build artifacts (preserves logs) |
|
||||
| `clean-logs` | Remove all log files |
|
||||
|
||||
### Why Build-and-Run (No Hot Reload)
|
||||
|
||||
This project uses a **build-and-run** approach instead of hot reload:
|
||||
|
||||
| Aspect | Hot Reload | Build-and-Run |
|
||||
|--------|------------|---------------|
|
||||
| Speed | Instant updates | Full rebuild required |
|
||||
| Dev/Prod parity | Different environments | Identical to production |
|
||||
| Debugging | Dev-only issues possible | What you test = what ships |
|
||||
| Logging | Console only | Full logs to files |
|
||||
|
||||
The tradeoff is slower iteration, but you always test exactly what will ship.
|
||||
|
||||
### What the Build Does
|
||||
|
||||
1. **Build with Tauri** - `npx tauri build` does all of the following:
|
||||
- Build frontend (`npm run build` → `dist/spa/`)
|
||||
- Build Rust binary with embedded frontend
|
||||
- Create .app bundle in `src-tauri/target/release/bundle/macos/`
|
||||
2. **Copy to build folder** - Copies bundle to `build/dev/` or `build/prod/`
|
||||
3. **Update Info.plist** - Sets correct bundle ID for dev/prod
|
||||
4. **Install (prod only)** - Copies to `~/Applications`
|
||||
|
||||
**Important**: Using `npx tauri build` ensures the frontend is properly embedded in the binary. Manual `cargo build --release` does NOT embed the frontend.
|
||||
|
||||
### Build Output
|
||||
|
||||
Builds are organized in separate folders:
|
||||
|
||||
```
|
||||
app/build/
|
||||
├── dev/
|
||||
│ └── AppName.app # Dev build (bundle ID: .dev)
|
||||
├── prod/
|
||||
│ └── AppName.app # Prod build
|
||||
├── logs/
|
||||
│ ├── build_dev_*.log # Build output logs
|
||||
│ └── build_latest.log # Symlink to latest
|
||||
└── console/
|
||||
├── console_*.log # App runtime console output
|
||||
└── console_latest.log # Symlink to latest
|
||||
```
|
||||
|
||||
Dev and prod have different bundle IDs, so both can run simultaneously.
|
||||
|
||||
### Logging
|
||||
|
||||
All build output and app console output is logged:
|
||||
|
||||
- **Build logs**: `build/logs/build_latest.log` - compilation output, errors
|
||||
- **Console logs**: `build/console/console_latest.log` - runtime `console.log()`, errors
|
||||
|
||||
When debugging, run `./build.sh logs` to see the latest console output.
|
||||
|
||||
### Console Logging Setup (Tauri)
|
||||
|
||||
For frontend `console.log()` to appear in the logs, you need:
|
||||
|
||||
1. **Boot file** - Copy `templates/scripts/console-logger.ts` to `app/src/boot/`
|
||||
2. **Register boot** - Add `'console-logger'` to `boot` array in `quasar.config.js`
|
||||
3. **Rust command** - Add `log_to_stdout` command to `src-tauri/src/commands.rs`
|
||||
4. **Cargo dep** - Add `chrono = "0.4"` to `Cargo.toml`
|
||||
|
||||
The console-logger.ts template includes the Rust code you need to add.
|
||||
|
||||
**How it works:**
|
||||
```
|
||||
console.log() → console-logger.ts → invoke('log_to_stdout') → Rust println!() → stdout → build.sh logs
|
||||
```
|
||||
|
||||
### When to Clean
|
||||
|
||||
Run `./build.sh clean` when:
|
||||
- Switching between major dependency versions
|
||||
- Build errors reference stale/cached files
|
||||
- Rust compilation issues after Tauri config changes
|
||||
- Before creating a fresh production build
|
||||
|
||||
## Node.js Environment
|
||||
|
||||
**Node.js version**: Always match Debian stable (currently Node.js 18.x for Debian 12 Bookworm)
|
||||
|
||||
Check current Debian stable Node version: https://packages.debian.org/stable/nodejs
|
||||
|
||||
**Package manager**: Use npm. Commit lock files for reproducible builds.
|
||||
|
||||
Rules:
|
||||
- Always commit `package-lock.json` - required for deterministic installs
|
||||
- Never ignore lock files (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`)
|
||||
- Use `npm install` to install dependencies
|
||||
- Use `npm run <script>` to run package scripts
|
||||
|
||||
## Communication Rules
|
||||
|
||||
**Always explain before acting.** Before running any command, SSH, database query, or making changes:
|
||||
- Give a short, concise explanation of WHAT you're about to do and WHY
|
||||
- Wait for approval on anything that modifies production data or server state
|
||||
- Don't chain multiple server commands without explaining each step
|
||||
|
||||
## Database Rules (CRITICAL)
|
||||
|
||||
**NEVER make direct database changes via Hasura MCP or Hasura Console.**
|
||||
|
||||
All database changes MUST be done through migrations so they:
|
||||
1. Are tracked in version control
|
||||
2. Persist across restarts (`nhost down` / `nhost up`)
|
||||
3. Can be reproduced in production
|
||||
|
||||
### Hasura MCP Usage
|
||||
|
||||
- **READ-ONLY**: Use Hasura MCP only to inspect schema, check what tables exist, verify tracking status
|
||||
- **NO MUTATIONS**: Never use it to create tables, add columns, track tables, or modify permissions
|
||||
- **MIGRATIONS ONLY**: All schema changes go in `app/nhost/migrations/default/`
|
||||
- **METADATA ONLY**: All Hasura config (tracking, permissions, relationships) go in `app/nhost/metadata/`
|
||||
|
||||
### How to Make Database Changes
|
||||
|
||||
1. Write SQL migration in `app/nhost/migrations/default/<N>_<name>/up.sql` (N = next version number)
|
||||
2. Write rollback in `app/nhost/migrations/default/<N>_<name>/down.sql`
|
||||
3. Add Hasura metadata YAML in `app/nhost/metadata/databases/default/tables/`
|
||||
4. Restart dev environment to apply migrations
|
||||
5. Commit and push
|
||||
|
||||
**Migration folder naming:**
|
||||
```
|
||||
app/nhost/migrations/default/
|
||||
├── 1_init_schema/
|
||||
├── 2_add_users/
|
||||
├── 3_add_settings/
|
||||
└── ...next version number...
|
||||
```
|
||||
|
||||
### Hasura Role Permissions (CRITICAL)
|
||||
|
||||
**NEVER define permissions for the `admin` role in Hasura metadata.**
|
||||
|
||||
Hasura reserves `admin` as a special role with **full unrestricted access**. Defining explicit permissions for `admin` causes metadata errors:
|
||||
```
|
||||
"cannot define permission for admin role"
|
||||
```
|
||||
|
||||
**Correct approach:**
|
||||
- Define permissions only for `user`, `public`, and custom roles
|
||||
- Users with `X-Hasura-Role: admin` automatically bypass all permission checks
|
||||
|
||||
**Example - WRONG:**
|
||||
```yaml
|
||||
select_permissions:
|
||||
- role: admin # DON'T DO THIS
|
||||
permission:
|
||||
columns: [id, name]
|
||||
filter: {}
|
||||
```
|
||||
|
||||
**Example - CORRECT:**
|
||||
```yaml
|
||||
select_permissions:
|
||||
- role: user # Only define for non-admin roles
|
||||
permission:
|
||||
columns: [id, name]
|
||||
filter: { created_by: { _eq: X-Hasura-User-Id } }
|
||||
```
|
||||
|
||||
## Development Approach
|
||||
|
||||
**Integrate-as-you-go** - Connect real backend services as you build. Don't build elaborate mock systems. The Docker infrastructure provides:
|
||||
- Database & Hasura - Real tables and GraphQL
|
||||
- Nhost Auth - Real authentication
|
||||
- Real data from the start
|
||||
|
||||
## Persistence Rules (CRITICAL)
|
||||
|
||||
**All persistence requires an explicit user action. No exceptions.**
|
||||
|
||||
Never auto-save. Never sync state in the background. Never write to the database on a timer or watcher. Every single write to a database, file, or storage must be traceable to a deliberate user action.
|
||||
|
||||
### The Rule
|
||||
|
||||
Data is only persisted when the user deliberately triggers it — clicking "Save", submitting a form, confirming a dialog, toggling a setting, etc.
|
||||
|
||||
### WRONG — Never Do This
|
||||
|
||||
- Auto-saving form fields as the user types
|
||||
- Syncing component state to the database on mount/unmount
|
||||
- Watchers that write to storage when reactive data changes
|
||||
- Debounced save on input change
|
||||
- Persisting UI state automatically (sidebar position, tab selection, scroll position)
|
||||
- Writing to the database during navigation or route changes
|
||||
- Background sync intervals or polling that writes data
|
||||
|
||||
### RIGHT — Always Do This
|
||||
|
||||
- User clicks "Save" → write to database
|
||||
- User submits a form → persist the data
|
||||
- User explicitly toggles a setting → save that setting
|
||||
- User clicks "Delete" → confirm, then remove from database
|
||||
- User drags an item to reorder → save on drop (explicit action)
|
||||
|
||||
### Why This Matters
|
||||
|
||||
Auto-save creates unpredictable behavior, race conditions, and persists data the user didn't intend to save. Every write should be traceable to a conscious user decision. If you can't point to the button or action the user took to trigger the save, it shouldn't be saving.
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
```
|
||||
User Action → Service Call → Database Write → Confirm Success to User
|
||||
```
|
||||
|
||||
1. User triggers action (click, submit, confirm)
|
||||
2. Call the appropriate service/API
|
||||
3. Write to database
|
||||
4. Show success feedback (Quasar Notify or UI update)
|
||||
5. If it fails, show an error — don't silently retry
|
||||
|
||||
## Code Quality
|
||||
|
||||
### TypeScript
|
||||
|
||||
- Use TypeScript strict mode
|
||||
- Define interfaces/types for all data structures in `src/types/`
|
||||
- No `any` types unless absolutely unavoidable — use `unknown` and narrow instead
|
||||
- Type all function parameters and return values
|
||||
|
||||
### Vue / Quasar Conventions
|
||||
|
||||
- Use Composition API with `<script setup lang="ts">` for all components
|
||||
- One component per file, named in PascalCase (e.g., `UserProfile.vue`)
|
||||
- Composables go in `src/composables/`, prefixed with `use` (e.g., `useAuth.ts`)
|
||||
- Keep components focused — if it's doing too much, split it
|
||||
- Props down, events up — don't mutate props, emit events to parent
|
||||
|
||||
### Naming
|
||||
|
||||
- **Files**: PascalCase for components (`UserCard.vue`), camelCase for everything else (`userService.ts`)
|
||||
- **Variables/functions**: camelCase
|
||||
- **Constants**: UPPER_SNAKE_CASE for true constants, camelCase for derived/computed values
|
||||
- **Types/Interfaces**: PascalCase, no `I` prefix (use `User` not `IUser`)
|
||||
- **CSS classes**: kebab-case
|
||||
|
||||
### General
|
||||
|
||||
- No magic numbers or strings — use named constants
|
||||
- Keep functions short and single-purpose
|
||||
- Colocate related code — don't scatter logic across files unnecessarily
|
||||
- Delete dead code, don't comment it out
|
||||
|
||||
## Error Handling
|
||||
|
||||
### User-Facing Errors
|
||||
|
||||
- Always show human-readable messages — never expose raw error objects, stack traces, or technical details to the user
|
||||
- Use Quasar's `Notify` plugin for transient feedback (success, warning, error)
|
||||
- Use inline error messages for form validation
|
||||
|
||||
### Technical Errors
|
||||
|
||||
- Log technical details to console for debugging (`console.error` with context)
|
||||
- Include the operation that failed and relevant IDs in log messages
|
||||
- Never swallow errors silently — if you catch it, either handle it or log it
|
||||
|
||||
### Pattern
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const result = await userService.updateProfile(data)
|
||||
Notify.create({ type: 'positive', message: 'Profile updated' })
|
||||
} catch (error) {
|
||||
console.error('Failed to update profile:', error)
|
||||
Notify.create({ type: 'negative', message: 'Could not update profile. Try again.' })
|
||||
}
|
||||
```
|
||||
|
||||
### Where to Catch
|
||||
|
||||
- Catch at **service boundaries** (API calls, database operations), not deep in utility functions
|
||||
- Let errors propagate up to where they can be meaningfully handled
|
||||
- Don't wrap every line in try/catch — catch at the level where you can show the user feedback
|
||||
|
||||
## Documentation
|
||||
|
||||
### docs/ vs InternalDocs/
|
||||
|
||||
This project has two documentation directories with different audiences:
|
||||
|
||||
| Directory | Audience | Purpose | Parsed for website? |
|
||||
|-----------|----------|---------|---------------------|
|
||||
| `docs/` | Public | User-facing app documentation — getting started, features, architecture | Yes |
|
||||
| `InternalDocs/` | Developers | Internal dev context — design decisions, technical debt, internal API contracts, Claude context | No |
|
||||
|
||||
**`docs/`** contains ONLY what the public needs. It gets parsed into HTML and served on the app website. No internal implementation details, no dev-only context.
|
||||
|
||||
**`InternalDocs/`** contains everything internal — design rationale, known technical debt, internal API documentation, integration notes, debugging guides, anything that helps the development team but doesn't belong in public docs.
|
||||
|
||||
Both directories are committed to git.
|
||||
|
||||
### Phase Documentation
|
||||
|
||||
**Every phase ends with documentation.** After completing a feature or phase:
|
||||
|
||||
1. **Create feature docs** - Copy `docs/TEMPLATE-feature.md` to `docs/feature-[name].md`
|
||||
2. **Update the index** - Add links to `docs/README.md` table of contents
|
||||
3. **Update core docs** - Keep `architecture.md`, `development.md` current
|
||||
4. **Update internal docs** - Add design decisions, technical debt notes, or internal API docs to `InternalDocs/` as needed
|
||||
|
||||
### docs/ Structure
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `docs/README.md` | Index with table of contents |
|
||||
| `docs/getting-started.md` | Installation & setup |
|
||||
| `docs/architecture.md` | Tech stack & design decisions |
|
||||
| `docs/development.md` | Development workflow |
|
||||
| `docs/deployment.md` | Build & deployment |
|
||||
| `docs/feature-*.md` | Feature-specific documentation |
|
||||
|
||||
### Naming Convention
|
||||
|
||||
- `feature-[name].md` - Feature docs (e.g., `feature-authentication.md`)
|
||||
- `component-[name].md` - Component docs
|
||||
- `integration-[name].md` - Integration docs
|
||||
- `guide-[topic].md` - How-to guides
|
||||
|
||||
## Commit Rules
|
||||
|
||||
- **Never mention Claude, AI, or "Generated with Claude Code" in commits**
|
||||
- All commits are authored by the developer - no co-author tags
|
||||
- Write commit messages in first person as the developer
|
||||
- Keep commit messages concise and descriptive of the changes
|
||||
|
||||
## Branch Strategy
|
||||
|
||||
- **main** - Production branch
|
||||
- **test** - Staging/testing branch
|
||||
|
||||
**Workflow:**
|
||||
1. Develop and test locally
|
||||
2. Push to `test` branch for staging
|
||||
3. Verify on staging
|
||||
4. Merge `test` → `main` for production
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```env
|
||||
# Nhost
|
||||
NHOST_ADMIN_SECRET=
|
||||
|
||||
# PostgreSQL
|
||||
POSTGRES_PASSWORD=
|
||||
|
||||
# JWT
|
||||
JWT_SECRET=
|
||||
|
||||
# App
|
||||
APP_URL=
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Project-Specific Configuration
|
||||
|
||||
<!-- Everything below this line is customized per project -->
|
||||
|
||||
## Project Name
|
||||
|
||||
[PROJECT_NAME]
|
||||
|
||||
## Description
|
||||
|
||||
[Brief description from build_spec.md]
|
||||
|
||||
## Database Schema
|
||||
|
||||
[Tables and relationships]
|
||||
|
||||
## URLs
|
||||
|
||||
| Service | Local | Production |
|
||||
|---------|-------|------------|
|
||||
| App | http://localhost:[dynamic] | |
|
||||
| GraphQL | http://localhost:8080 | |
|
||||
|
||||
**Note**: For Tauri desktop apps, use dynamic port allocation. The dev server will find the first available port.
|
||||
|
||||
## MCP Servers
|
||||
|
||||
[Configure in .mcp.json if needed]
|
||||
|
||||
## Servers
|
||||
|
||||
[Production server details if applicable]
|
||||
|
||||
## Dockerized Frontend for Production
|
||||
|
||||
When deploying a fully dockerized project, add a frontend service to docker-compose:
|
||||
|
||||
### 1. Create `app/Dockerfile.frontend`
|
||||
|
||||
```dockerfile
|
||||
# Build stage
|
||||
FROM node:20-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
### 2. Create `app/nginx.conf`
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Add frontend service to `docker-compose.yml`
|
||||
|
||||
All Traefik hostnames use hardcoded localhost values. TeamIDE's proxy rewrites the Host header to localhost, so this works both locally and in production.
|
||||
|
||||
```yaml
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.frontend
|
||||
depends_on:
|
||||
graphql:
|
||||
condition: service_healthy
|
||||
auth:
|
||||
condition: service_healthy
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.frontend.rule: Host(`localhost`)
|
||||
traefik.http.routers.frontend.entrypoints: web
|
||||
traefik.http.services.frontend.loadbalancer.server.port: "80"
|
||||
extra_hosts:
|
||||
- host.docker.internal:host-gateway
|
||||
- localhost:host-gateway
|
||||
- auth.localhost:host-gateway
|
||||
- hasura.localhost:host-gateway
|
||||
- storage.localhost:host-gateway
|
||||
restart: always
|
||||
```
|
||||
|
||||
### 4. Traefik hostname pattern
|
||||
|
||||
All services use hardcoded localhost hostnames:
|
||||
- Frontend: `Host(\`localhost\`)`
|
||||
- Auth: `Host(\`auth.localhost\`)`
|
||||
- Hasura: `Host(\`hasura.localhost\`)`
|
||||
- Storage: `Host(\`storage.localhost\`)`
|
||||
- Functions: `Host(\`functions.localhost\`)`
|
||||
|
||||
## TeamIDE Hosting
|
||||
|
||||
This project is deployed via TeamIDE.
|
||||
|
||||
- **Workspace ID**: `your-workspace-id-here`
|
||||
- Use `teamide_deploy` to deploy changes
|
||||
- Use `teamide_set_env_vars` to configure environment variables
|
||||
- Use `teamide_get_logs` to check build status
|
||||
|
||||
To get your workspace ID, run:
|
||||
```bash
|
||||
teamide workspace list
|
||||
```
|
||||
Reference in New Issue
Block a user