- Constitution and project spec (spec.yml) - 7 comprehensive feature specs (tasks, batches, labor, compliance, inventory, integrations, comms) - Phase 1 implementation plan (6-week roadmap) - Week 1 task breakdown (15 concrete tasks) - Architecture and compliance documentation - Backend and frontend setup guides - Deployment guide for nexus-vector - CI/CD workflows (Forgejo Actions) - Quick start guide for developers Project is ready for implementation with: - Automated testing on every push - Automatic deployment to nexus-vector on push to main - Database migrations handled automatically - Health checks and monitoring Stack: TypeScript, Fastify, React, Vite, PostgreSQL, Prisma, Docker
397 lines
8.7 KiB
Markdown
397 lines
8.7 KiB
Markdown
# CA Grow Ops Manager — Backend
|
|
|
|
**Version**: 0.1.0
|
|
**Stack**: TypeScript, Node.js, Express/Fastify, PostgreSQL, Prisma
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
The backend is a **feature-first, domain-driven** Node.js application that provides typed HTTP/JSON APIs for all platform features. It uses Prisma ORM for type-safe database access and follows strict security and testing practices.
|
|
|
|
---
|
|
|
|
## Tech Stack
|
|
|
|
- **Language**: TypeScript 5.x
|
|
- **Runtime**: Node.js 20.x LTS
|
|
- **Framework**: Express or Fastify (to be decided during implementation)
|
|
- **Database**: PostgreSQL 15.x
|
|
- **ORM**: Prisma 5.x
|
|
- **Authentication**: JWT (access + refresh tokens)
|
|
- **Testing**: Jest + Supertest
|
|
- **Validation**: Zod or Joi
|
|
- **Logging**: Winston or Pino
|
|
- **Email**: SendGrid or Mailgun
|
|
|
|
---
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
backend/
|
|
├── src/
|
|
│ ├── modules/ # Feature-first modules
|
|
│ │ ├── cultivation-ops/ # Tasks, batches, rooms
|
|
│ │ │ ├── tasks/
|
|
│ │ │ │ ├── tasks.controller.ts
|
|
│ │ │ │ ├── tasks.service.ts
|
|
│ │ │ │ ├── tasks.schema.ts
|
|
│ │ │ │ └── tasks.test.ts
|
|
│ │ │ ├── batches/
|
|
│ │ │ └── rooms/
|
|
│ │ ├── compliance/ # Documents, audit packets
|
|
│ │ ├── labor/ # Timeclock, hours, reports
|
|
│ │ ├── inventory/ # Materials, lots, transactions
|
|
│ │ ├── integrations/ # METRC, environmental, hardware
|
|
│ │ ├── communications/ # Comments, announcements, notifications
|
|
│ │ └── auth/ # Authentication, authorization
|
|
│ ├── shared/ # Shared utilities
|
|
│ │ ├── middleware/ # Auth, validation, error handling
|
|
│ │ ├── utils/ # Helpers, formatters
|
|
│ │ └── types/ # Shared TypeScript types
|
|
│ ├── prisma/ # Prisma schema and migrations
|
|
│ │ ├── schema.prisma
|
|
│ │ └── migrations/
|
|
│ ├── config/ # Configuration
|
|
│ │ ├── database.ts
|
|
│ │ ├── auth.ts
|
|
│ │ └── email.ts
|
|
│ ├── app.ts # Express/Fastify app setup
|
|
│ └── server.ts # Server entry point
|
|
├── tests/ # Integration tests
|
|
│ ├── setup.ts
|
|
│ └── integration/
|
|
├── .env.example # Environment variables template
|
|
├── package.json
|
|
├── tsconfig.json
|
|
└── README.md
|
|
```
|
|
|
|
---
|
|
|
|
## Domain Modules
|
|
|
|
Each module follows this structure:
|
|
|
|
```
|
|
module-name/
|
|
├── entity.controller.ts # HTTP route handlers
|
|
├── entity.service.ts # Business logic
|
|
├── entity.schema.ts # Validation schemas (Zod/Joi)
|
|
├── entity.test.ts # Unit tests
|
|
└── index.ts # Module exports
|
|
```
|
|
|
|
### Example: Tasks Module
|
|
|
|
```typescript
|
|
// tasks.controller.ts
|
|
export const getTasks = async (req, res) => {
|
|
const tasks = await tasksService.findAll(req.query);
|
|
res.json(tasks);
|
|
};
|
|
|
|
// tasks.service.ts
|
|
export const findAll = async (filters) => {
|
|
return prisma.task.findMany({ where: filters });
|
|
};
|
|
|
|
// tasks.schema.ts
|
|
export const createTaskSchema = z.object({
|
|
name: z.string().min(1),
|
|
dueDate: z.string().datetime(),
|
|
assigneeId: z.string().optional(),
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## API Design
|
|
|
|
### RESTful Conventions
|
|
|
|
- `GET /api/tasks` - List tasks
|
|
- `GET /api/tasks/:id` - Get task detail
|
|
- `POST /api/tasks` - Create task
|
|
- `PATCH /api/tasks/:id` - Update task
|
|
- `DELETE /api/tasks/:id` - Delete task
|
|
|
|
### Response Format
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": { ... },
|
|
"meta": {
|
|
"page": 1,
|
|
"pageSize": 20,
|
|
"total": 100
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error Format
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Invalid input",
|
|
"details": [
|
|
{ "field": "dueDate", "message": "Required" }
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Authentication & Authorization
|
|
|
|
### JWT-Based Auth
|
|
|
|
- **Access Token**: 15-minute expiry, stored in httpOnly cookie
|
|
- **Refresh Token**: 7-day expiry, stored in localStorage
|
|
- **Token Payload**: `{ userId, roleId, iat, exp }`
|
|
|
|
### RBAC (Role-Based Access Control)
|
|
|
|
Roles:
|
|
|
|
- `Owner`: Full access
|
|
- `Compliance Manager`: Full compliance, read-only operations
|
|
- `Head Grower`: Full cultivation ops, read-only compliance
|
|
- `Staff`: Limited access to tasks and timeclock
|
|
- `Accountant`: Read-only reports
|
|
|
|
Middleware:
|
|
|
|
```typescript
|
|
export const requireRole = (roles: string[]) => {
|
|
return (req, res, next) => {
|
|
if (!roles.includes(req.user.role)) {
|
|
return res.status(403).json({ error: 'Forbidden' });
|
|
}
|
|
next();
|
|
};
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Database (Prisma)
|
|
|
|
### Schema Example
|
|
|
|
```prisma
|
|
model Task {
|
|
id String @id @default(cuid())
|
|
name String
|
|
description String?
|
|
status TaskStatus @default(PENDING)
|
|
dueDate DateTime
|
|
assigneeId String?
|
|
assignee User? @relation(fields: [assigneeId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
enum TaskStatus {
|
|
PENDING
|
|
IN_PROGRESS
|
|
COMPLETE
|
|
OVERDUE
|
|
}
|
|
```
|
|
|
|
### Migrations
|
|
|
|
```bash
|
|
# Create migration
|
|
npx prisma migrate dev --name add_tasks_table
|
|
|
|
# Apply migrations
|
|
npx prisma migrate deploy
|
|
|
|
# Generate Prisma Client
|
|
npx prisma generate
|
|
```
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Unit Tests (Jest)
|
|
|
|
```typescript
|
|
describe('TasksService', () => {
|
|
it('should create a task', async () => {
|
|
const task = await tasksService.create({
|
|
name: 'Water plants',
|
|
dueDate: new Date(),
|
|
});
|
|
expect(task.name).toBe('Water plants');
|
|
});
|
|
});
|
|
```
|
|
|
|
### Integration Tests (Supertest)
|
|
|
|
```typescript
|
|
describe('POST /api/tasks', () => {
|
|
it('should create a task', async () => {
|
|
const res = await request(app)
|
|
.post('/api/tasks')
|
|
.send({ name: 'Water plants', dueDate: '2025-12-10' })
|
|
.expect(201);
|
|
expect(res.body.data.name).toBe('Water plants');
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
```bash
|
|
# Database
|
|
DATABASE_URL=postgresql://user:password@localhost:5432/ca_grow_ops
|
|
|
|
# Auth
|
|
JWT_SECRET=your-secret-key
|
|
JWT_ACCESS_EXPIRY=15m
|
|
JWT_REFRESH_EXPIRY=7d
|
|
|
|
# Email
|
|
EMAIL_SERVICE=sendgrid
|
|
EMAIL_API_KEY=your-api-key
|
|
EMAIL_FROM=noreply@example.com
|
|
|
|
# Integrations
|
|
METRC_API_KEY=your-metrc-key
|
|
METRC_API_URL=https://api-ca.metrc.com
|
|
|
|
# Server
|
|
PORT=3000
|
|
NODE_ENV=development
|
|
```
|
|
|
|
---
|
|
|
|
## Development Workflow
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# Set up environment variables
|
|
cp .env.example .env
|
|
|
|
# Run database migrations
|
|
npx prisma migrate dev
|
|
|
|
# Seed database (optional)
|
|
npx prisma db seed
|
|
|
|
# Start dev server
|
|
npm run dev
|
|
```
|
|
|
|
### Scripts
|
|
|
|
```json
|
|
{
|
|
"scripts": {
|
|
"dev": "tsx watch src/server.ts",
|
|
"build": "tsc",
|
|
"start": "node dist/server.js",
|
|
"test": "jest",
|
|
"test:watch": "jest --watch",
|
|
"test:coverage": "jest --coverage",
|
|
"lint": "eslint src --ext .ts",
|
|
"format": "prettier --write src",
|
|
"migrate": "prisma migrate dev",
|
|
"migrate:deploy": "prisma migrate deploy",
|
|
"prisma:generate": "prisma generate"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Security Best Practices
|
|
|
|
- **Input Validation**: Validate all inputs with Zod/Joi
|
|
- **SQL Injection**: Use Prisma parameterized queries
|
|
- **XSS**: Sanitize user inputs; use Content Security Policy
|
|
- **CSRF**: Use CSRF tokens for state-changing operations
|
|
- **Rate Limiting**: Limit API requests per user/IP
|
|
- **Audit Logging**: Log all critical operations
|
|
|
|
---
|
|
|
|
## Deployment
|
|
|
|
### Docker
|
|
|
|
```dockerfile
|
|
FROM node:20-alpine
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm ci --only=production
|
|
COPY . .
|
|
RUN npx prisma generate
|
|
RUN npm run build
|
|
CMD ["npm", "start"]
|
|
```
|
|
|
|
### Docker Compose
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
services:
|
|
backend:
|
|
build: .
|
|
ports:
|
|
- "3000:3000"
|
|
environment:
|
|
DATABASE_URL: postgresql://user:password@db:5432/ca_grow_ops
|
|
depends_on:
|
|
- db
|
|
db:
|
|
image: postgres:15
|
|
environment:
|
|
POSTGRES_USER: user
|
|
POSTGRES_PASSWORD: password
|
|
POSTGRES_DB: ca_grow_ops
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
volumes:
|
|
postgres_data:
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
1. **Choose Framework**: Decide between Express and Fastify
|
|
2. **Set Up Project**: Initialize Node.js project with TypeScript
|
|
3. **Configure Prisma**: Set up Prisma schema and migrations
|
|
4. **Implement Auth**: Build authentication and RBAC
|
|
5. **Build Modules**: Implement feature modules per spec
|
|
6. **Write Tests**: Unit and integration tests for all modules
|
|
7. **Deploy**: Set up CI/CD and deploy to staging
|
|
|
|
---
|
|
|
|
## Resources
|
|
|
|
- [Prisma Documentation](https://www.prisma.io/docs)
|
|
- [Express Documentation](https://expressjs.com)
|
|
- [Fastify Documentation](https://www.fastify.io)
|
|
- [Jest Documentation](https://jestjs.io)
|
|
- [TypeScript Documentation](https://www.typescriptlang.org/docs)
|