refactor: Rebrand from 777wolfpack/CA Grow Ops to Veridian
- 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:
parent
3aa349a63f
commit
ca8a3e8cee
17 changed files with 258 additions and 862 deletions
|
|
@ -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)
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
|
||||||
17
CI-CD.md
17
CI-CD.md
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
627
DEPLOYMENT.md
627
DEPLOYMENT.md
|
|
@ -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:
|
||||||
|
|
||||||
|
1. **Run the deploy script**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# From nexus-vector or local machine with Tailscale
|
./deploy.sh test # or prod
|
||||||
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
|
2. **Follow the prompts**:
|
||||||
|
- The script will set up the remote directory.
|
||||||
```bash
|
- It will clone the repository.
|
||||||
# From local machine (current directory: ca-grow-ops-manager)
|
- It will generate secure credentials (`docker-compose.env`) if missing.
|
||||||
cd /Users/ten/ANTIGRAVITY/777wolfpack/ca-grow-ops-manager
|
- It will build and start the containers.
|
||||||
|
|
||||||
# Initialize Git if not already done
|
|
||||||
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
|
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
||||||
201
deploy.sh
201
deploy.sh
|
|
@ -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
|
||||||
git remote add origin https://git.runfoo.run/malty/ca-grow-ops-manager.git
|
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}"
|
echo -e "${GREEN}✓ Remote added${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}✓ Remote 'origin' exists${NC}"
|
||||||
|
fi
|
||||||
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}"
|
||||||
|
ENV_FILE="$DEPLOY_PATH/docker-compose.env"
|
||||||
|
HAS_ENV=$(ssh "$USER@$HOST" "[ -f $ENV_FILE ] && echo 'yes' || echo 'no'")
|
||||||
|
|
||||||
|
if [ "$HAS_ENV" = "no" ]; then
|
||||||
|
echo -e "${YELLOW}Creating new environment file on remote...${NC}"
|
||||||
DB_PASSWORD=$(openssl rand -base64 32)
|
DB_PASSWORD=$(openssl rand -base64 32)
|
||||||
JWT_SECRET=$(openssl rand -base64 64)
|
JWT_SECRET=$(openssl rand -base64 64)
|
||||||
|
|
||||||
ssh admin@nexus-vector "cat > /srv/containers/ca-grow-ops-manager/docker-compose.env << 'EOF'
|
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}"
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue