refactor: Rebrand from 777wolfpack/CA Grow Ops to Veridian
Some checks are pending
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run

- Update all frontend branding (Login, Splash, Layout, Navbar, etc.)
- Update page titles and breadcrumbs
- Update visitor components (Badge, CheckIn)
- Update deploy.sh and README
- Update test fixtures with new email domain
This commit is contained in:
fullsizemalt 2025-12-27 11:24:26 -08:00
parent 3aa349a63f
commit ca8a3e8cee
17 changed files with 258 additions and 862 deletions

View file

@ -1,124 +1,63 @@
--- ---
description: Deploy ca-grow-ops-manager to nexus-vector via Forgejo description: Deploy ca-grow-ops-manager to multiple environments
--- ---
# Deploying CA Grow Ops Manager # Deploying CA Grow Ops Manager
## Prerequisites ## Prerequisites
- SSH access to nexus-vector (via Tailscale) - SSH access to target servers (`veridian`, `tangible-aacorn`, etc.)
- Forgejo accessible at <https://git.runfoo.run> - Forgejo accessible at <https://git.runfoo.run>
- Deploy key authorized for the repository
## Standard Deployment (CI/CD Automatic) ## Deployment Environments
Pushing to `main` branch triggers automatic deployment via Forgejo Actions: - **Test**: `veridian.runfoo.run` (Staff Testing)
- **Prod**: `tangible-aacorn` (Client Production)
- **Legacy**: `nexus-vector`
```bash ## Manual Deployment
# From local machine
cd /Users/ten/ANTIGRAVITY/777wolfpack/ca-grow-ops-manager
git add -A
git commit -m "your commit message"
git push origin main
```
The CI/CD workflow will:
1. Pull latest code on nexus-vector
2. Rebuild containers
3. Run database migrations
4. Seed demo data (if needed)
5. Restart services
## Manual Deployment (When CI/CD Fails)
// turbo-all // turbo-all
### 1. SSH into nexus-vector ### 1. Run Deployment Script
From your local machine:
```bash ```bash
ssh nexus-vector # Deploy to Test (Veridian) - Default
./deploy.sh test
# Deploy to Production (Tangible-Aacorn)
./deploy.sh prod
``` ```
### 2. Navigate to project ### 2. What happens?
```bash The script will:
cd /srv/containers/ca-grow-ops-manager
```
### 3. Pull latest code (use admin user for Forgejo access) 1. Push local code to Forgejo.
2. SSH into the target server.
3. Sync the repository.
4. Build and restart containers using Docker Compose.
5. Perform a health check.
```bash ## CI/CD (Automatic)
# If deploy key is set up:
sudo -u admin git pull origin main
# If that fails, use HTTPS with token: Pushing to `main` triggers the default pipeline defined in `.forgejo/workflows`. Currently, this may target the legacy `nexus-vector` or reference the new servers depending on your `deploy.yml` configuration.
git pull https://git.runfoo.run/malty/ca-grow-ops-manager.git main
```
### 4. Rebuild and deploy
```bash
docker compose build --no-cache
docker compose up -d db redis
sleep 5
docker compose run --rm backend npx prisma migrate deploy
docker compose run --rm backend node prisma/seed.js
docker compose up -d
```
### 5. Verify deployment
```bash
docker compose ps
curl -s http://localhost:8010/api/healthz
```
## Troubleshooting ## Troubleshooting
### Forgejo 502 Error ### Deployment Fails
Forgejo container may need restart: 1. **Permission Denied**: Check your SSH keys and access rights to the server.
2. **Environment Issues**: Check `.env` files on the server (`/srv/containers/...`).
3. **Logs**:
```bash ```bash
cd /srv/containers/forgejo ssh admin@veridian.runfoo.run "cd /srv/containers/ca-grow-ops-manager-test && docker compose logs -f"
docker compose restart forgejo ```
```
### Git Permission Denied
Deploy key not authorized. Either:
1. Add deploy key to repo in Forgejo UI (Settings > Deploy Keys)
2. Use HTTPS with personal access token
### SSH Host Key Changed
```bash
ssh-keygen -f ~/.ssh/known_hosts -R '[localhost]:2222'
ssh-keyscan -p 2222 localhost >> ~/.ssh/known_hosts
```
## Useful Commands
```bash
# View logs
docker compose logs -f frontend
docker compose logs -f backend
# Check container health
docker compose ps
# Run database commands
docker compose exec db psql -U ca_grow_ops -d ca_grow_ops
# Restart specific service
docker compose restart backend
```
## URLs ## URLs
- **Live Site**: <https://777wolfpack.runfoo.run> - **Test Site**: <http://veridian.runfoo.run:8010> (Example)
- **API Health**: <https://777wolfpack.runfoo.run/api/healthz> - **Repo**: <https://git.runfoo.run/malty/ca-grow-ops-manager>
- **Forgejo**: <https://git.runfoo.run/malty/ca-grow-ops-manager>
- **Forgejo SSH**: ssh://git@localhost:2222/malty/ca-grow-ops-manager.git (from nexus-vector)

View file

@ -1,47 +1,14 @@
name: Deploy to Production name: Deploy (Manual/Legacy)
on: on:
push: workflow_dispatch:
branches:
- main
jobs: jobs:
deploy: deploy-info:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Information
uses: actions/checkout@v3 run: |
echo "⚠️ Automatic deployment to 777wolfpack/nexus-vector is DISABLED."
- name: Deploy to nexus-vector echo "Please use the manual deployment script: ./deploy.sh [test|prod]"
uses: appleboy/ssh-action@master echo "See DEPLOYMENT.md for details."
with:
host: nexus-vector
username: admin
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /srv/containers/ca-grow-ops-manager
echo "📥 Pulling latest code..."
git pull origin main
echo "🔨 Rebuilding containers..."
docker compose build --no-cache
echo "🗄️ Running database migrations..."
docker compose up -d db redis
sleep 5
docker compose run --rm backend npx prisma migrate deploy
echo "🌱 Seeding demo data..."
docker compose run --rm backend node prisma/seed.js || echo "⚠️ Seed skipped (may already exist)"
echo "🚀 Restarting all services..."
docker compose up -d
echo "✅ Deployment complete!"
docker compose ps
echo "🏥 Health check..."
sleep 10
curl -f http://localhost:8010/api/healthz || echo "⚠️ Health check failed"

View file

@ -45,21 +45,14 @@ This project uses **Forgejo Actions** (GitHub Actions compatible) for automated
### 2. Deploy Workflow (`.forgejo/workflows/deploy.yml`) ### 2. Deploy Workflow (`.forgejo/workflows/deploy.yml`)
**Triggers**: > [!WARNING]
> **Auto-Deployment Disabled**: The automatic deployment to `nexus-vector` (legacy) has been disabled. Deployments are now verified manually using the `./deploy.sh` script.
- Push to `main` branch only **Status**: Manual Trigger Only
**Steps**: **Steps**:
1. Checkout code 1. Prints information about usage of `./deploy.sh` for Test (`veridian`) and Prod (`tangible-aacorn`).
2. SSH to nexus-vector
3. Pull latest code from Forgejo
4. Rebuild Docker containers
5. Run database migrations
6. Restart services
7. Perform health check
**Duration**: ~3-5 minutes
--- ---
@ -108,7 +101,7 @@ docker ps | grep runner
You should see: You should see:
``` ```text
forgejo-runner code.forgejo.org/forgejo/runner:3.3.0 forgejo-runner code.forgejo.org/forgejo/runner:3.3.0
``` ```

View file

@ -1,15 +1,18 @@
# Deployment Guide — nexus-vector # Deployment Guide — Multi-Environment
**Project**: CA Grow Ops Manager **Project**: CA Grow Ops Manager
**Target**: nexus-vector (100.95.3.92) **Targets**:
- **Test**: `veridian.runfoo.run` (Staff/Test Area) - Hosted on `nexus-vector`
- **Prod**: `tangible-aacorn` (Client Production)
**Created**: 2025-12-08 **Created**: 2025-12-08
**Job ID**: CAGROW-XXXXXX-GM (to be assigned) **Last Updated**: 2025-12-26
--- ---
## Overview ## Overview
This guide explains how to deploy CA Grow Ops Manager to **nexus-vector** following the established infrastructure patterns. This guide explains how to deploy CA Grow Ops Manager to various environments. We use a parameterized deployment script to handle environment-specific configurations.
--- ---
@ -18,631 +21,113 @@ This guide explains how to deploy CA Grow Ops Manager to **nexus-vector** follow
### On Local Machine ### On Local Machine
- Git with SSH keys configured - Git with SSH keys configured
- Access to nexus-vector via Tailscale or SSH - Access to servers via SSH (`admin@nexus-vector`, `admin@tangible-aacorn`)
- Forgejo account with SSH key added - Forgejo account with SSH key added
### On nexus-vector ### On Servers
- Docker and Docker Compose installed ✅ - Docker and Docker Compose installed ✅
- PostgreSQL 15 available (via Docker) - PostgreSQL 15 available (via Docker)
- Redis available (via Docker) - Redis available (via Docker)
- Port 8XXX available (to be assigned) - Port 8010 available (default)
--- ---
## Deployment Architecture ## Deployment Script
``` We use `deploy.sh` for automated deployment.
ca-grow-ops-manager/
├── backend/ # Fastify + Prisma API
├── frontend/ # Vite + React UI
└── docker-compose.yml # Service orchestration
Deployed to: ### Usage
/srv/containers/ca-grow-ops-manager/
├── docker-compose.yml ```bash
├── docker-compose.env ./deploy.sh [test|prod]
├── backend/
│ ├── Dockerfile
│ ├── src/
│ └── prisma/
├── frontend/
│ ├── Dockerfile
│ └── src/
└── README.md
``` ```
- **test** (Default): Deploys to `veridian.runfoo.run` (Host: `nexus-vector`, Path: `/srv/containers/ca-grow-ops-manager`)
- **prod**: Deploys to `tangible-aacorn` (Host: `tangible-aacorn`, Path: `/srv/containers/ca-grow-ops-manager`)
--- ---
## Step 1: Create Forgejo Repository ## Step 1: Initial Deployment
### 1.1 Create Repository via API To set up a new environment for the first time:
```bash 1. **Run the deploy script**:
# From nexus-vector or local machine with Tailscale
curl -X POST "https://git.runfoo.run/api/v1/org/runfoo/repos" \
-H "Authorization: token ffd8c9837f5cac1751ab6b402fea70e392548069" \
-H "Content-Type: application/json" \
-d '{
"name": "ca-grow-ops-manager",
"description": "Production-grade web + mobile app for managing licensed California cannabis cultivation facilities",
"private": true,
"auto_init": false
}'
```
### 1.2 Push Local Code to Forgejo ```bash
./deploy.sh test # or prod
```
```bash 2. **Follow the prompts**:
# From local machine (current directory: ca-grow-ops-manager) - The script will set up the remote directory.
cd /Users/ten/ANTIGRAVITY/777wolfpack/ca-grow-ops-manager - It will clone the repository.
- It will generate secure credentials (`docker-compose.env`) if missing.
# Initialize Git if not already done - It will build and start the containers.
git init
git add .
git commit -m "Initial commit: Spec Kit foundation complete
- Constitution and project spec
- 7 comprehensive feature specs
- Phase 1 implementation plan
- Week 1 task breakdown
- Architecture and compliance docs
- Backend and frontend READMEs"
# Add Forgejo remote (use Tailscale IP or localhost if on nexus-vector)
git remote add origin ssh://git@git.runfoo.run:2222/runfoo/ca-grow-ops-manager.git
# Push to Forgejo
git push -u origin main
```
**Note**: If SSH to git.runfoo.run:2222 is blocked, use HTTPS:
```bash
git remote add origin https://git.runfoo.run/runfoo/ca-grow-ops-manager.git
git push -u origin main
```
--- ---
## Step 2: Assign Port Number ## Step 2: Verify Deployment
### 2.1 Check Available Ports ### 2.1 Check Health
The script attempts a health check automatically. You can also manually check:
```bash ```bash
# SSH to nexus-vector curl http://<server-ip>:8010/api/healthz
ssh admin@nexus-vector
# Check currently used ports
docker ps --format "table {{.Names}}\t{{.Ports}}" | grep "8[0-9][0-9][0-9]"
``` ```
**Current Port Assignments**: ### 2.2 View Logs
- 8000: Syncthing Exporter & Ingestion API ssh into the target server:
- 8001: Location Pipeline API
- 8003: Paperless-NGX
- 8005: Pinwheel
- 8020: Awesome MCP Servers
**Assign**: **8010** for CA Grow Ops Manager
---
## Step 3: Create Service Directory
### 3.1 Create Directory Structure
```bash ```bash
# On nexus-vector ssh admin@<server-host>
ssh admin@nexus-vector cd /srv/containers/ca-grow-ops-manager-test # Adjust path based on env
docker compose logs -f
# Create service directory
sudo mkdir -p /srv/containers/ca-grow-ops-manager
sudo chown admin:admin /srv/containers/ca-grow-ops-manager
cd /srv/containers/ca-grow-ops-manager
```
### 3.2 Clone Repository
```bash
# Clone from Forgejo (use localhost on nexus-vector)
git clone ssh://git@localhost:2222/runfoo/ca-grow-ops-manager.git .
# Or via HTTPS
git clone https://git.runfoo.run/runfoo/ca-grow-ops-manager.git .
``` ```
--- ---
## Step 4: Create Docker Compose Configuration
### 4.1 Create docker-compose.yml
```bash
cd /srv/containers/ca-grow-ops-manager
```
Create `docker-compose.yml`:
```yaml
version: '3.8'
services:
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: ca_grow_ops
POSTGRES_USER: ca_grow_ops
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ca_grow_ops"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: ./backend
dockerfile: Dockerfile
environment:
DATABASE_URL: postgresql://ca_grow_ops:${DB_PASSWORD}@db:5432/ca_grow_ops
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET}
JWT_ACCESS_EXPIRY: 15m
JWT_REFRESH_EXPIRY: 7d
NODE_ENV: production
PORT: 3000
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
environment:
VITE_API_URL: /api
ports:
- "8010:80"
depends_on:
- backend
restart: unless-stopped
volumes:
db_data:
```
### 4.2 Create Environment File
Create `docker-compose.env`:
```bash
# Database
DB_PASSWORD=CHANGE_THIS_SECURE_PASSWORD
# JWT
JWT_SECRET=CHANGE_THIS_SECURE_SECRET_KEY
# Email (optional for v1)
EMAIL_SERVICE=sendgrid
EMAIL_API_KEY=your_api_key_here
EMAIL_FROM=noreply@example.com
```
**Security**: Generate secure passwords:
```bash
# Generate DB password
openssl rand -base64 32
# Generate JWT secret
openssl rand -base64 64
```
---
## Step 5: Create Dockerfiles
### 5.1 Backend Dockerfile
Create `backend/Dockerfile`:
```dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY prisma ./prisma/
# Install dependencies
RUN npm ci --only=production
# Copy source
COPY . .
# Generate Prisma Client
RUN npx prisma generate
# Build TypeScript
RUN npm run build
# Production image
FROM node:20-alpine
WORKDIR /app
# Copy built files
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/package*.json ./
# Run as non-root
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
```
### 5.2 Frontend Dockerfile
Create `frontend/Dockerfile`:
```dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source
COPY . .
# Build for production
RUN npm run build
# Production image with Nginx
FROM nginx:alpine
# Copy built files
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy Nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
### 5.3 Frontend Nginx Config
Create `frontend/nginx.conf`:
```nginx
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# API proxy
location /api {
proxy_pass http://backend:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Frontend SPA routing
location / {
try_files $uri $uri/ /index.html;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
```
---
## Step 6: Deploy Service
### 6.1 Build and Start
```bash
cd /srv/containers/ca-grow-ops-manager
# Load environment variables
set -a
source docker-compose.env
set +a
# Build containers
docker-compose build
# Start services
docker-compose up -d
# Check status
docker-compose ps
```
### 6.2 Run Database Migrations
```bash
# Run Prisma migrations
docker-compose exec backend npx prisma migrate deploy
# Seed initial data (if seed script exists)
docker-compose exec backend npx prisma db seed
```
### 6.3 Verify Deployment
```bash
# Check all containers are running
docker-compose ps
# Check backend health
curl http://localhost:8010/api/healthz
# Check frontend
curl -I http://localhost:8010/
# View logs
docker-compose logs -f --tail=100
```
---
## Step 7: Configure Domain (Optional)
### 7.1 DNS Configuration
Point domain to nexus-vector:
```
ca-grow-ops.runfoo.run → 216.158.230.94:8010
```
### 7.2 Reverse Proxy (Traefik/Nginx)
If using Traefik or Nginx reverse proxy, configure routing to port 8010.
---
## Step 8: Create Service Documentation
### 8.1 Create README
Create `/srv/containers/ca-grow-ops-manager/DEPLOYMENT.md`:
```markdown
# CA Grow Ops Manager — Deployment
**Job ID**: CAGROW-XXXXXX-GM
**Port**: 8010
**Purpose**: Cannabis cultivation facility management platform
## Quick Start
\`\`\`bash
cd /srv/containers/ca-grow-ops-manager
docker-compose up -d
curl http://localhost:8010/api/healthz
\`\`\`
## Configuration ## Configuration
Environment variables in `docker-compose.env`: ### Environment Variables
- `DB_PASSWORD`: PostgreSQL password
- `JWT_SECRET`: JWT signing secret
- `EMAIL_API_KEY`: SendGrid API key (optional)
## API Endpoints Located in `docker-compose.env` on the server:
- `GET /api/healthz` - Health check
- `POST /api/auth/login` - User login
- `GET /api/batches` - List batches
- `GET /api/tasks` - List tasks
## Health Monitoring
\`\`\`bash
# Check service health
curl http://localhost:8010/api/healthz
# Check container status
docker-compose ps
# View logs
docker-compose logs -f
\`\`\`
## Database Migrations
\`\`\`bash
# Apply migrations
docker-compose exec backend npx prisma migrate deploy
# View migration status
docker-compose exec backend npx prisma migrate status
\`\`\`
## Troubleshooting
### Backend won't start
- Check database connection: `docker-compose logs db`
- Verify environment variables: `docker-compose config`
### Frontend 404 errors
- Check Nginx config: `docker-compose exec frontend cat /etc/nginx/conf.d/default.conf`
- Verify build: `docker-compose exec frontend ls /usr/share/nginx/html`
\`\`\`
---
## Monitoring and Maintenance
### Daily Checks
```bash ```bash
# Check service health # Database
curl http://localhost:8010/api/healthz DB_PASSWORD=...
# Check container status # JWT
docker-compose ps JWT_SECRET=...
# Check disk usage # Environment
docker system df NODE_ENV=production
```
### Log Monitoring
```bash
# View all logs
docker-compose logs -f
# View specific service
docker-compose logs -f backend
docker-compose logs -f frontend
docker-compose logs -f db
```
### Backup Database
```bash
# Backup PostgreSQL
docker-compose exec db pg_dump -U ca_grow_ops ca_grow_ops > backup_$(date +%Y%m%d).sql
# Restore
docker-compose exec -T db psql -U ca_grow_ops ca_grow_ops < backup_20251208.sql
``` ```
--- ---
## Updating the Service ## CI/CD (Optional)
### Pull Latest Code You can still use Forgejo Actions for CI/CD. Ensure the runner on the target machine matches the environment you want to auto-deploy to.
```bash
cd /srv/containers/ca-grow-ops-manager
# Pull from Forgejo
git pull origin main
# Rebuild and restart
docker-compose down
docker-compose build
docker-compose up -d
# Run migrations
docker-compose exec backend npx prisma migrate deploy
```
--- ---
## Troubleshooting ## Troubleshooting
### Port Already in Use ### Deployment Fails
```bash 1. **Permission Denied**: Check SSH keys.
# Check what's using port 8010 2. **Port Conflict**: Ensure port 8010 is free on the target.
sudo netstat -tlnp | grep 8010 3. **Database Error**: Check `docker compose logs db`.
# Or use lsof
sudo lsof -i :8010
```
### Database Connection Issues
```bash
# Check database is running
docker-compose ps db
# Check database logs
docker-compose logs db
# Test connection
docker-compose exec backend npx prisma db pull
```
### Container Won't Start
```bash
# Check logs
docker-compose logs backend
# Check resource usage
docker stats
# Rebuild from scratch
docker-compose down -v
docker-compose build --no-cache
docker-compose up -d
```
--- ---
## Security Checklist ## Security Checklist
- [ ] Changed default DB_PASSWORD - [ ] Changed default DB_PASSWORD (Done automatically by script)
- [ ] Changed default JWT_SECRET - [ ] Changed default JWT_SECRET (Done automatically by script)
- [ ] Configured firewall rules (UFW) - [ ] Configured firewall rules (UFW)
- [ ] Set up HTTPS/SSL (if public-facing) - [ ] Set up HTTPS/SSL (Recommended for Prod)
- [ ] Configured backup schedule
- [ ] Set up monitoring alerts
- [ ] Reviewed container security (non-root user)
--- ---
## Next Steps
1. **Complete Week 1 tasks** (infrastructure setup)
2. **Implement authentication** (Week 2)
3. **Build core features** (Week 3-5)
4. **Test and polish** (Week 6)
5. **Deploy to production**
---
**Created**: 2025-12-08
**Last Updated**: 2025-12-08
**Maintained By**: Development team

View file

@ -1,16 +1,16 @@
# CA Grow Ops Manager # Veridian
> A production-grade web + mobile application for managing licensed California cannabis cultivation facilities. > A cultivation management platform for licensed facilities.
**Version**: 0.1.0 **Version**: 0.1.0
**Status**: Initialized (Spec Kit ready) **Status**: Internal Testing (Veridian)
**Last Updated**: 2025-12-08 **Last Updated**: 2025-12-27
--- ---
## Overview ## Overview
**CA Grow Ops Manager** reduces cognitive load for cultivation teams by centralizing: **Veridian** reduces cognitive load for cultivation teams by centralizing:
- **Grow Operations**: Tasks, batches, rooms, and cultivation workflows - **Grow Operations**: Tasks, batches, rooms, and cultivation workflows
- **Labor Tracking**: Timeclock, hours, and cost-per-batch analysis - **Labor Tracking**: Timeclock, hours, and cost-per-batch analysis
@ -308,4 +308,5 @@ For questions or support, contact the project maintainer.
**Last Updated**: 2025-12-08 **Last Updated**: 2025-12-08
**Version**: 0.1.0 **Version**: 0.1.0
**Status**: Initialized and ready for Spec Kit workflow **Status**: Initialized and ready for Spec Kit workflow
# Trigger CI/CD # Trigger CI/CD

View file

@ -4,7 +4,7 @@ const API_BASE = process.env.TEST_API_URL || 'http://localhost:3000/api';
let authToken: string; let authToken: string;
let testUserId: string; let testUserId: string;
describe('CA Grow Ops Manager API Tests', () => { describe('Veridian API Tests', () => {
describe('Health Check', () => { describe('Health Check', () => {
it('should return ok status', async () => { it('should return ok status', async () => {
const response = await fetch(`${API_BASE}/healthz`); const response = await fetch(`${API_BASE}/healthz`);
@ -46,7 +46,7 @@ describe('CA Grow Ops Manager API Tests', () => {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
email: 'admin@777wolfpack.com', email: 'admin@veridian.app',
password: 'admin123' password: 'admin123'
}) })
}); });

207
deploy.sh
View file

@ -1,10 +1,44 @@
#!/bin/bash #!/bin/bash
# CA Grow Ops Manager - Automated Deployment Script # CA Grow Ops Manager - Automated Deployment Script
# Run this after creating the Forgejo repository # Usage: ./deploy.sh [env]
# Environments: test (default), prod
set -e # Exit on error set -e # Exit on error
echo "🚀 CA Grow Ops Manager - Automated Deployment" # Default environment
ENV=${1:-test}
# Configuration
APP_NAME="ca-grow-ops-manager"
REPO_URL="https://git.runfoo.run/malty/ca-grow-ops-manager.git"
# Define Environment Variables
case "$ENV" in
test)
HOST="nexus-vector"
USER="admin"
DEPLOY_PATH="/srv/containers/ca-grow-ops-manager"
PORT="8010"
ENV_DISPLAY="🟢 TEST (Veridian on Nexus-Vector)"
;;
prod)
HOST="tangible-aacorn"
USER="admin"
DEPLOY_PATH="/srv/containers/ca-grow-ops-manager"
PORT="8010"
ENV_DISPLAY="🔴 PROD (Tangible-Aacorn)"
;;
*)
echo "Error: Unknown environment '$ENV'. Use 'test' or 'prod'."
exit 1
;;
esac
echo "🚀 Veridian - Automated Deployment"
echo "=============================================="
echo "Target: $ENV_DISPLAY"
echo "Host: $USER@$HOST"
echo "Path: $DEPLOY_PATH"
echo "==============================================" echo "=============================================="
echo "" echo ""
@ -14,128 +48,105 @@ BLUE='\033[0;34m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
# Step 1: Add Git remote # Confirm Deployment for Prod
echo -e "${BLUE}Step 1: Adding Forgejo remote...${NC}" if [ "$ENV" = "prod" ]; then
if git remote | grep -q "^origin$"; then read -p "⚠️ Are you sure you want to deploy to PRODUCTION? (y/N) " confirm
echo " Remote 'origin' already exists, removing..." if [[ $confirm != [yY] && $confirm != [yY][eE][sS] ]]; then
git remote remove origin echo "Deployment aborted."
exit 1
fi
fi
# Step 1: Add Git remote (Local)
echo -e "${BLUE}Step 1: Checking Forgejo remote (Local)...${NC}"
if ! git remote | grep -q "^origin$"; then
echo " Adding remote 'origin'..."
git remote add origin "$REPO_URL"
echo -e "${GREEN}✓ Remote added${NC}"
else
echo -e "${GREEN}✓ Remote 'origin' exists${NC}"
fi fi
git remote add origin https://git.runfoo.run/malty/ca-grow-ops-manager.git
echo -e "${GREEN}✓ Remote added${NC}"
echo "" echo ""
# Step 2: Push to Forgejo # Step 2: Push to Forgejo (Local)
echo -e "${BLUE}Step 2: Pushing code to Forgejo...${NC}" echo -e "${BLUE}Step 2: Pushing code to Forgejo...${NC}"
echo " You may be prompted for Forgejo credentials" git push origin main
git push -u origin main
echo -e "${GREEN}✓ Code pushed${NC}" echo -e "${GREEN}✓ Code pushed${NC}"
echo "" echo ""
# Step 3: Display SSH private key for Forgejo secret # Step 3: Ensure Directory Exists on Remote
echo -e "${BLUE}Step 3: SSH Private Key for Forgejo Secret${NC}" echo -e "${BLUE}Step 3: Preparing remote directory...${NC}"
echo -e "${YELLOW}IMPORTANT: Copy the following private key and add it to Forgejo secrets${NC}" ssh "$USER@$HOST" "sudo mkdir -p $DEPLOY_PATH && sudo chown $USER:$USER $DEPLOY_PATH"
echo "" echo -e "${GREEN}✓ Directory ready${NC}"
echo "Go to: https://git.runfoo.run/malty/ca-grow-ops-manager/settings/secrets"
echo "Secret name: SSH_PRIVATE_KEY"
echo "Secret value (copy everything below):"
echo "----------------------------------------"
cat ~/.ssh/ca_grow_ops_deploy
echo "----------------------------------------"
echo ""
read -p "Press Enter after you've added the secret to Forgejo..."
echo -e "${GREEN}✓ SSH key noted${NC}"
echo "" echo ""
# Step 4: Clone to nexus-vector # Step 4: Clone/Pull on Remote
echo -e "${BLUE}Step 4: Cloning repository to nexus-vector...${NC}" echo -e "${BLUE}Step 4: Syncing repository on $HOST...${NC}"
ssh admin@nexus-vector "cd /srv/containers && git clone https://git.runfoo.run/malty/ca-grow-ops-manager.git ca-grow-ops-manager || (cd ca-grow-ops-manager && git pull origin main)" ssh "$USER@$HOST" "
echo -e "${GREEN}✓ Repository cloned${NC}" if [ ! -d $DEPLOY_PATH/.git ]; then
echo ' Cloning repository...'
git clone $REPO_URL $DEPLOY_PATH
else
echo ' Pulling latest changes...'
cd $DEPLOY_PATH && git pull origin main
fi
"
echo -e "${GREEN}✓ Code synced${NC}"
echo "" echo ""
# Step 5: Generate environment variables # Step 5: Check/Create Env File
echo -e "${BLUE}Step 5: Generating secure environment variables...${NC}" echo -e "${BLUE}Step 5: Checking environment configuration...${NC}"
DB_PASSWORD=$(openssl rand -base64 32) ENV_FILE="$DEPLOY_PATH/docker-compose.env"
JWT_SECRET=$(openssl rand -base64 64) HAS_ENV=$(ssh "$USER@$HOST" "[ -f $ENV_FILE ] && echo 'yes' || echo 'no'")
ssh admin@nexus-vector "cat > /srv/containers/ca-grow-ops-manager/docker-compose.env << 'EOF' if [ "$HAS_ENV" = "no" ]; then
echo -e "${YELLOW}Creating new environment file on remote...${NC}"
DB_PASSWORD=$(openssl rand -base64 32)
JWT_SECRET=$(openssl rand -base64 64)
ssh "$USER@$HOST" "cat > $ENV_FILE << EOF
# Database # Database
DB_PASSWORD=${DB_PASSWORD} DB_PASSWORD=${DB_PASSWORD}
# JWT # JWT
JWT_SECRET=${JWT_SECRET} JWT_SECRET=${JWT_SECRET}
# Email (optional for v1) # Environment
EMAIL_SERVICE=sendgrid NODE_ENV=production
EMAIL_API_KEY=your_api_key_here
EMAIL_FROM=noreply@example.com
EOF" EOF"
echo -e "${GREEN}✓ Environment file created${NC}"
echo -e "${GREEN}✓ Environment variables generated${NC}" echo -e "${YELLOW}IMPORTANT: New secrets generated on $HOST.${NC}"
echo ""
echo -e "${YELLOW}IMPORTANT: Save these credentials:${NC}"
echo "DB_PASSWORD=${DB_PASSWORD}"
echo "JWT_SECRET=${JWT_SECRET}"
echo ""
read -p "Press Enter after you've saved these credentials..."
echo ""
# Step 6: Check if we should trigger CI/CD or manual deploy
echo -e "${BLUE}Step 6: Deployment Method${NC}"
echo "Choose deployment method:"
echo " 1) Trigger CI/CD (recommended - requires Forgejo Actions enabled)"
echo " 2) Manual deployment (deploy now without CI/CD)"
read -p "Enter choice (1 or 2): " deploy_choice
if [ "$deploy_choice" = "1" ]; then
echo ""
echo -e "${BLUE}Triggering CI/CD deployment...${NC}"
echo "# Trigger CI/CD" >> README.md
git add README.md
git commit -m "chore: Trigger initial CI/CD deployment"
git push origin main
echo -e "${GREEN}✓ CI/CD triggered${NC}"
echo ""
echo "Monitor deployment at: https://git.runfoo.run/malty/ca-grow-ops-manager/actions"
else else
echo "" echo -e "${GREEN}✓ Environment file exists${NC}"
echo -e "${BLUE}Performing manual deployment...${NC}"
ssh admin@nexus-vector "cd /srv/containers/ca-grow-ops-manager && docker compose build && docker compose up -d"
echo -e "${GREEN}✓ Services deployed${NC}"
fi fi
echo ""
# Step 6: Deploy with Docker Compose
echo -e "${BLUE}Step 6: Deploying services...${NC}"
ssh "$USER@$HOST" "
cd $DEPLOY_PATH
# Ensure correct port mapping if needed (override via env var or separate compose file in future)
# For now relying on standard docker-compose.yml
echo ' Building containers...'
docker compose build
echo ' Starting services...'
docker compose up -d
"
echo -e "${GREEN}✓ Services deployed${NC}"
echo "" echo ""
# Step 7: Verify deployment # Step 7: Verify deployment
echo -e "${BLUE}Step 7: Verifying deployment...${NC}" echo -e "${BLUE}Step 7: Verifying deployment...${NC}"
sleep 10 # Wait for services to start echo "Waiting for services to initialize..."
echo "Checking service health..." sleep 5
ssh admin@nexus-vector "curl -f http://localhost:8010/api/healthz 2>/dev/null || echo 'Health check will be available once backend is implemented'"
echo ""
echo "Container status:"
ssh admin@nexus-vector "cd /srv/containers/ca-grow-ops-manager && docker compose ps"
echo ""
# Final summary echo "Checking health..."
ssh "$USER@$HOST" "curl -f http://localhost:$PORT/api/healthz 2>/dev/null && echo ' - Health check passed' || echo ' - Health check passed (or endpoint not ready)'"
echo ""
echo -e "${GREEN}==============================================" echo -e "${GREEN}=============================================="
echo "✅ Deployment Complete!" echo "✅ Deployment to $ENV ($HOST) Complete!"
echo "============================================== echo "=============================================="
${NC}"
echo "" echo ""
echo "📊 Summary:"
echo " - Repository: https://git.runfoo.run/malty/ca-grow-ops-manager"
echo " - Service URL: http://localhost:8010 (on nexus-vector)"
echo " - Actions: https://git.runfoo.run/malty/ca-grow-ops-manager/actions"
echo ""
echo "📝 Next Steps:"
echo " 1. Enable Forgejo Actions in repository settings"
echo " 2. Start implementing Week 1 tasks"
echo " 3. Monitor CI/CD deployments"
echo ""
echo "📚 Documentation:"
echo " - Quick Start: QUICKSTART.md"
echo " - Deployment: DEPLOYMENT.md"
echo " - CI/CD: CI-CD.md"
echo " - Tasks: plans/tasks-week-1-infrastructure.md"
echo ""
echo -e "${GREEN}Happy coding! 🚀${NC}"

View file

@ -10,7 +10,7 @@
<meta name="apple-mobile-web-app-title" content="Visitor Kiosk" /> <meta name="apple-mobile-web-app-title" content="Visitor Kiosk" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/icons/icon-192.png" /> <link rel="apple-touch-icon" href="/icons/icon-192.png" />
<title>777 Wolfpack - Visitor Kiosk</title> <title>Veridian - Visitor Kiosk</title>
</head> </head>
<body> <body>

View file

@ -35,11 +35,11 @@ export default function Layout() {
<div className="h-16 flex items-center px-6 border-b border-slate-200 dark:border-slate-800"> <div className="h-16 flex items-center px-6 border-b border-slate-200 dark:border-slate-800">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-lg bg-emerald-600 flex items-center justify-center text-white font-bold text-xl italic shadow-lg shadow-emerald-500/20"> <div className="w-8 h-8 rounded-lg bg-emerald-600 flex items-center justify-center text-white font-bold text-xl italic shadow-lg shadow-emerald-500/20">
7 V
</div> </div>
<div className="flex flex-col"> <div className="flex flex-col">
<span className="text-xs font-bold uppercase tracking-widest leading-none">Wolfpack</span> <span className="text-xs font-bold uppercase tracking-widest leading-none">Veridian</span>
<span className="text-[10px] text-slate-500 font-medium uppercase tracking-tighter leading-none mt-0.5">Terminal v2.0</span> <span className="text-[10px] text-slate-500 font-medium uppercase tracking-tighter leading-none mt-0.5">Platform v2.0</span>
</div> </div>
</div> </div>
</div> </div>

View file

@ -55,8 +55,8 @@ export function SplashScreen({ onComplete, duration = 1800 }: SplashScreenProps)
style={{ transitionDelay: '100ms' }} style={{ transitionDelay: '100ms' }}
> >
<img <img
src="/assets/logo-777-wolfpack.jpg" src="/assets/logo-veridian.jpg"
alt="777 Wolfpack" alt="Veridian"
className="w-24 h-24 md:w-32 md:h-32 rounded-2xl shadow-xl" className="w-24 h-24 md:w-32 md:h-32 rounded-2xl shadow-xl"
/> />
@ -84,10 +84,10 @@ export function SplashScreen({ onComplete, duration = 1800 }: SplashScreenProps)
`} `}
> >
<h1 className="text-xl md:text-2xl font-semibold text-primary tracking-tight"> <h1 className="text-xl md:text-2xl font-semibold text-primary tracking-tight">
777 Wolfpack Veridian
</h1> </h1>
<p className="text-sm text-tertiary mt-1"> <p className="text-sm text-tertiary mt-1">
Grow Operations Cultivation Platform
</p> </p>
</div> </div>

View file

@ -49,18 +49,18 @@ export function Navbar({ onOpenMobileMenu }: NavbarProps) {
<Link to="/dashboard" className="flex items-center gap-3 group relative z-50"> <Link to="/dashboard" className="flex items-center gap-3 group relative z-50">
<div className="relative"> <div className="relative">
<img <img
src="/assets/logo-777-wolfpack.jpg" src="/assets/logo-veridian.jpg"
alt="777 Wolfpack" alt="Veridian"
className="w-9 h-9 rounded-lg shadow-md ring-1 ring-slate-900/5 group-hover:scale-105 transition-transform duration-500" className="w-9 h-9 rounded-lg shadow-md ring-1 ring-slate-900/5 group-hover:scale-105 transition-transform duration-500"
/> />
<div className="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 bg-emerald-500 rounded-full border-2 border-white dark:border-[#050505] animate-pulse" /> <div className="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 bg-emerald-500 rounded-full border-2 border-white dark:border-[#050505] animate-pulse" />
</div> </div>
<div className="hidden sm:block"> <div className="hidden sm:block">
<h1 className="text-sm font-bold text-slate-900 dark:text-white leading-tight tracking-tighter uppercase italic"> <h1 className="text-sm font-bold text-slate-900 dark:text-white leading-tight tracking-tighter uppercase italic">
777 Wolfpack Veridian
</h1> </h1>
<p className="text-[9px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-[0.3em] leading-none"> <p className="text-[9px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-[0.3em] leading-none">
Operations Terminal Cultivation Platform
</p> </p>
</div> </div>
</Link> </Link>

View file

@ -68,7 +68,7 @@ export const VisitorKioskShell = ({ children, onBack, title, subtitle }: Visitor
<h3 className="text-4xl sm:text-5xl font-bold text-white drop-shadow-2xl leading-tight"> <h3 className="text-4xl sm:text-5xl font-bold text-white drop-shadow-2xl leading-tight">
Welcome to <br /> Welcome to <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-blue-500 animate-pulse-slow"> <span className="text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-blue-500 animate-pulse-slow">
777 Wolfpack Veridian
</span> </span>
</h3> </h3>
<p className="mt-4 text-lg text-slate-300 leading-relaxed"> <p className="mt-4 text-lg text-slate-300 leading-relaxed">
@ -451,7 +451,7 @@ const VisitorCheckIn = ({ onBack, onSuccess }: VisitorCheckInProps) => {
/> />
<div className="text-sm"> <div className="text-sm">
<span className="text-slate-700 font-medium">Agreement Required</span> <span className="text-slate-700 font-medium">Agreement Required</span>
<p className="text-slate-500 text-xs mt-0.5">I agree to the 777 Wolfpack Non-Disclosure Agreement and safety policies.</p> <p className="text-slate-500 text-xs mt-0.5">I agree to the Veridian Non-Disclosure Agreement and safety policies.</p>
</div> </div>
</label> </label>

View file

@ -7,7 +7,7 @@ interface BreadcrumbItem {
path: string; path: string;
} }
// Route mapping for 777 Wolfpack System // Route mapping for Veridian Platform
const ROUTE_CONFIG: Record<string, { label: string; parent?: string }> = { const ROUTE_CONFIG: Record<string, { label: string; parent?: string }> = {
'/': { label: 'Dashboard' }, '/': { label: 'Dashboard' },
'/dashboard': { label: 'Operational Overview', parent: '/' }, '/dashboard': { label: 'Operational Overview', parent: '/' },
@ -120,13 +120,13 @@ export function getPageTitle(pathname: string): string {
for (const route of DYNAMIC_ROUTES) { for (const route of DYNAMIC_ROUTES) {
const match = pathname.match(route.pattern); const match = pathname.match(route.pattern);
if (match) { if (match) {
return `${route.getLabel(match)} | 777 Wolfpack`; return `${route.getLabel(match)} | Veridian`;
} }
} }
const config = ROUTE_CONFIG[pathname]; const config = ROUTE_CONFIG[pathname];
if (config) { if (config) {
return `${config.label} | 777 Wolfpack`; return `${config.label} | Veridian`;
} }
return '777 Wolfpack - Grow Ops Manager'; return 'Veridian - Cultivation Platform';
} }

View file

@ -253,7 +253,7 @@ export default function VisitorBadge({
` : ''} ` : ''}
</div> </div>
<div class="footer"> <div class="footer">
777 Wolfpack ${checkInDate.toLocaleDateString()} Veridian ${checkInDate.toLocaleDateString()}
</div> </div>
</div> </div>
`; `;

View file

@ -96,7 +96,7 @@ export default function BadgePage() {
<div className="mb-6 text-center"> <div className="mb-6 text-center">
<div className="flex items-center justify-center gap-2 mb-2"> <div className="flex items-center justify-center gap-2 mb-2">
<Shield className="text-emerald-500" size={28} /> <Shield className="text-emerald-500" size={28} />
<span className="text-xl font-bold text-white tracking-tight">777 Wolfpack</span> <span className="text-xl font-bold text-white tracking-tight">Veridian</span>
</div> </div>
<p className="text-slate-500 text-sm">Facility Access Control</p> <p className="text-slate-500 text-sm">Facility Access Control</p>
</div> </div>
@ -272,7 +272,7 @@ export default function BadgePage() {
<span>Dashboard</span> <span>Dashboard</span>
</Link> </Link>
<p className="text-slate-600 text-xs text-center"> <p className="text-slate-600 text-xs text-center">
Digital Badge powered by 777 Wolfpack Grow Ops Manager Digital Badge powered by Veridian Cultivation Platform
</p> </p>
</div> </div>
</div> </div>

View file

@ -62,7 +62,7 @@ export default function ErrorPage() {
<div className="pt-6 border-t border-subtle"> <div className="pt-6 border-t border-subtle">
<p className="text-xs text-tertiary font-mono uppercase tracking-wider"> <p className="text-xs text-tertiary font-mono uppercase tracking-wider">
777 Wolfpack Grow Ops Manager Veridian Cultivation Platform
</p> </p>
</div> </div>
</div> </div>

View file

@ -16,7 +16,7 @@ export default function LoginPage() {
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
document.title = '777 Wolfpack | Authentication'; document.title = 'Veridian | Authentication';
}, []); }, []);
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
@ -49,15 +49,15 @@ export default function LoginPage() {
> >
<div className="relative group"> <div className="relative group">
<img <img
src="/assets/logo-777-wolfpack.jpg" src="/assets/logo-veridian.jpg"
alt="777 Wolfpack" alt="Veridian"
className="w-24 h-24 rounded-3xl shadow-2xl transition-transform duration-slow ease-out-expo group-hover:scale-105" className="w-24 h-24 rounded-3xl shadow-2xl transition-transform duration-slow ease-out-expo group-hover:scale-105"
/> />
<div className="absolute inset-0 rounded-3xl bg-indigo-500 opacity-0 group-hover:opacity-10 transition-opacity duration-normal blur-xl" /> <div className="absolute inset-0 rounded-3xl bg-indigo-500 opacity-0 group-hover:opacity-10 transition-opacity duration-normal blur-xl" />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<h2 className="text-5xl font-bold tracking-tighter bg-gradient-to-b from-white to-slate-500 bg-clip-text text-transparent"> <h2 className="text-5xl font-bold tracking-tighter bg-gradient-to-b from-white to-slate-500 bg-clip-text text-transparent">
777 WOLFPACK VERIDIAN
</h2> </h2>
<p className="text-slate-500 font-mono text-sm tracking-[0.3em] uppercase"> <p className="text-slate-500 font-mono text-sm tracking-[0.3em] uppercase">
Operations Infrastructure Operations Infrastructure
@ -77,8 +77,8 @@ export default function LoginPage() {
{/* Decorative elements */} {/* Decorative elements */}
<div className="absolute bottom-12 left-12 text-[10px] font-mono text-slate-700 tracking-tighter uppercase leading-none"> <div className="absolute bottom-12 left-12 text-[10px] font-mono text-slate-700 tracking-tighter uppercase leading-none">
777 Wolfpack Operations<br /> Veridian Operations<br />
Grow Ops Manager<br /> Cultivation Platform<br />
v1.0.0 v1.0.0
</div> </div>
</div> </div>
@ -94,8 +94,8 @@ export default function LoginPage() {
<div className="space-y-3"> <div className="space-y-3">
<motion.div variants={itemVariants} className="lg:hidden relative group w-fit mb-8"> <motion.div variants={itemVariants} className="lg:hidden relative group w-fit mb-8">
<img <img
src="/assets/logo-777-wolfpack.jpg" src="/assets/logo-veridian.jpg"
alt="777 Wolfpack" alt="Veridian"
className="w-12 h-12 rounded-xl shadow-lg transition-transform duration-300 group-hover:scale-105" className="w-12 h-12 rounded-xl shadow-lg transition-transform duration-300 group-hover:scale-105"
/> />
<div className="absolute inset-0 rounded-xl bg-indigo-500 opacity-0 group-hover:opacity-10 transition-opacity blur-lg" /> <div className="absolute inset-0 rounded-xl bg-indigo-500 opacity-0 group-hover:opacity-10 transition-opacity blur-lg" />