Add deployment dashboard docs and artifacts
- Add design doc for Vercel-style deployment dashboard - Add wave-based implementation plan (4 waves, 11 agents) - Add implementation summary artifact - Update CLAUDE.md with CloudBeaver credentials Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
245
.artifacts/2026-02-06_17-30_deployment-dashboard-design.md
Normal file
245
.artifacts/2026-02-06_17-30_deployment-dashboard-design.md
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
# NUC Portal - Deployment Dashboard Design
|
||||||
|
|
||||||
|
**Date:** 2026-02-06 17:30
|
||||||
|
**Context:** Reverse-engineered Vercel deployment dashboard to design similar feature for nuc-portal
|
||||||
|
|
||||||
|
## Task Summary
|
||||||
|
|
||||||
|
**Goal:** Add deployment detail page to nuc-portal
|
||||||
|
- Current: Click row in Deployments tab → expand/collapse logs inline
|
||||||
|
- New: Click row → navigate to `/deployments/[uuid]` dashboard page
|
||||||
|
- Keep: Add explicit expand button for inline log preview
|
||||||
|
|
||||||
|
## Vercel Dashboard Structure (Reference)
|
||||||
|
|
||||||
|
### Page Layout
|
||||||
|
```
|
||||||
|
Breadcrumb: Deployments > [deployment-id]
|
||||||
|
Tabs: Deployment | Logs | Resources | Source | Open Graph
|
||||||
|
Actions: Share | Logs | Visit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment Tab Sections
|
||||||
|
|
||||||
|
**1. Deployment Details Card (Top)**
|
||||||
|
| Left | Right |
|
||||||
|
|------|-------|
|
||||||
|
| Preview screenshot | Created: user + date |
|
||||||
|
| | Status: Ready + "Latest" badge |
|
||||||
|
| | Duration: 43s + "43d ago" |
|
||||||
|
| | Environment: Production + "Current" |
|
||||||
|
| | Domains: list with "+N" overflow |
|
||||||
|
| | Source: branch + commit hash + message |
|
||||||
|
|
||||||
|
**2. Collapsible Sections:**
|
||||||
|
|
||||||
|
| Section | Content |
|
||||||
|
|---------|---------|
|
||||||
|
| **Deployment Settings** | Recommendations cards, Build Settings (concurrent builds, machine specs, prioritize prod), Runtime Settings (fluid compute, function CPU, Node version, protections) |
|
||||||
|
| **Build Logs** | Header: line count, warnings, search. Body: timestamped log lines, warnings highlighted yellow |
|
||||||
|
| **Deployment Summary** | Framework badge, Edge Middleware count, Static Assets (filterable by type), Functions, ISR Functions, Cron Jobs |
|
||||||
|
| **Deployment Checks** | External check integrations or "No checks configured" |
|
||||||
|
| **Assigning Custom Domains** | Domain list with status checkmarks and manage links |
|
||||||
|
|
||||||
|
**3. Bottom Cards (4-column grid):**
|
||||||
|
- Runtime Logs | Observability | Speed Insights | BotID
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Coolify Data Available
|
||||||
|
|
||||||
|
### From Deployment Table
|
||||||
|
```typescript
|
||||||
|
interface CoolifyDeployment {
|
||||||
|
deployment_uuid: string;
|
||||||
|
application_id: string;
|
||||||
|
application_name: string;
|
||||||
|
server_name: string;
|
||||||
|
status: 'finished' | 'error' | 'in_progress' | 'queued' | 'cancelled';
|
||||||
|
commit: string; // SHA
|
||||||
|
commit_message: string; // Full message
|
||||||
|
is_webhook: boolean;
|
||||||
|
is_api: boolean;
|
||||||
|
force_rebuild: boolean;
|
||||||
|
rollback: boolean;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
finished_at: string | null;
|
||||||
|
logs: DeploymentLog[]; // JSON array with timestamps
|
||||||
|
deployment_url: string; // Coolify UI link
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### From Application Table
|
||||||
|
```typescript
|
||||||
|
interface CoolifyApp {
|
||||||
|
uuid: string;
|
||||||
|
name: string;
|
||||||
|
fqdn: string; // e.g., "http://nuc.lan"
|
||||||
|
status: string; // e.g., "running:unknown"
|
||||||
|
git_repository: string;
|
||||||
|
git_branch: string;
|
||||||
|
build_pack: string; // nixpacks, dockerfile, etc.
|
||||||
|
ports_exposes: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Direct Database Query (via tinker)
|
||||||
|
```bash
|
||||||
|
ssh nuc "docker exec coolify php artisan tinker --execute=\"
|
||||||
|
use App\\\\Models\\\\Application;
|
||||||
|
use App\\\\Models\\\\ApplicationDeploymentQueue;
|
||||||
|
\\\$app = Application::where('uuid', '<uuid>')->first();
|
||||||
|
\\\$d = ApplicationDeploymentQueue::where('application_id', \\\$app->id)->latest()->first();
|
||||||
|
echo json_encode(\\\$d->toArray(), JSON_PRETTY_PRINT);
|
||||||
|
\""
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gap Analysis
|
||||||
|
|
||||||
|
### What Coolify Provides (Direct Mapping)
|
||||||
|
- Deployment UUID, status, timestamps, duration (computed)
|
||||||
|
- Git branch, commit SHA, commit message
|
||||||
|
- Build logs with timestamps
|
||||||
|
- App name, FQDN/domains, build pack, ports
|
||||||
|
- Webhook/API trigger info
|
||||||
|
|
||||||
|
### Gaps - Ranked by Priority
|
||||||
|
|
||||||
|
**Tier 1 - High Impact, Easy**
|
||||||
|
1. **Healthcheck Status** - Docker API: `docker inspect --format='{{.State.Health.Status}}'`
|
||||||
|
2. **Container Metrics** - Docker stats: CPU%, Memory usage
|
||||||
|
3. **Environment Label** - Parse from Coolify project/environment structure
|
||||||
|
|
||||||
|
**Tier 2 - High Impact, Medium Effort**
|
||||||
|
4. **Preview Screenshot** - Playwright screenshot service triggered on deploy success
|
||||||
|
5. **Runtime Logs Link** - Deep link to Dozzle: `http://dozzle.nuc.lan/container/<name>`
|
||||||
|
6. **Rollback Button** - Coolify API supports rollback
|
||||||
|
|
||||||
|
**Tier 3 - Nice to Have**
|
||||||
|
7. **Build Cache Status** - Parse "Restored build cache" from logs
|
||||||
|
8. **Image Size** - `docker images --format` after build
|
||||||
|
9. **Uptime Since Deploy** - Container start time from Docker
|
||||||
|
|
||||||
|
**Tier 4 - Future**
|
||||||
|
10. **Response Time** - Uptime Kuma integration
|
||||||
|
11. **Error Rate** - Log parsing or APM
|
||||||
|
12. **Git Diff Link** - Construct Gitea compare URL
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Proposed Implementation
|
||||||
|
|
||||||
|
### Route Structure
|
||||||
|
```
|
||||||
|
/deployments/[uuid] → Deployment dashboard page
|
||||||
|
```
|
||||||
|
|
||||||
|
### UI Changes to Deployments Table
|
||||||
|
- Add expand/collapse button (chevron icon) on each row
|
||||||
|
- Row click → navigate to dashboard
|
||||||
|
- Button click → expand logs inline (current behavior)
|
||||||
|
|
||||||
|
### Dashboard Page Sections (Simplified for Coolify)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ ← Back to Deployments rok0w0gg [Actions] │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ Created: alezmad · Feb 6, 2026 │
|
||||||
|
│ │ Preview │ Status: ● Ready Health: ● Healthy │
|
||||||
|
│ │ or Icon │ Duration: 2m 51s · 2h ago │
|
||||||
|
│ └──────────┘ Environment: production │
|
||||||
|
│ │
|
||||||
|
│ Domains: http://nuc.lan │
|
||||||
|
│ Source: main · f7c57ca · "Use domain-based URLs..." │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ ▼ Build Logs 43s ⚠2 ✓ │
|
||||||
|
│ [Timestamped log content...] │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ ▶ Container Info │
|
||||||
|
│ Build: nixpacks · Ports: 3000 │
|
||||||
|
│ CPU: 2.3% · Memory: 156MB │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||||
|
│ │ Runtime │ │ Redeploy │ │ Rollback │ │
|
||||||
|
│ │ Logs → │ │ │ │ │ │
|
||||||
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Endpoints Needed
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Existing (nuc-portal)
|
||||||
|
GET /api/deployments // List all
|
||||||
|
GET /api/deployments/[uuid] // Get one with logs
|
||||||
|
|
||||||
|
// New endpoints
|
||||||
|
GET /api/deployments/[uuid]/health // Container healthcheck
|
||||||
|
GET /api/deployments/[uuid]/stats // CPU/Memory from docker stats
|
||||||
|
POST /api/deployments/[uuid]/rollback // Trigger rollback
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Commands for New Features
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Healthcheck status
|
||||||
|
docker inspect --format='{{.State.Health.Status}}' <container>
|
||||||
|
|
||||||
|
# Container stats (one-shot)
|
||||||
|
docker stats --no-stream --format='{{.CPUPerc}},{{.MemUsage}}' <container>
|
||||||
|
|
||||||
|
# Container start time (for uptime)
|
||||||
|
docker inspect --format='{{.State.StartedAt}}' <container>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files to Modify
|
||||||
|
|
||||||
|
```
|
||||||
|
nuc-portal/
|
||||||
|
├── src/app/deployments/[uuid]/page.tsx # NEW - Dashboard page
|
||||||
|
├── src/components/DeploymentsTable.tsx # Add expand button, row click nav
|
||||||
|
├── src/components/DeploymentDashboard.tsx # NEW - Dashboard component
|
||||||
|
├── src/app/api/deployments/[uuid]/
|
||||||
|
│ ├── route.ts # Existing - add more fields
|
||||||
|
│ ├── health/route.ts # NEW - healthcheck endpoint
|
||||||
|
│ └── stats/route.ts # NEW - container stats
|
||||||
|
└── src/lib/docker.ts # NEW - Docker API helpers
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Order
|
||||||
|
|
||||||
|
### Phase 1: Basic Dashboard (MVP)
|
||||||
|
1. Create `/deployments/[uuid]` route and page
|
||||||
|
2. Add expand button to table rows
|
||||||
|
3. Make row click navigate to dashboard
|
||||||
|
4. Display existing deployment data in dashboard layout
|
||||||
|
|
||||||
|
### Phase 2: Enhanced Data (Tier 1 gaps)
|
||||||
|
5. Add healthcheck status endpoint
|
||||||
|
6. Add container metrics endpoint
|
||||||
|
7. Add environment label parsing
|
||||||
|
|
||||||
|
### Phase 3: Actions & Links (Tier 2 gaps)
|
||||||
|
8. Add Dozzle deep link
|
||||||
|
9. Add rollback button
|
||||||
|
10. (Optional) Screenshot service
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- Coolify API: `http://192.168.1.3:8000/api/v1/`
|
||||||
|
- Existing nuc-portal code: `/Users/agutierrez/Desktop/nuc/nuc-portal/`
|
||||||
|
- Deployment types: `nuc-portal/src/lib/deployments.ts`
|
||||||
|
- Coolify helpers: `nuc-portal/src/lib/coolify.ts`
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,173 @@
|
|||||||
|
# Deployment Dashboard Implementation
|
||||||
|
|
||||||
|
**Date:** 2026-02-06 19:30
|
||||||
|
**Project:** nuc-portal
|
||||||
|
**Context:** Implemented Vercel-style deployment detail pages with parallel wave-based execution
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Added a comprehensive deployment dashboard to nuc-portal that displays detailed information about individual deployments, including real-time container health, stats, build logs, and quick action buttons.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
User Flow:
|
||||||
|
Deployments Tab → Click Row → /deployments/[uuid] (Dashboard)
|
||||||
|
→ Click Chevron → Expand Logs Inline (preserved)
|
||||||
|
|
||||||
|
API Flow:
|
||||||
|
Dashboard Page
|
||||||
|
├── /api/deployments/[uuid] → Deployment details
|
||||||
|
├── /api/deployments/[uuid]/health → Container health (SWR 10s)
|
||||||
|
├── /api/deployments/[uuid]/stats → Container stats (SWR 10s)
|
||||||
|
└── /api/deployments/[uuid]/redeploy → Trigger new deployment
|
||||||
|
```
|
||||||
|
|
||||||
|
## Files Created
|
||||||
|
|
||||||
|
| File | Purpose | Lines |
|
||||||
|
|------|---------|-------|
|
||||||
|
| `src/app/deployments/[uuid]/page.tsx` | Dynamic route for deployment details | ~150 |
|
||||||
|
| `src/components/DeploymentDashboard.tsx` | Main dashboard component with tabs | ~800 |
|
||||||
|
| `src/components/DeploymentSkeleton.tsx` | Loading, error, empty state components | ~200 |
|
||||||
|
| `src/lib/docker.ts` | Docker API helpers via SSH | ~150 |
|
||||||
|
| `src/app/api/deployments/[uuid]/health/route.ts` | Container health endpoint | ~50 |
|
||||||
|
| `src/app/api/deployments/[uuid]/stats/route.ts` | Container stats endpoint | ~60 |
|
||||||
|
| `src/app/api/deployments/[uuid]/redeploy/route.ts` | Redeploy trigger endpoint | ~50 |
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
| File | Changes |
|
||||||
|
|------|---------|
|
||||||
|
| `src/components/DeploymentsTable.tsx` | Added expand button, row click navigation |
|
||||||
|
| `src/components/Icons.tsx` | Added missing icons (user, clock, share, etc.) |
|
||||||
|
| `src/components/index.ts` | Exported new components |
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Dashboard Tabs
|
||||||
|
1. **Deployment** - Main view with metadata, preview, and collapsible sections
|
||||||
|
2. **Logs** - Build logs with warning highlighting
|
||||||
|
3. **Resources** - CPU, Memory, Network I/O, Block I/O
|
||||||
|
4. **Source** - Git branch, commit, message
|
||||||
|
|
||||||
|
### Real-Time Data (SWR)
|
||||||
|
- Health status polling every 10 seconds
|
||||||
|
- Container stats polling every 10 seconds
|
||||||
|
- Auto-refresh with loading states
|
||||||
|
|
||||||
|
### Action Cards
|
||||||
|
| Card | Action | URL |
|
||||||
|
|------|--------|-----|
|
||||||
|
| Runtime Logs | Opens Dozzle | `http://192.168.1.3:9999/container/{name}` |
|
||||||
|
| Coolify | Opens Coolify deployment | `http://coolify.nuc.lan:8000/...` |
|
||||||
|
| Visit Site | Opens app FQDN | `{deployment.fqdn}` |
|
||||||
|
| Redeploy | Triggers new deployment | POST `/api/.../redeploy` |
|
||||||
|
|
||||||
|
### Edge Cases Handled
|
||||||
|
- `in_progress` - Shows "Building..." with amber banner
|
||||||
|
- `error` - Shows error banner, auto-expands logs
|
||||||
|
- `cancelled` - Shows grey cancelled state
|
||||||
|
- Missing container - Graceful degradation with messages
|
||||||
|
- Missing git info - Shows "—" instead of crashing
|
||||||
|
- Null duration - Shows "In progress..."
|
||||||
|
|
||||||
|
### Loading & Error States
|
||||||
|
- `DeploymentSkeleton` - Animated loading skeleton
|
||||||
|
- `DeploymentError` - Error with retry button
|
||||||
|
- `DeploymentEmpty` - Empty state for edge cases
|
||||||
|
|
||||||
|
## Docker API Helpers
|
||||||
|
|
||||||
|
`src/lib/docker.ts` provides:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Execute command via SSH to NUC
|
||||||
|
sshExec(command: string): Promise<string | null>
|
||||||
|
|
||||||
|
// Get container health status
|
||||||
|
getContainerHealth(containerName: string): Promise<'healthy' | 'unhealthy' | 'starting' | 'none' | null>
|
||||||
|
|
||||||
|
// Get container resource stats
|
||||||
|
getContainerStats(containerName: string): Promise<{
|
||||||
|
cpuPercent: number;
|
||||||
|
memoryUsage: string;
|
||||||
|
memoryLimit: string;
|
||||||
|
memoryPercent: number;
|
||||||
|
netIO: { rx: string; tx: string };
|
||||||
|
blockIO: { read: string; write: string };
|
||||||
|
} | null>
|
||||||
|
|
||||||
|
// Get container uptime
|
||||||
|
getContainerUptime(containerName: string): Promise<{
|
||||||
|
startedAt: string;
|
||||||
|
seconds: number;
|
||||||
|
formatted: string;
|
||||||
|
} | null>
|
||||||
|
|
||||||
|
// Find container by app name or UUID
|
||||||
|
findContainerByAppName(appName: string): Promise<string | null>
|
||||||
|
findContainerByUuid(appUuid: string): Promise<string | null>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Method
|
||||||
|
|
||||||
|
Used parallel wave-based execution with 11 total agents:
|
||||||
|
|
||||||
|
| Wave | Tasks | Agents | Duration |
|
||||||
|
|------|-------|--------|----------|
|
||||||
|
| Wave 1 | Route, Docker helpers, Table UI | 3 parallel | ~3.5 min |
|
||||||
|
| Wave 2 | Dashboard, Health API, Stats API, Navigation | 4 parallel | ~4.5 min |
|
||||||
|
| Wave 3 | Data integration, Links, Redeploy | 3 parallel | ~5 min |
|
||||||
|
| Wave 4 | Loading states, Edge cases | 2 parallel | ~3 min |
|
||||||
|
|
||||||
|
**Total implementation time:** ~16 minutes
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start dev server
|
||||||
|
cd /Users/agutierrez/Desktop/nuc/nuc-portal
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Get a deployment UUID
|
||||||
|
curl http://localhost:3000/api/deployments | jq '.[0].deployment_uuid'
|
||||||
|
|
||||||
|
# Test dashboard page
|
||||||
|
open http://localhost:3000/deployments/<uuid>
|
||||||
|
|
||||||
|
# Test API endpoints
|
||||||
|
curl http://localhost:3000/api/deployments/<uuid>/health | jq
|
||||||
|
curl http://localhost:3000/api/deployments/<uuid>/stats | jq
|
||||||
|
curl -X POST http://localhost:3000/api/deployments/<uuid>/redeploy | jq
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies Added
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"swr": "^2.x" // For data fetching with auto-refresh
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## URLs Configuration
|
||||||
|
|
||||||
|
Per user request, Coolify URLs use domain name:
|
||||||
|
- ✅ `http://coolify.nuc.lan:8000` (not IP)
|
||||||
|
- Dozzle still uses IP (no domain configured): `http://192.168.1.3:9999`
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
Dashboard follows Vercel's deployment page design:
|
||||||
|
- Header with app icon, metadata grid
|
||||||
|
- Collapsible sections (Settings, Build Logs, Container Stats, Summary)
|
||||||
|
- Action cards grid at bottom
|
||||||
|
- Tab navigation (Deployment, Logs, Resources, Source)
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- Design doc: `.artifacts/2026-02-06_17-30_deployment-dashboard-design.md`
|
||||||
|
- Implementation plan: `.artifacts/2026-02-06_18-00_deployment-dashboard-implementation-plan.md`
|
||||||
|
- nuc-portal repo: `/Users/agutierrez/Desktop/nuc/nuc-portal/`
|
||||||
340
CLAUDE.md
340
CLAUDE.md
@@ -8,14 +8,80 @@ ssh nuc
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Connection Details:**
|
**Connection Details:**
|
||||||
- Hostname: `192.168.1.3`
|
- Hostname: `192.168.1.3` (local) or `100.113.153.45` (Tailscale)
|
||||||
- User: `alezmad`
|
- User: `alezmad`
|
||||||
- SSH Key: `~/.ssh/id_ed25519_nuc`
|
- SSH Key: `~/.ssh/id_ed25519_nuc`
|
||||||
|
|
||||||
|
## DNS & Tailscale Setup
|
||||||
|
|
||||||
|
### Why Tailscale IP for DNS
|
||||||
|
|
||||||
|
All `.nuc.lan` domains resolve to the **Tailscale IP** (`100.113.153.45`) instead of the local IP (`192.168.1.3`). This ensures services work from **anywhere** regardless of your current network's subnet.
|
||||||
|
|
||||||
|
**Problem solved:** When connecting from a remote network that also uses `192.168.x.x`, traffic to `192.168.1.3` stays local instead of going through Tailscale. Using Tailscale IP (`100.x.x.x`) avoids this conflict.
|
||||||
|
|
||||||
|
### Configured Domains (OpenWrt Router DNS)
|
||||||
|
|
||||||
|
| Domain | Resolves To | Service |
|
||||||
|
|--------|-------------|---------|
|
||||||
|
| `nuc.lan` | `100.113.153.45` | NUC Portal |
|
||||||
|
| `nuc.local` | `100.113.153.45` | NUC Portal |
|
||||||
|
| `coolify.nuc.lan` | `100.113.153.45` | Coolify |
|
||||||
|
| `gitea.nuc.lan` | `100.113.153.45` | Gitea |
|
||||||
|
| `outline.nuc.lan` | `100.113.153.45` | Outline Wiki |
|
||||||
|
| `files.nuc.lan` | `100.113.153.45` | FileBrowser |
|
||||||
|
| `mail.nuc.lan` | `100.113.153.45` | Snappymail |
|
||||||
|
| `vault.nuc.lan` | `100.113.153.45` | Vaultwarden |
|
||||||
|
| `homepage.nuc.lan` | `100.113.153.45` | NUC Portal |
|
||||||
|
| `brand.nuc.lan` | `100.113.153.45` | Whyrating Brand |
|
||||||
|
| `templates.nuc.lan` | `100.113.153.45` | Whyrating Templates |
|
||||||
|
| `whyrating.nuc.lan` | `100.113.153.45` | Whyrating Hub |
|
||||||
|
|
||||||
|
### Traefik Routing (Dynamic Config)
|
||||||
|
|
||||||
|
Traefik routes domain-based requests to the correct backend. Config location: `/data/coolify/proxy/dynamic/nuc-services.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Routes for port-based services via domain names
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
coolify:
|
||||||
|
rule: Host(`coolify.nuc.lan`)
|
||||||
|
service: coolify
|
||||||
|
services:
|
||||||
|
coolify:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: http://host.docker.internal:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a New Domain
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Add DNS entry on router (via NUC jump host)
|
||||||
|
ssh nuc "ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 '
|
||||||
|
uci add dhcp domain
|
||||||
|
uci set dhcp.@domain[-1].name=\"newservice.nuc.lan\"
|
||||||
|
uci set dhcp.@domain[-1].ip=\"100.113.153.45\"
|
||||||
|
uci commit dhcp
|
||||||
|
/etc/init.d/dnsmasq restart
|
||||||
|
'"
|
||||||
|
|
||||||
|
# 2. Add Traefik route (if needed for port-based service)
|
||||||
|
# Edit /data/coolify/proxy/dynamic/nuc-services.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Always-On Tailscale
|
||||||
|
|
||||||
|
**Keep Tailscale running** - it's designed to be always-on:
|
||||||
|
- When on home network: Uses direct connection (no relay, same performance as local)
|
||||||
|
- When remote: Routes through Tailscale mesh
|
||||||
|
- Minimal resource usage (~0% CPU when idle)
|
||||||
|
|
||||||
## Service Management
|
## Service Management
|
||||||
|
|
||||||
### Coolify (Primary Service Manager)
|
### Coolify (Primary Service Manager)
|
||||||
All services are managed through Coolify at `http://192.168.1.3:8000`
|
All services are managed through Coolify at `http://coolify.nuc.lan` (or `http://100.113.153.45:8000`)
|
||||||
|
|
||||||
**Prefer using Coolify MCP** (`mcp__coolify__*`) for service management - it's faster and more reliable than SSH commands.
|
**Prefer using Coolify MCP** (`mcp__coolify__*`) for service management - it's faster and more reliable than SSH commands.
|
||||||
|
|
||||||
@@ -128,11 +194,114 @@ Task(subagent_type="general-purpose", prompt="Add services to Homepage...", desc
|
|||||||
| MCP | Purpose |
|
| MCP | Purpose |
|
||||||
|-----|---------|
|
|-----|---------|
|
||||||
| `mcp__coolify__*` | Service management, deployments, env vars |
|
| `mcp__coolify__*` | Service management, deployments, env vars |
|
||||||
|
| `mcp__stalwart-mail__*` | Email server management (users, domains, queue) |
|
||||||
|
| `mcp__email-client__*` | Read/send emails via IMAP/SMTP (see below) |
|
||||||
| `mcp__nocodb__*` | Database operations, table management |
|
| `mcp__nocodb__*` | Database operations, table management |
|
||||||
| `mcp__ssh-manager__*` | Direct SSH commands, file transfers |
|
| `mcp__ssh-manager__*` | Direct SSH commands, file transfers |
|
||||||
| `mcp__n8n__*` | Workflow automation (if configured) |
|
| `mcp__n8n__*` | Workflow automation (if configured) |
|
||||||
| `mcp__playwriter__*` | Browser automation fallback (see below) |
|
| `mcp__playwriter__*` | Browser automation fallback (see below) |
|
||||||
|
|
||||||
|
### Stalwart Mail MCP (Quick Guide)
|
||||||
|
|
||||||
|
**Location:** `~/mcp-servers/stalwart-mail/`
|
||||||
|
|
||||||
|
Manage the self-hosted Stalwart mail server via natural language.
|
||||||
|
|
||||||
|
**Available Tools:**
|
||||||
|
|
||||||
|
| Category | Tools |
|
||||||
|
|----------|-------|
|
||||||
|
| **Users** | `list_users`, `get_user`, `create_user`, `update_user_password`, `delete_user`, `add_email_alias` |
|
||||||
|
| **Domains** | `create_domain`, `generate_dkim` |
|
||||||
|
| **Queue** | `list_queue`, `get_queue_status`, `delete_queued_message`, `retry_queued_message` |
|
||||||
|
| **Monitoring** | `get_metrics`, `get_dmarc_reports`, `get_server_logs` |
|
||||||
|
| **DNS** | `check_dns_records`, `troubleshoot_delivery` |
|
||||||
|
| **Spam** | `train_spam`, `train_ham`, `update_spam_filter` |
|
||||||
|
|
||||||
|
**Usage Examples:**
|
||||||
|
```
|
||||||
|
"List all mail users"
|
||||||
|
"Create user sales with email sales@whyrating.com and password Secret123"
|
||||||
|
"Check the mail queue"
|
||||||
|
"Verify DNS records for whyrating.com"
|
||||||
|
"Show server metrics"
|
||||||
|
"Delete user john"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Direct API Test (if MCP not responding):**
|
||||||
|
```bash
|
||||||
|
curl -s -u "admin:QfKYjCJdxu" "http://192.168.1.3:8081/api/principal" | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reconfigure MCP:**
|
||||||
|
```bash
|
||||||
|
claude mcp remove stalwart-mail
|
||||||
|
claude mcp add stalwart-mail \
|
||||||
|
-e STALWART_URL=http://192.168.1.3:8081 \
|
||||||
|
-e STALWART_USER=admin \
|
||||||
|
-e STALWART_PASS=QfKYjCJdxu \
|
||||||
|
--scope user \
|
||||||
|
-- ~/mcp-servers/stalwart-mail/.venv/bin/python ~/mcp-servers/stalwart-mail/server.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ SMTP Authentication Requirements:**
|
||||||
|
1. **Password format:** Must be SHA-512 hashed (not plaintext). When creating users via API:
|
||||||
|
```python
|
||||||
|
import crypt
|
||||||
|
hashed = crypt.crypt('password', crypt.mksalt(crypt.METHOD_SHA512))
|
||||||
|
# Use hashed value in 'secrets' field
|
||||||
|
```
|
||||||
|
2. **SMTP login:** Use username only (e.g., `info`), NOT full email (`info@whyrating.com`)
|
||||||
|
3. **Port 465 (SMTPS):** Supports PLAIN/LOGIN auth with implicit TLS
|
||||||
|
4. **Port 587 (Submission):** Requires STARTTLS, only OAuth supported without TLS
|
||||||
|
|
||||||
|
**Send email via Python (from NUC):**
|
||||||
|
```python
|
||||||
|
import smtplib, ssl
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
context.check_hostname = False
|
||||||
|
context.verify_mode = ssl.CERT_NONE
|
||||||
|
|
||||||
|
with smtplib.SMTP_SSL('localhost', 465, context=context) as server:
|
||||||
|
server.login('info', 'whyrating2026') # Username only!
|
||||||
|
server.sendmail('info@whyrating.com', 'recipient@example.com', msg.as_string())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Email Client MCP (Read/Send Emails)
|
||||||
|
|
||||||
|
**Package:** [mcp-email-server](https://github.com/ai-zerolab/mcp-email-server)
|
||||||
|
|
||||||
|
Read and send emails via IMAP/SMTP directly from Claude.
|
||||||
|
|
||||||
|
**Configured for:** `info@whyrating.com` on Stalwart
|
||||||
|
|
||||||
|
**Usage Examples:**
|
||||||
|
```
|
||||||
|
"Check my inbox"
|
||||||
|
"Read the latest email"
|
||||||
|
"Send an email to john@example.com with subject Hello"
|
||||||
|
"Search emails from support@"
|
||||||
|
"List email folders"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Reconfigure:**
|
||||||
|
```bash
|
||||||
|
claude mcp remove email-client
|
||||||
|
claude mcp add email-client \
|
||||||
|
-e MCP_EMAIL_SERVER_EMAIL_ADDRESS=info@whyrating.com \
|
||||||
|
-e MCP_EMAIL_SERVER_PASSWORD=whyrating2026 \
|
||||||
|
-e MCP_EMAIL_SERVER_IMAP_HOST=192.168.1.3 \
|
||||||
|
-e MCP_EMAIL_SERVER_IMAP_PORT=143 \
|
||||||
|
-e MCP_EMAIL_SERVER_SMTP_HOST=192.168.1.3 \
|
||||||
|
-e MCP_EMAIL_SERVER_SMTP_PORT=587 \
|
||||||
|
-e MCP_EMAIL_SERVER_SMTP_VERIFY_SSL=false \
|
||||||
|
-e MCP_EMAIL_SERVER_ENABLE_ATTACHMENT_DOWNLOAD=true \
|
||||||
|
--scope user \
|
||||||
|
-- uvx mcp-email-server@latest stdio
|
||||||
|
```
|
||||||
|
|
||||||
### Adding Remote MCP Servers (HTTP Transport)
|
### Adding Remote MCP Servers (HTTP Transport)
|
||||||
|
|
||||||
**Use `claude mcp add --transport http` for remote MCP endpoints** - this is the recommended method for services with native MCP support.
|
**Use `claude mcp add --transport http` for remote MCP endpoints** - this is the recommended method for services with native MCP support.
|
||||||
@@ -261,24 +430,31 @@ ssh nuc "docker exec <container_name> <command>"
|
|||||||
|
|
||||||
## Services & Ports
|
## Services & Ports
|
||||||
|
|
||||||
| Service | Port | URL | Container |
|
**Preferred access via domain names** (works from anywhere via Tailscale):
|
||||||
|---------|------|-----|-----------|
|
|
||||||
| Homepage | 3000 | http://192.168.1.3:3000 | homepage-* |
|
| Service | Domain | Port-based URL | Container |
|
||||||
| Coolify | 8000 | http://192.168.1.3:8000 | coolify |
|
|---------|--------|----------------|-----------|
|
||||||
| Gitea | 3030 | http://192.168.1.3:3030 | gitea-* |
|
| NUC Portal | `http://nuc.lan` | - | nuc-portal-* |
|
||||||
| Outline | 3080 | http://192.168.1.3:3080 | outline-* |
|
| Coolify | `http://coolify.nuc.lan` | `http://100.113.153.45:8000` | coolify |
|
||||||
| NocoDB | 8084 | http://192.168.1.3:8084 | nocodb-* |
|
| Gitea | `http://gitea.nuc.lan` | `http://100.113.153.45:3030` | gitea-* |
|
||||||
| n8n | 5678 | http://192.168.1.3:5678 | n8n-* |
|
| Outline | `http://outline.nuc.lan` | `http://100.113.153.45:3080` | outline-* |
|
||||||
| Vaultwarden | 8222 | http://192.168.1.3:8222 | vaultwarden-* |
|
| FileBrowser | `http://files.nuc.lan` | `http://100.113.153.45:8085` | filebrowser-* |
|
||||||
| Ntfy | 8333 | http://192.168.1.3:8333 | ntfy-* |
|
| Snappymail | `http://mail.nuc.lan` | `http://100.113.153.45:8082` | snappymail-* |
|
||||||
| MinIO Console | 9001 | http://192.168.1.3:9001 | minio-* |
|
| Vaultwarden | `http://vault.nuc.lan` | `http://100.113.153.45:8222` | vaultwarden-* |
|
||||||
| MinIO API | 9000 | http://192.168.1.3:9000 | minio-* |
|
| Homepage | `http://homepage.nuc.lan` | `http://100.113.153.45:3000` | nuc-portal-* |
|
||||||
| Authentik | 9090 | http://192.168.1.3:9090 | authentik-* |
|
| NocoDB | - | `http://100.113.153.45:8084` | nocodb-* |
|
||||||
| FileBrowser | 8085 | http://192.168.1.3:8085 | filebrowser-* |
|
| n8n | - | `http://100.113.153.45:5678` | n8n-* |
|
||||||
| Adminer | 8088 | http://192.168.1.3:8088 | adminer |
|
| Ntfy | - | `http://100.113.153.45:8333` | ntfy-* |
|
||||||
| Uptime Kuma | 3001 | http://192.168.1.3:3001 | uptime-kuma |
|
| MinIO Console | - | `http://100.113.153.45:9001` | minio-* |
|
||||||
| Kopia | 51515 | http://192.168.1.3:51515 | kopia |
|
| MinIO API | - | `http://100.113.153.45:9000` | minio-* |
|
||||||
| Dozzle | 9999 | http://192.168.1.3:9999 | dozzle |
|
| Authentik | - | `http://100.113.153.45:9090` | authentik-* |
|
||||||
|
| Adminer | - | `http://100.113.153.45:8088` | adminer |
|
||||||
|
| Uptime Kuma | - | `http://100.113.153.45:3001` | uptime-kuma |
|
||||||
|
| Kopia | - | `http://100.113.153.45:51515` | kopia |
|
||||||
|
| Dozzle | - | `http://100.113.153.45:9999` | dozzle |
|
||||||
|
| CloudBeaver | - | `http://100.113.153.45:8978` | cloudbeaver-* |
|
||||||
|
|
||||||
|
**Note:** Use Tailscale IP (`100.113.153.45`) instead of `192.168.1.3` to avoid subnet conflicts when remote.
|
||||||
|
|
||||||
## Port Forwarding
|
## Port Forwarding
|
||||||
|
|
||||||
@@ -527,6 +703,63 @@ mcp__chrome-devtools__fill(uid="<uid>", value="<text>")
|
|||||||
- Token URL: `http://192.168.1.3:3030/login/oauth/access_token`
|
- Token URL: `http://192.168.1.3:3030/login/oauth/access_token`
|
||||||
- Userinfo URL: `http://192.168.1.3:3030/login/oauth/userinfo`
|
- Userinfo URL: `http://192.168.1.3:3030/login/oauth/userinfo`
|
||||||
|
|
||||||
|
## ⚠️ Critical Credentials & Access
|
||||||
|
|
||||||
|
### CloudBeaver (Database Manager)
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **URL** | `http://192.168.1.3:8978` |
|
||||||
|
| **Admin User** | `cbadmin` |
|
||||||
|
| **Admin Password** | `CloudBeaver2026!` |
|
||||||
|
| **Service UUID** | `joo4g4k0w08k8kcosgsgswc0` |
|
||||||
|
|
||||||
|
**Pre-configured connections:** 9 databases across 3 folders. Turbostarter DB is now in service `v4gogwwc8wkk4888ksscc4k4` (container: `db-v4gogwwc8wkk4888ksscc4k4`).
|
||||||
|
Connected to 7 Docker networks for direct container-to-container access.
|
||||||
|
|
||||||
|
### Vaultwarden (Password Manager)
|
||||||
|
|
||||||
|
**⚠️ CRITICAL: Vaultwarden REQUIRES HTTPS** - The Web Crypto API needs a secure context for client-side encryption. HTTP access will NOT work (blank page/loading forever).
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **HTTPS URL** | `https://nuc-tailscale.tail58f5ad.ts.net:8443` |
|
||||||
|
| **HTTP URL** | `http://192.168.1.3:8222` (won't load - HTTPS required) |
|
||||||
|
| **Admin Email** | `admin@nuc.lan` |
|
||||||
|
| **Admin Password** | `NucVault2026!Secure` |
|
||||||
|
|
||||||
|
**Access via Tailscale Funnel:**
|
||||||
|
```bash
|
||||||
|
# Vaultwarden is exposed on port 8443 via Tailscale Funnel
|
||||||
|
open "https://nuc-tailscale.tail58f5ad.ts.net:8443"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stalwart Mail Server
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Admin URL** | `http://192.168.1.3:8081` |
|
||||||
|
| **Username** | `admin` |
|
||||||
|
| **Password** | `QfKYjCJdxu` |
|
||||||
|
| **Webmail (Snappymail)** | `http://192.168.1.3:8082` |
|
||||||
|
| **Service UUID** | `kw00kok0w0s8gcok008gk04k` |
|
||||||
|
| **MCP Server** | `mcp__stalwart-mail__*` (see Quick Guide above) |
|
||||||
|
|
||||||
|
**Mail Users:**
|
||||||
|
| Email | Username | Password |
|
||||||
|
|-------|----------|----------|
|
||||||
|
| `info@whyrating.com` | `info` | `whyrating2026` |
|
||||||
|
|
||||||
|
**DNS Records Configured:** SPF, DKIM (Ed25519 + RSA), DMARC, MX for `whyrating.com`
|
||||||
|
|
||||||
|
**Container Status:** Running
|
||||||
|
|
||||||
|
### Gitea Users (for Outline OIDC)
|
||||||
|
|
||||||
|
| Username | Password | Notes |
|
||||||
|
|----------|----------|-------|
|
||||||
|
| `nedas` | `NedasNUC2026!` | Regular user account |
|
||||||
|
|
||||||
## Gitea-Coolify Integration (Git Auto-Deploy)
|
## Gitea-Coolify Integration (Git Auto-Deploy)
|
||||||
|
|
||||||
Deploy Next.js apps from self-hosted Gitea with auto-deploy on push. Full docs: `docs/gitea-coolify-auto-deploy.md`
|
Deploy Next.js apps from self-hosted Gitea with auto-deploy on push. Full docs: `docs/gitea-coolify-auto-deploy.md`
|
||||||
@@ -594,7 +827,7 @@ SSH config is set up for direct git operations:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone a repo
|
# Clone a repo
|
||||||
git clone gitea:nuc/nuc-portal.git
|
git clone gitea:alezmad/nuc-portal.git
|
||||||
|
|
||||||
# Push changes (triggers auto-deploy via webhook)
|
# Push changes (triggers auto-deploy via webhook)
|
||||||
git push origin main
|
git push origin main
|
||||||
@@ -634,7 +867,7 @@ mcp__coolify__application(
|
|||||||
project_uuid="a8484ggc88c40w4g4k004ow0",
|
project_uuid="a8484ggc88c40w4g4k004ow0",
|
||||||
environment_name="production",
|
environment_name="production",
|
||||||
server_uuid="qk84w0goo4w48g4ggsoo0oss",
|
server_uuid="qk84w0goo4w48g4ggsoo0oss",
|
||||||
git_repository="git@gitea-ho0cwgcwos88cwc48g84c0g8:nuc/<repo>.git",
|
git_repository="git@gitea-ho0cwgcwos88cwc48g84c0g8:alezmad/<repo>.git",
|
||||||
git_branch="main",
|
git_branch="main",
|
||||||
build_pack="nixpacks",
|
build_pack="nixpacks",
|
||||||
ports_exposes="3000",
|
ports_exposes="3000",
|
||||||
@@ -681,7 +914,7 @@ Add via Gitea: Repository → Settings → Deploy Keys → **Enable Write Access
|
|||||||
When creating a new repo that should auto-deploy:
|
When creating a new repo that should auto-deploy:
|
||||||
|
|
||||||
1. **[ ] Add deploy key to Gitea repo**
|
1. **[ ] Add deploy key to Gitea repo**
|
||||||
- Go to: `http://192.168.1.3:3030/nuc/<repo>/settings/keys`
|
- Go to: `http://192.168.1.3:3030/alezmad/<repo>/settings/keys`
|
||||||
- Add the deploy key above with **Write Access** enabled
|
- Add the deploy key above with **Write Access** enabled
|
||||||
|
|
||||||
2. **[ ] Create Coolify application** (use `mcp__coolify__application` with `action="create_key"`)
|
2. **[ ] Create Coolify application** (use `mcp__coolify__application` with `action="create_key"`)
|
||||||
@@ -691,7 +924,7 @@ When creating a new repo that should auto-deploy:
|
|||||||
4. **[ ] Set webhook secret** via tinker command (use shared secret above)
|
4. **[ ] Set webhook secret** via tinker command (use shared secret above)
|
||||||
|
|
||||||
5. **[ ] Create Gitea webhook**
|
5. **[ ] Create Gitea webhook**
|
||||||
- Go to: `http://192.168.1.3:3030/nuc/<repo>/settings/hooks`
|
- Go to: `http://192.168.1.3:3030/alezmad/<repo>/settings/hooks`
|
||||||
- Add Webhook → Gitea
|
- Add Webhook → Gitea
|
||||||
- **URL:** `http://coolify:8080/webhooks/source/gitea/events/manual?uuid=<app-uuid>`
|
- **URL:** `http://coolify:8080/webhooks/source/gitea/events/manual?uuid=<app-uuid>`
|
||||||
- **Secret:** `9eb07a77964563378c5d66d99006e06ba3da39d232905d4b12554ff91ca39718`
|
- **Secret:** `9eb07a77964563378c5d66d99006e06ba3da39d232905d4b12554ff91ca39718`
|
||||||
@@ -728,10 +961,59 @@ Coolify's "Gitea Source" uses GitHub App-style OAuth with JWT - **this doesn't w
|
|||||||
|
|
||||||
| App | URL | Repository | UUID |
|
| App | URL | Repository | UUID |
|
||||||
|-----|-----|------------|------|
|
|-----|-----|------------|------|
|
||||||
| nuc-portal | http://nuc.lan | `nuc/nuc-portal` | `t80w0cw0oooc4g0soswos4so` |
|
| nuc-portal | http://nuc.lan | `alezmad/nuc-portal` | `t80w0cw0oooc4g0soswos4so` |
|
||||||
| whyrating-hub | http://whyrating.nuc.lan | `nuc/whyrating-hub` | `vw4ggc40socwkgwg4osc8wg8` |
|
| whyrating-hub | http://whyrating.nuc.lan | `alezmad/whyrating-hub` | `vw4ggc40socwkgwg4osc8wg8` |
|
||||||
| whyrating-brand | http://brand.nuc.lan | `nuc/whyrating-brand` | `r80gk0ccgg0okos8cw848kkk` |
|
| whyrating-brand | http://brand.nuc.lan | `alezmad/whyrating-brand` | `r80gk0ccgg0okos8cw848kkk` |
|
||||||
| whyrating-templates | http://templates.nuc.lan | `nuc/whyrating-templates` | `qw80g4sog0kk8cc4wkcs8sgc` |
|
| whyrating-templates | http://templates.nuc.lan | `alezmad/whyrating-templates` | `qw80g4sog0kk8cc4wkcs8sgc` |
|
||||||
|
| turbostarter | https://alezmad-nuc.tail58f5ad.ts.net | `alezmad/turbostarter` | `v4gogwwc8wkk4888ksscc4k4` (service) |
|
||||||
|
|
||||||
|
### Turbostarter (Knosia) - Build & Deploy
|
||||||
|
|
||||||
|
Turbostarter is deployed as a **Coolify Service** (not a standalone app) with full docker-compose infrastructure: web + pgvector + minio.
|
||||||
|
|
||||||
|
**Architecture:** Tailscale Funnel (HTTPS) → Traefik (HTTP:80) → web container
|
||||||
|
|
||||||
|
**FQDN (Traefik):** `http://alezmad-nuc.tail58f5ad.ts.net` (HTTP internally — Tailscale handles TLS termination)
|
||||||
|
|
||||||
|
**Build & Deploy workflow:**
|
||||||
|
```bash
|
||||||
|
# 1. Build image locally (ARM→AMD cross-compile)
|
||||||
|
cd /Users/agutierrez/Desktop/turbostarter-export
|
||||||
|
docker build --platform linux/amd64 \
|
||||||
|
--build-arg NEXT_PUBLIC_URL=https://alezmad-nuc.tail58f5ad.ts.net \
|
||||||
|
-t 192.168.1.3:3030/alezmad/turbostarter:latest .
|
||||||
|
|
||||||
|
# 2. Push to Gitea registry
|
||||||
|
docker push 192.168.1.3:3030/alezmad/turbostarter:latest
|
||||||
|
|
||||||
|
# 3. Redeploy via Coolify (stop + start for full container recreation)
|
||||||
|
mcp__coolify__control(resource="service", action="stop", uuid="v4gogwwc8wkk4888ksscc4k4")
|
||||||
|
mcp__coolify__control(resource="service", action="start", uuid="v4gogwwc8wkk4888ksscc4k4")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Containers:**
|
||||||
|
| Container | Image | Purpose |
|
||||||
|
|-----------|-------|---------|
|
||||||
|
| `web-v4gogwwc8wkk4888ksscc4k4` | `localhost:3030/alezmad/turbostarter:latest` | Next.js app |
|
||||||
|
| `db-v4gogwwc8wkk4888ksscc4k4` | `pgvector/pgvector:pg17` | PostgreSQL + pgvector |
|
||||||
|
| `minio-v4gogwwc8wkk4888ksscc4k4` | `minio/minio:latest` | Object storage |
|
||||||
|
| `minio-init-v4gogwwc8wkk4888ksscc4k4` | `minio/mc:latest` | One-time bucket init |
|
||||||
|
|
||||||
|
**Database access (via SSH tunnel):**
|
||||||
|
```bash
|
||||||
|
# Get DB container IP first
|
||||||
|
ssh nuc "docker inspect db-v4gogwwc8wkk4888ksscc4k4 --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'"
|
||||||
|
# Then tunnel to IP (not container name)
|
||||||
|
ssh -L 5440:<container_ip>:5432 nuc
|
||||||
|
# Connect: postgres://turbostarter:turbostarter@localhost:5440/core
|
||||||
|
```
|
||||||
|
|
||||||
|
**Seeded users:** `me+admin@turbostarter.dev` / `Pa$$w0rd` (admin), `me+user@turbostarter.dev` / `Pa$$w0rd`
|
||||||
|
|
||||||
|
**Key env vars:**
|
||||||
|
- `BETTER_AUTH_TRUSTED_ORIGINS` — comma-separated list of allowed origins (CSRF protection)
|
||||||
|
- `NEXT_PUBLIC_URL` — build-time arg baked into Next.js static output (must rebuild to change)
|
||||||
|
- `DATABASE_URL` — internal docker network connection to pgvector
|
||||||
|
|
||||||
### New Site from nuc-portal Template
|
### New Site from nuc-portal Template
|
||||||
|
|
||||||
@@ -751,7 +1033,7 @@ rm -rf .git .next node_modules
|
|||||||
npm install && npm run build # verify it builds
|
npm install && npm run build # verify it builds
|
||||||
git init && git add -A && git commit -m "Initial commit"
|
git init && git add -A && git commit -m "Initial commit"
|
||||||
# Create repo in Gitea first, then:
|
# Create repo in Gitea first, then:
|
||||||
git remote add origin gitea:nuc/<repo-name>.git
|
git remote add origin gitea:alezmad/<repo-name>.git
|
||||||
git push -u origin main
|
git push -u origin main
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user