diff --git a/assets/linkFortyLogo.png b/assets/linkFortyLogo.png new file mode 100644 index 0000000..bee8254 Binary files /dev/null and b/assets/linkFortyLogo.png differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..ad6ac92 Binary files /dev/null and b/assets/logo.png differ diff --git a/examples/Dockerfile b/examples/Dockerfile new file mode 100644 index 0000000..0c65710 --- /dev/null +++ b/examples/Dockerfile @@ -0,0 +1,45 @@ +FROM node:22-alpine + +WORKDIR /app + +# Install curl and dumb-init for health checks and signal handling +# Split to avoid busybox trigger issues in ARM64 QEMU builds +RUN apk add --no-cache --no-scripts curl dumb-init && \ + /bin/busybox --install -s || true + +# Create non-root user for security +RUN addgroup -g 1001 -S linkforty && \ + adduser -S linkforty -u 1001 -G linkforty + +# Copy package files +COPY package*.json ./ + +# Install dependencies (skip prepare script since we already built in CI) +RUN npm ci --only=production --ignore-scripts && \ + npm cache clean --force + +# Copy source files +COPY dist ./dist +COPY examples/basic-server.ts ./ + +# Install tsx for running TypeScript +RUN npm install -g tsx + +# Change ownership to non-root user +RUN chown -R linkforty:linkforty /app + +# Switch to non-root user +USER linkforty + +# Expose port +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:3000/api/sdk/v1/health || exit 1 + +# Use dumb-init for proper signal handling +ENTRYPOINT ["dumb-init", "--"] + +# Run migrations on startup and then start server +CMD ["sh", "-c", "tsx dist/scripts/migrate.js && tsx basic-server.ts"] diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..f673e5f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,329 @@ +
+ LinkForty Logo + + # @linkforty/core Examples + + This directory contains example implementations for deploying LinkForty Core. +
+ +## Quick Start with Docker Compose + +The easiest way to get started is using Docker Compose, which will set up PostgreSQL, Redis, and the LinkForty server. + +### 1. Start All Services + +```bash +cd examples +docker compose up -d +``` + +This will start: +- PostgreSQL database (port 5432) +- Redis cache (port 6379) +- LinkForty server (port 3000) + +### 2. Access the Server + +The API will be available at `http://localhost:3000` + +**Test it:** +```bash +# Health check +curl http://localhost:3000/health + +# Create a test link (you'll need a userId first) +curl -X POST http://localhost:3000/api/links \ + -H "Content-Type: application/json" \ + -d '{ + "userId": "test-user", + "originalUrl": "https://example.com", + "title": "My First Link" + }' +``` + +### 3. View Logs + +```bash +docker compose logs -f linkforty +``` + +### 4. Stop Services + +```bash +docker compose down +``` + +To remove volumes (data will be lost): +```bash +docker compose down -v +``` + +## Basic Server (Node.js) + +If you prefer to run the server directly with Node.js: + +### Prerequisites + +- Node.js 18+ +- PostgreSQL 14+ +- Redis 6+ + +### 1. Install Dependencies + +```bash +npm install @linkforty/core +``` + +### 2. Start PostgreSQL and Redis + +Using Docker: +```bash +docker run -d --name postgres -p 5432:5432 \ + -e POSTGRES_DB=linkforty \ + -e POSTGRES_USER=linkforty \ + -e POSTGRES_PASSWORD=changeme \ + postgres:15-alpine + +docker run -d --name redis -p 6379:6379 \ + redis:7-alpine +``` + +Or install locally using your package manager. + +### 3. Run the Example Server + +```bash +# Set environment variables +export DATABASE_URL=postgresql://linkforty:changeme@localhost:5432/linkforty +export REDIS_URL=redis://localhost:6379 +export PORT=3000 + +# Run the server +npx tsx examples/basic-server.ts +``` + +## Custom Implementation + +You can also create your own server implementation: + +### TypeScript Example + +```typescript +import { createServer } from '@linkforty/core'; + +async function start() { + const server = await createServer({ + database: { + url: process.env.DATABASE_URL, + pool: { + min: 2, + max: 10, + }, + }, + redis: { + url: process.env.REDIS_URL, + }, + cors: { + origin: ['https://yourdomain.com'], + }, + logger: true, + }); + + // Add custom routes + server.get('/custom', async (request, reply) => { + return { message: 'Custom endpoint' }; + }); + + await server.listen({ + port: 3000, + host: '0.0.0.0', + }); + + console.log('Server running!'); +} + +start(); +``` + +### JavaScript Example + +```javascript +const { createServer } = require('@linkforty/core'); + +async function start() { + const server = await createServer({ + database: { + url: 'postgresql://linkforty:changeme@localhost:5432/linkforty' + }, + redis: { + url: 'redis://localhost:6379' + } + }); + + await server.listen({ port: 3000, host: '0.0.0.0' }); + console.log('Server running on http://localhost:3000'); +} + +start().catch(console.error); +``` + +## Environment Variables + +Create a `.env` file: + +```bash +DATABASE_URL=postgresql://linkforty:changeme@localhost:5432/linkforty +REDIS_URL=redis://localhost:6379 +PORT=3000 +NODE_ENV=development +CORS_ORIGIN=* +``` + +## Database Migrations + +The database schema is automatically initialized on first startup. If you need to run migrations manually: + +```bash +npx tsx node_modules/@linkforty/core/dist/scripts/migrate.js +``` + +## Production Deployment + +For production deployments: + +1. **Use environment variables** for configuration +2. **Enable Redis** for caching +3. **Set up PostgreSQL replication** for high availability +4. **Use a process manager** (PM2, systemd) +5. **Set NODE_ENV=production** +6. **Configure CORS** to allow only your domains +7. **Use HTTPS** with a reverse proxy (nginx, Caddy) + +### Example with PM2 + +```bash +# Install PM2 +npm install -g pm2 + +# Start server +pm2 start examples/basic-server.ts --name linkforty + +# View logs +pm2 logs linkforty + +# Monitor +pm2 monit + +# Restart +pm2 restart linkforty + +# Set to start on boot +pm2 startup +pm2 save +``` + +### Example nginx config + +```nginx +server { + listen 80; + server_name links.yourdomain.com; + + location / { + proxy_pass http://localhost:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + 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; + proxy_cache_bypass $http_upgrade; + } +} +``` + +## API Usage Examples + +### Create a Link + +```bash +curl -X POST http://localhost:3000/api/links \ + -H "Content-Type: application/json" \ + -d '{ + "userId": "user-123", + "originalUrl": "https://example.com/product", + "title": "Product Page", + "iosUrl": "myapp://product/123", + "androidUrl": "myapp://product/123", + "utmParameters": { + "source": "twitter", + "medium": "social", + "campaign": "launch" + }, + "customCode": "product-launch" + }' +``` + +### Get All Links + +```bash +curl "http://localhost:3000/api/links?userId=user-123" +``` + +### Update a Link + +```bash +curl -X PUT "http://localhost:3000/api/links/link-id?userId=user-123" \ + -H "Content-Type: application/json" \ + -d '{ + "title": "Updated Title", + "isActive": false + }' +``` + +### Get Analytics + +```bash +# Overview +curl "http://localhost:3000/api/analytics/overview?userId=user-123&days=30" + +# Link-specific +curl "http://localhost:3000/api/analytics/links/link-id?userId=user-123&days=7" +``` + +### Test Redirect + +```bash +curl -I http://localhost:3000/product-launch +``` + +## Troubleshooting + +### Database Connection Failed + +- Verify PostgreSQL is running: `docker ps | grep postgres` +- Check connection string in DATABASE_URL +- Ensure database exists: `psql -U linkforty -d linkforty -c "SELECT 1;"` + +### Redis Connection Failed + +- Verify Redis is running: `docker ps | grep redis` +- Test connection: `redis-cli ping` +- Make sure REDIS_URL is correct + +### Port Already in Use + +- Check what's using the port: `lsof -i :3000` +- Change the PORT environment variable + +### Migrations Not Running + +- Run manually: `npx tsx node_modules/@linkforty/core/dist/scripts/migrate.js` +- Check database permissions + +## Support + +- Documentation: https://github.com/linkforty/core +- Issues: https://github.com/linkforty/core/issues +- Discussions: https://github.com/linkforty/core/discussions diff --git a/examples/basic-server.ts b/examples/basic-server.ts new file mode 100644 index 0000000..cb435c9 --- /dev/null +++ b/examples/basic-server.ts @@ -0,0 +1,46 @@ +import { createServer } from '@linkforty/core'; + +function getTrustProxy(): boolean | number | undefined { + const v = process.env.TRUST_PROXY; + if (v === undefined || v === '') return undefined; + if (v === '1' || v.toLowerCase() === 'true') return true; + const n = Number(v); + if (!Number.isNaN(n) && n >= 0) return n; + return undefined; +} + +async function start() { + const server = await createServer({ + database: { + url: process.env.DATABASE_URL || 'postgresql://postgres:password@localhost:5432/linkforty', + }, + redis: { + url: process.env.REDIS_URL || 'redis://localhost:6379', + }, + cors: { + origin: process.env.CORS_ORIGIN || '*', + }, + trustProxy: getTrustProxy(), + }); + + await server.listen({ + port: Number(process.env.PORT) || 3000, + host: '0.0.0.0', + }); + + console.log('LinkForty server running on http://localhost:3000'); + console.log(''); + console.log('API Endpoints:'); + console.log(' POST /api/links - Create a new link'); + console.log(' GET /api/links - List all links (requires ?userId=xxx)'); + console.log(' GET /api/links/:id - Get a specific link (requires ?userId=xxx)'); + console.log(' PUT /api/links/:id - Update a link (requires ?userId=xxx)'); + console.log(' DELETE /api/links/:id - Delete a link (requires ?userId=xxx)'); + console.log(' GET /api/analytics/overview - Get analytics overview (requires ?userId=xxx)'); + console.log(' GET /api/analytics/links/:linkId - Get link analytics (requires ?userId=xxx)'); + console.log(''); + console.log('Public Endpoint:'); + console.log(' GET /:shortCode - Redirect to target URL'); +} + +start().catch(console.error); diff --git a/examples/docker-compose.yml b/examples/docker-compose.yml new file mode 100644 index 0000000..23cfed7 --- /dev/null +++ b/examples/docker-compose.yml @@ -0,0 +1,56 @@ +services: + postgres: + image: postgres:15-alpine + environment: + POSTGRES_DB: linkforty + POSTGRES_USER: linkforty + POSTGRES_PASSWORD: changeme + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U linkforty"] + interval: 5s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + + linkforty: + build: + context: .. + dockerfile: examples/Dockerfile + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + environment: + DATABASE_URL: postgresql://linkforty:changeme@postgres:5432/linkforty?sslmode=disable + REDIS_URL: redis://redis:6379 + NODE_ENV: production + PORT: 3000 + ports: + - "3000:3000" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/sdk/v1/health"] + interval: 5s + timeout: 5s + retries: 10 + start_period: 10s + restart: unless-stopped + +volumes: + postgres_data: + redis_data: diff --git a/infra/CONTRIBUTING.md b/infra/CONTRIBUTING.md new file mode 100644 index 0000000..a097ee7 --- /dev/null +++ b/infra/CONTRIBUTING.md @@ -0,0 +1,425 @@ +
+ LinkForty Logo + + # Contributing Infrastructure Templates + + Thank you for your interest in contributing infrastructure templates to LinkForty! This guide will help you add support for new cloud platforms and deployment methods. +
+ +## Overview + +We welcome community contributions for additional infrastructure providers. This allows LinkForty users to deploy on their preferred platforms with minimal setup. + +## Official vs Community Support + +### Official Support (Maintained by Core Team) +- Docker & Docker Compose (`examples/`) +- Fly.io (`infra/fly.io/`) + +### Community Support (Maintained by Contributors) +- All other platforms +- We validate they work but may not provide detailed troubleshooting + +## Contribution Guidelines + +### Before You Start + +1. **Check existing issues/PRs** to avoid duplicate work +2. **Open a discussion** on GitHub to gauge interest +3. **Choose a popular platform** that benefits many users +4. **Test thoroughly** before submitting + +### Requested Providers + +We're especially interested in templates for: +- **AWS** (ECS/Fargate, Elastic Beanstalk) +- **Google Cloud** (Cloud Run, GKE) +- **Azure** (Container Instances, App Service) +- **Railway** +- **Render** +- **DigitalOcean App Platform** +- **Kubernetes** (generic manifests) +- **Terraform** (multi-cloud IaC) + +## Template Requirements + +### Must-Have Components + +Your infrastructure template **must** include: + +1. **Configuration Files** + - Platform-specific deployment config (e.g., `app.yaml`, `terraform.tf`) + - Environment variable template + - Build/deployment instructions + +2. **Documentation** + - `DEPLOYMENT.md` - Step-by-step deployment guide + - Database setup instructions + - Redis setup instructions (optional but recommended) + - Cost estimates for different tiers + +3. **Security Guidelines** + - SSL/TLS configuration + - Secret management best practices + - Network security recommendations + - Backup strategy + +4. **Testing** + - Proof of successful deployment + - Screenshots or deployment logs + - Performance benchmarks (optional) + +### Directory Structure + +Add your template under `infra/[provider]/`: + +``` +infra/ +├── your-provider/ +│ ├── README.md # Quick overview +│ ├── DEPLOYMENT.md # Detailed deployment guide +│ ├── config.[ext] # Platform-specific config +│ ├── .env.production.example # Environment variables +│ └── [additional files] # Provider-specific resources +``` + +### Example Structure: AWS ECS + +``` +infra/ +├── aws-ecs/ +│ ├── README.md # Overview of AWS ECS deployment +│ ├── DEPLOYMENT.md # Step-by-step guide +│ ├── task-definition.json # ECS task definition +│ ├── service.json # ECS service config +│ ├── cloudformation.yaml # Infrastructure template (optional) +│ ├── .env.production.example # Environment variables +│ └── scripts/ +│ ├── setup.sh # Automated setup script (optional) +│ └── deploy.sh # Deployment script (optional) +``` + +## Documentation Standards + +### DEPLOYMENT.md Template + +Your `DEPLOYMENT.md` should include: + +```markdown +# Deploying LinkForty to [Provider Name] + +## Prerequisites +- Account setup +- CLI tools needed +- Required permissions + +## Step 1: Database Setup +- PostgreSQL 13+ configuration +- Connection string format +- Security settings + +## Step 2: Redis Setup (Optional) +- Redis configuration +- Connection string format + +## Step 3: Application Deployment +- Configuration steps +- Secret management +- Build and deploy process + +## Step 4: Verification +- Health check verification +- Test redirect creation +- Troubleshooting common issues + +## Scaling +- Vertical scaling +- Horizontal scaling +- Multi-region deployment + +## Monitoring +- Logs access +- Metrics dashboard +- Alerts setup + +## Cost Estimation +- Free tier (if available) +- Small production setup +- Large production setup + +## Troubleshooting +- Common issues and solutions + +## Resources +- Platform documentation links +``` + +### README.md Template + +```markdown +# LinkForty on [Provider Name] + +Quick overview of deploying LinkForty to [Provider]. + +## Quick Start + +[One-click deploy button if available] + +## Features +- What makes this platform good for LinkForty +- Key benefits + +## Cost Estimate +- Starting at $X/month + +## Documentation +- [Full deployment guide](./DEPLOYMENT.md) + +## Support +- Community-maintained +- Link to platform's support +``` + +## Code Quality Standards + +### Configuration Files + +- **Use comments** to explain non-obvious settings +- **Include defaults** that work for most users +- **Parameterize** where possible (don't hardcode values) +- **Follow platform conventions** (naming, structure) + +### Environment Variables + +- **Match LinkForty's standards** (see `examples/.env.example`) +- **Include all required variables** +- **Document optional variables** +- **Show example values** (with placeholders for secrets) + +### Security + +- **Never include actual secrets** in example files +- **Use secure defaults** (SSL/TLS, authentication) +- **Document security best practices** +- **Include secret rotation instructions** + +## Testing Your Template + +Before submitting, test your template: + +### 1. Fresh Deployment Test + +- [ ] Start from scratch (new account or clean environment) +- [ ] Follow your DEPLOYMENT.md step-by-step +- [ ] Verify all commands work as documented +- [ ] Note any issues or unclear steps + +### 2. Functional Test + +- [ ] Application starts successfully +- [ ] Health check endpoint responds +- [ ] Create a test link via API +- [ ] Verify redirect works +- [ ] Check logs are accessible +- [ ] Test database connection +- [ ] Test Redis connection (if used) + +### 3. Security Test + +- [ ] HTTPS is enforced +- [ ] Secrets are not exposed in logs +- [ ] Database uses SSL +- [ ] CORS is configurable +- [ ] No public exposure of internal services + +### 4. Documentation Test + +- [ ] Another person can follow your guide successfully +- [ ] All prerequisites are listed +- [ ] Cost estimates are accurate +- [ ] Troubleshooting section is helpful + +## Pull Request Process + +### 1. Prepare Your PR + +Create a branch: +```bash +git checkout -b infra/add-[provider-name] +``` + +Add your files: +```bash +git add infra/[provider-name]/ +``` + +Commit with clear message: +```bash +git commit -m "Add infrastructure template for [Provider Name]" +``` + +### 2. Update Main README + +Add your provider to `infra/README.md`: + +```markdown +### [Provider Name] (Community) +**Location:** [`infra/[provider-name]/`](./[provider-name]/) + +**Best for:** +- [Use case 1] +- [Use case 2] + +[View deployment guide →](./[provider-name]/DEPLOYMENT.md) +``` + +### 3. PR Description Template + +```markdown +## Summary +Adds infrastructure template for deploying LinkForty to [Provider Name]. + +## What's Included +- [ ] Configuration files +- [ ] Deployment guide +- [ ] Environment variable template +- [ ] Security documentation +- [ ] Cost estimates + +## Testing +- [ ] Successfully deployed to [Provider] +- [ ] Verified all features work +- [ ] Tested by at least one other person + +## Screenshots/Logs +[Include proof of successful deployment] + +## Cost Estimate +- Free tier: [Yes/No] +- Small production: ~$X/month +- Medium production: ~$Y/month + +## Additional Notes +[Any platform-specific quirks or considerations] +``` + +### 4. Review Process + +Your PR will be reviewed for: +- **Completeness** - All required components present +- **Accuracy** - Instructions work as documented +- **Security** - Follows best practices +- **Quality** - Clear, well-documented +- **Maintenance** - Reasonable to maintain + +We may request changes or improvements. + +### 5. After Merge + +- Your template will be listed in the main README +- You'll be credited as the maintainer +- Users may open issues specific to your platform +- You'll be tagged for platform-specific questions + +## Maintenance Expectations + +### As a Contributor + +You're expected to: +- **Respond to issues** related to your platform (within reason) +- **Update templates** when platform changes significantly +- **Test updates** before merging changes +- **Notify maintainers** if you can no longer maintain + +### If You Can't Maintain + +If you can't continue maintaining: +1. Open an issue titled "Seeking maintainer for [Provider]" +2. Tag current maintainers +3. We'll mark it as "community-seeking-maintainer" + +### What Core Maintainers Will Do + +- Review and merge PRs for your platform +- Help with general LinkForty questions +- Validate deployments still work (periodically) +- Archive unmaintained platforms (if necessary) + +### What Core Maintainers Won't Do + +- Debug provider-specific issues (beyond basic validation) +- Maintain deep expertise in all platforms +- Provide 24/7 support for community platforms + +## Support Boundaries + +### In-Scope Support (You Provide) + +- Platform-specific deployment questions +- Configuration issues unique to the platform +- Cost optimization for that platform +- Platform CLI/API usage + +### Out-of-Scope Support (User's Responsibility) + +- Cloud account setup and billing +- Platform account permissions/access issues +- General cloud computing questions +- Non-Link-Forty application issues + +### Core LinkForty Support (Maintainers Provide) + +- Application bugs +- Database schema issues +- API behavior questions +- General architecture questions + +## Examples to Learn From + +### Good Example: Fly.io Template + +Located at `infra/fly.io/`, this template demonstrates: +- ✅ Clear, step-by-step instructions +- ✅ Complete configuration files with comments +- ✅ Security checklist +- ✅ Cost estimates at multiple tiers +- ✅ Troubleshooting section +- ✅ Links to platform documentation + +### What Not to Do + +- ❌ One-liner deployment without explanation +- ❌ Missing environment variables +- ❌ No security guidance +- ❌ Untested instructions +- ❌ Hardcoded values (like app names) +- ❌ No cost information + +## Getting Help + +### Questions About Contributing + +- Open a discussion on GitHub +- Tag maintainers: @[maintainer-username] +- Join our community chat (if available) + +### Technical Questions + +- Review existing templates (especially `infra/fly.io/`) +- Check LinkForty's main documentation +- Ask in GitHub Discussions + +## Recognition + +Contributors will be: +- Listed in the template's README as maintainer +- Mentioned in release notes +- Credited in the project's contributors list + +Thank you for helping make LinkForty accessible on more platforms! + +## License + +All infrastructure templates are licensed under the same MIT license as LinkForty Core. + +By contributing, you agree to license your contribution under this license. diff --git a/infra/README.md b/infra/README.md new file mode 100644 index 0000000..ff98cf7 --- /dev/null +++ b/infra/README.md @@ -0,0 +1,121 @@ +
+ LinkForty Logo + + # LinkForty Infrastructure + + This directory contains infrastructure-as-code templates and deployment guides for running LinkForty in production environments. +
+ +## Available Deployment Options + +### Docker & Docker Compose (Self-Hosted) +For local development or self-managed infrastructure. + +**Location:** [`examples/`](../examples/) + +**Best for:** +- Local development +- Self-managed VPS/bare metal servers +- Custom infrastructure requirements +- Full control over all components + +**What's included:** +- Multi-stage Dockerfile for production builds +- Docker Compose orchestration with PostgreSQL and Redis +- Environment configuration examples + +[View Docker deployment guide →](../examples/README.md) + +--- + +### Fly.io (Managed Platform) +Recommended for production deployments with minimal DevOps overhead. + +**Location:** [`infra/fly.io/`](./fly.io/) + +**Best for:** +- Production deployments +- Global edge distribution +- Auto-scaling applications +- Managed PostgreSQL and Redis +- Teams without dedicated DevOps + +**What's included:** +- `fly.toml` - Application configuration +- Deployment guide with step-by-step instructions +- PostgreSQL and Redis setup guides +- Production security checklist +- Environment configuration templates + +[View Fly.io deployment guide →](./fly.io/DEPLOYMENT.md) + +**Estimated costs:** Starting at ~$10-15/month for small production deployments + +--- + +## Choosing a Deployment Option + +| Feature | Docker Compose | Fly.io | +|-------------------------------|----------------------------|-----------------------------| +| **Setup complexity** | Medium | Low | +| **Infrastructure management** | You manage | Fully managed | +| **Scaling** | Manual | Automatic | +| **Global distribution** | Manual setup | Built-in edge regions | +| **Database backups** | You configure | Automated | +| **SSL/TLS certificates** | You configure | Automatic | +| **Cost** | Infrastructure only | ~$10-15+/month | +| **Best for** | Custom needs, self-hosting | Production, fast deployment | + +## Community-Contributed Providers + +We welcome community contributions for additional infrastructure providers! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines. + +**Requested providers:** +- AWS (ECS/Fargate) +- Google Cloud Run +- Azure Container Instances +- Railway +- Render +- DigitalOcean App Platform + +## Support + +### Official Support +We provide official support and maintenance for: +- Docker & Docker Compose templates (in `examples/`) +- Fly.io templates (in `infra/fly.io/`) + +### Community Support +Additional provider templates are community-maintained. We validate they work but may not provide detailed troubleshooting for provider-specific issues. + +### Getting Help + +- **Application issues:** [GitHub Issues](https://github.com/yourusername/linkforty-core/issues) +- **Infrastructure questions:** Check provider-specific documentation first +- **Security concerns:** See [SECURITY.md](../SECURITY.md) + +## Security Notice + +**Production Deployment Responsibility** + +While we provide infrastructure templates and security checklists, **you are responsible for**: +- Securing your cloud provider accounts +- Managing secrets and API keys +- Configuring firewalls and network policies +- Compliance with relevant regulations (GDPR, HIPAA, etc.) +- Monitoring and incident response +- Cost management and billing + +Always review the security checklist for your chosen platform before deploying to production. + +## Quick Start + +1. Choose your deployment platform +2. Follow the deployment guide in the respective directory +3. Review the security checklist +4. Deploy and test +5. Set up monitoring and backups + +## License + +All infrastructure templates are provided under the same MIT license as LinkForty Core. diff --git a/infra/fly.io/.env.production.example b/infra/fly.io/.env.production.example new file mode 100644 index 0000000..6fc737b --- /dev/null +++ b/infra/fly.io/.env.production.example @@ -0,0 +1,143 @@ +# LinkForty Production Environment Configuration +# +# This file shows all environment variables needed for production deployment on Fly.io +# +# SECURITY WARNING: Never commit actual secrets to version control! +# Use `fly secrets set` to configure these values securely. +# +# Example: +# fly secrets set DATABASE_URL="postgresql://..." +# fly secrets set REDIS_URL="rediss://..." + +# ============================================================================ +# REQUIRED: Database Configuration +# ============================================================================ + +# PostgreSQL connection string +# Format: postgresql://username:password@host:port/database?sslmode=require +# +# For Fly Postgres (automatically set by `fly postgres attach`): +# postgresql://user:pass@appname.internal:5432/dbname?sslmode=require +# +# For external providers (Supabase, Neon, AWS RDS): +# Use connection string from provider dashboard +# +# IMPORTANT: Always include ?sslmode=require for production +DATABASE_URL="postgresql://username:password@host:5432/linkforty?sslmode=require" + +# ============================================================================ +# OPTIONAL BUT RECOMMENDED: Redis Cache +# ============================================================================ + +# Redis connection string (TLS encrypted) +# Format: rediss://default:password@host:6379 +# +# For Upstash Redis (via Fly.io): +# Run: fly redis create +# Use the REDIS_URL provided +# +# IMPORTANT: Use rediss:// (double 's') for TLS encryption in production +# +# If not set, LinkForty will work but without caching (slower, higher DB load) +REDIS_URL="rediss://default:password@host:6379" + +# ============================================================================ +# APPLICATION CONFIGURATION +# ============================================================================ + +# Environment mode (should always be "production" for deployed apps) +NODE_ENV="production" + +# Server port (default: 8080, must match fly.toml internal_port) +PORT="8080" + +# ============================================================================ +# CORS CONFIGURATION +# ============================================================================ + +# Allowed origins for CORS (comma-separated for multiple origins) +# +# Single origin: +# CORS_ORIGIN="https://yourdomain.com" +# +# Multiple origins: +# CORS_ORIGIN="https://yourdomain.com,https://app.yourdomain.com,https://www.yourdomain.com" +# +# SECURITY WARNING: Never use "*" in production - only specific domains! +CORS_ORIGIN="https://yourdomain.com" + +# ============================================================================ +# OPTIONAL: Advanced Configuration +# ============================================================================ + +# Log level (error, warn, info, debug) +# Default: "info" +# Use "error" or "warn" in production to reduce log volume +LOG_LEVEL="info" + +# Request timeout in milliseconds +# Default: 30000 (30 seconds) +# REQUEST_TIMEOUT="30000" + +# Database connection pool settings +# Default min: 2, max: 10 +# Adjust based on your database plan and expected load +# DB_POOL_MIN="2" +# DB_POOL_MAX="10" + +# Redis cache TTL (time-to-live) in seconds +# Default: 3600 (1 hour) +# Higher values = less DB load, potentially stale data +# Lower values = more accurate, higher DB load +# REDIS_TTL="3600" + +# ============================================================================ +# CUSTOM DOMAIN (Optional) +# ============================================================================ + +# If using a custom domain for short links +# Example: If your short links should be https://go.yourdomain.com/abc123 +# Set this to match your domain configuration +# +# See: https://fly.io/docs/app-guides/custom-domains-with-fly/ +# CUSTOM_DOMAIN="go.yourdomain.com" + +# ============================================================================ +# MONITORING & OBSERVABILITY (Optional) +# ============================================================================ + +# Sentry DSN for error tracking +# Sign up at https://sentry.io and get your DSN +# SENTRY_DSN="https://...@sentry.io/..." + +# New Relic license key +# NEW_RELIC_LICENSE_KEY="..." + +# Datadog API key +# DATADOG_API_KEY="..." + +# ============================================================================ +# DEPLOYMENT CHECKLIST +# ============================================================================ +# +# Before deploying to production, ensure: +# +# [ ] DATABASE_URL is set with sslmode=require +# [ ] REDIS_URL is set (or intentionally omitted) +# [ ] CORS_ORIGIN is set to your actual domain(s), NOT "*" +# [ ] NODE_ENV is set to "production" +# [ ] All secrets are set via `fly secrets set`, not in fly.toml +# [ ] You've reviewed SECURITY.md checklist +# [ ] Database migrations have been tested +# [ ] Health check endpoint (/health) works +# [ ] You have database backups configured +# +# Set secrets with: +# fly secrets set DATABASE_URL="..." --app your-app +# fly secrets set REDIS_URL="..." --app your-app +# fly secrets set CORS_ORIGIN="..." --app your-app +# +# Verify secrets (values are hidden): +# fly secrets list --app your-app +# +# ============================================================================ diff --git a/infra/fly.io/DEPLOYMENT.md b/infra/fly.io/DEPLOYMENT.md new file mode 100644 index 0000000..f423e21 --- /dev/null +++ b/infra/fly.io/DEPLOYMENT.md @@ -0,0 +1,372 @@ +# Deploying LinkForty to Fly.io + +This guide walks you through deploying LinkForty to [Fly.io](https://fly.io), a global application platform that makes deployment simple. + +## Prerequisites + +1. **Fly.io Account** + - Sign up at https://fly.io/app/sign-up + - Credit card required (but has generous free tier) + +2. **Fly CLI Installed** + ```bash + # macOS/Linux + curl -L https://fly.io/install.sh | sh + + # Windows + iwr https://fly.io/install.ps1 -useb | iex + ``` + +3. **Authenticate** + ```bash + fly auth login + ``` + +4. **LinkForty Built Locally** + ```bash + npm install + npm run build + ``` + +## Step 1: Customize Configuration + +1. Edit `infra/fly.io/fly.toml`: + ```toml + app = "your-unique-app-name" # Must be globally unique + primary_region = "iad" # Choose your region + ``` + +2. Available regions (run `fly platform regions` for full list): + - `iad` - Washington DC (US East) + - `lax` - Los Angeles (US West) + - `lhr` - London (Europe) + - `fra` - Frankfurt (Europe) + - `syd` - Sydney (Asia-Pacific) + - `nrt` - Tokyo (Asia-Pacific) + +## Step 2: Create PostgreSQL Database + +LinkForty requires PostgreSQL 13+. + +```bash +# Create a Postgres cluster +fly postgres create --name linkforty-db --region iad + +# Choose configuration when prompted: +# - Development: 1GB RAM, 10GB storage (free tier eligible) +# - Production: 2GB+ RAM, 20GB+ storage + +# Attach database to your app +fly postgres attach linkforty-db --app your-unique-app-name +``` + +This automatically sets the `DATABASE_URL` secret in your app. + +**Alternative:** Use [Supabase](https://supabase.com) or another managed PostgreSQL provider and set `DATABASE_URL` manually (see Step 5). + +📖 Detailed PostgreSQL setup: [fly.postgres.md](./fly.postgres.md) + +## Step 3: Create Redis Cache (Optional but Recommended) + +Redis improves performance by ~90% for repeated link lookups. + +```bash +# Create Upstash Redis (Fly's Redis partner) +fly redis create --name linkforty-redis --region iad + +# Choose plan when prompted: +# - Development: Free tier (256MB) +# - Production: Eviction-$10 or Eviction-$40 + +# Note the REDIS_URL provided - you'll set it in Step 5 +``` + +📖 Detailed Redis setup: [fly.redis.md](./fly.redis.md) + +## Step 4: Create Your Fly.io App + +From the root of your LinkForty project: + +```bash +# Copy fly.toml to root directory +cp infra/fly.io/fly.toml fly.toml + +# Create the app (don't deploy yet) +fly apps create your-unique-app-name --org personal +``` + +## Step 5: Set Secrets (Environment Variables) + +```bash +# DATABASE_URL (automatically set if you used fly postgres attach) +# If using external PostgreSQL: +fly secrets set DATABASE_URL="postgresql://user:password@host:5432/dbname?sslmode=require" + +# REDIS_URL (from Step 3 output) +fly secrets set REDIS_URL="redis://default:password@host:6379" + +# CORS Origin (your frontend domain) +fly secrets set CORS_ORIGIN="https://yourdomain.com" + +# Optional: Custom port (defaults to 8080) +fly secrets set PORT="8080" + +# View configured secrets (values are hidden) +fly secrets list +``` + +⚠️ **Security Note:** Never commit secrets to git. Use `fly secrets set` only. + +## Step 6: Initial Deployment + +```bash +# Deploy from the root directory (where fly.toml is located) +fly deploy + +# This will: +# 1. Build your Docker image +# 2. Push to Fly.io registry +# 3. Run migrations (via release_command in fly.toml) +# 4. Deploy to your chosen region(s) +# 5. Start health checks +``` + +**First deployment takes 3-5 minutes.** Subsequent deployments are faster. + +## Step 7: Verify Deployment + +```bash +# Check app status +fly status + +# View recent logs +fly logs + +# Open your app in browser +fly open + +# Test the health endpoint +curl https://your-app.fly.dev/health +``` + +## Step 8: Create Your First Link + +```bash +# Using curl +curl -X POST https://your-app.fly.dev/api/links \ + -H "Content-Type: application/json" \ + -d '{ + "userId": "user-123", + "iosUrl": "myapp://product/123", + "androidUrl": "myapp://product/123", + "webUrl": "https://mysite.com/product/123" + }' + +# Response includes your short code +# {"code":"abc123","shortUrl":"https://your-app.fly.dev/abc123"} +``` + +Test the redirect: +```bash +curl -L https://your-app.fly.dev/abc123 +``` + +## Scaling Your Deployment + +### Vertical Scaling (More Resources Per Machine) + +Edit `fly.toml`: +```toml +[vm] + cpu_kind = "shared" # or "performance" for dedicated CPUs + cpus = 2 + memory_mb = 512 # or 1024, 2048, etc. +``` + +Then deploy: +```bash +fly deploy +``` + +### Horizontal Scaling (More Machines) + +```bash +# Scale to 3 machines in primary region +fly scale count 3 + +# Or use auto-scaling (edit fly.toml first) +# Uncomment the [[services.autoscaling]] section +fly deploy +``` + +### Multi-Region Deployment + +Edit `fly.toml` to uncomment regions: +```toml +[[regions]] +name = "iad" # US East +[[regions]] +name = "lhr" # Europe +[[regions]] +name = "nrt" # Asia +``` + +Deploy: +```bash +fly deploy +``` + +Fly.io automatically routes users to the nearest region. + +## Database Migrations + +Migrations run automatically on deploy (via `release_command` in fly.toml). + +To run manually: +```bash +# SSH into a machine +fly ssh console + +# Run migrations +npm run migrate + +# Exit +exit +``` + +## Monitoring and Logs + +```bash +# Real-time logs +fly logs + +# Filter by app instance +fly logs -i instance-id + +# View metrics dashboard +fly dashboard + +# Check machine status +fly status + +# View recent deployments +fly releases +``` + +## Troubleshooting + +### Deployment Fails + +```bash +# Check build logs +fly logs --app your-app + +# Verify secrets are set +fly secrets list + +# Check app configuration +fly config show +``` + +### Health Checks Failing + +Ensure your app exposes `/health` endpoint: + +```typescript +// In your Fastify setup +fastify.get('/health', async (request, reply) => { + return { status: 'ok' }; +}); +``` + +Check `fly.toml` health check path matches. + +### Database Connection Issues + +```bash +# Verify DATABASE_URL is set +fly secrets list + +# Check database status +fly postgres status linkforty-db + +# View database connection info +fly postgres db list linkforty-db +``` + +### Out of Memory Errors + +Increase memory allocation in `fly.toml`: +```toml +[vm] + memory_mb = 512 # Increase from 256 +``` + +### High Latency + +Consider: +- Enabling Redis cache (see Step 3) +- Multi-region deployment +- Increasing machine resources +- Database connection pooling (already configured) + +## Updating Your Deployment + +```bash +# After making code changes: +npm run build +fly deploy + +# To rollback to previous version: +fly releases +fly releases rollback +``` + +## Cost Optimization + +**Free Tier Eligible Setup:** +- 1x shared-cpu-1x machine (256MB) = Free +- Fly Postgres (1GB, development tier) = Free +- Upstash Redis (256MB) = Free +- **Total: $0/month** for low-traffic apps + +**Small Production Setup (~$10-15/month):** +- 1x shared-cpu-1x (512MB) = ~$3.50/month +- Fly Postgres (2GB) = ~$7/month +- Upstash Redis (Eviction-$10) = ~$10/month +- **Total: ~$20/month** + +**View your costs:** +```bash +fly billing show +``` + +## Security Checklist + +Before going to production, review [SECURITY.md](./SECURITY.md) for: +- Environment variable security +- Database SSL configuration +- CORS settings +- Secret rotation +- Backup strategy + +## Next Steps + +- Set up custom domain: https://fly.io/docs/app-guides/custom-domains-with-fly/ +- Configure TLS certificates: Automatic with Fly.io +- Set up monitoring alerts: https://fly.io/docs/reference/metrics/ +- Enable database backups: See [fly.postgres.md](./fly.postgres.md) + +## Getting Help + +- **Fly.io Docs:** https://fly.io/docs/ +- **Fly.io Community:** https://community.fly.io/ +- **LinkForty Issues:** https://github.com/yourusername/linkforty-core/issues +- **Security Issues:** See [SECURITY.md](../../SECURITY.md) + +## Resources + +- [Fly.io Pricing](https://fly.io/docs/about/pricing/) +- [Fly.io Regions](https://fly.io/docs/reference/regions/) +- [Fly.io Node.js Guide](https://fly.io/docs/languages-and-frameworks/node/) +- [Fly.io PostgreSQL](https://fly.io/docs/postgres/) diff --git a/infra/fly.io/SECURITY.md b/infra/fly.io/SECURITY.md new file mode 100644 index 0000000..151bb57 --- /dev/null +++ b/infra/fly.io/SECURITY.md @@ -0,0 +1,391 @@ +# Security Checklist for Production Deployment + +This checklist helps ensure your LinkForty deployment on Fly.io follows security best practices. + +## ✅ Pre-Deployment Security Checklist + +### Environment Variables & Secrets + +- [ ] All sensitive values are set using `fly secrets set` (never in `fly.toml`) +- [ ] `DATABASE_URL` includes `?sslmode=require` for PostgreSQL +- [ ] `REDIS_URL` includes authentication credentials +- [ ] `CORS_ORIGIN` is set to your actual frontend domain(s), not `*` +- [ ] `NODE_ENV` is set to `"production"` +- [ ] No secrets are committed to version control +- [ ] `.env` files are in `.gitignore` + +### Database Security + +- [ ] PostgreSQL uses SSL/TLS connections (`sslmode=require`) +- [ ] Database password is strong (20+ characters, random) +- [ ] Database is not publicly accessible (Fly Postgres is private by default) +- [ ] Connection pooling is configured (default: 2-10 connections) +- [ ] Database backups are enabled (see [fly.postgres.md](./fly.postgres.md)) +- [ ] Backup retention policy is configured + +### Redis Security + +- [ ] Redis requires authentication (Upstash Redis includes this by default) +- [ ] Redis connection uses TLS (`rediss://` protocol) +- [ ] Redis is not publicly accessible +- [ ] Connection timeout is configured + +### Application Security + +- [ ] CORS is properly configured (not set to `*` in production) +- [ ] Rate limiting is enabled for link creation endpoints +- [ ] Input validation is active (Zod schemas) +- [ ] SQL injection protection via parameterized queries (pg library handles this) +- [ ] Health check endpoint (`/health`) exposes no sensitive data +- [ ] Error messages don't leak sensitive information +- [ ] Logging doesn't include secrets or PII + +### Network Security + +- [ ] HTTPS is enforced (`force_https = true` in fly.toml) +- [ ] HTTP is redirected to HTTPS +- [ ] Health checks use HTTPS +- [ ] No sensitive services are exposed publicly + +### Access Control + +- [ ] Fly.io account uses strong password + 2FA +- [ ] Fly.io organization access is limited to required team members +- [ ] Deploy tokens (if used in CI/CD) have minimum required permissions +- [ ] Database credentials are rotated regularly (quarterly minimum) + +--- + +## 🔐 Security Configuration Details + +### 1. Database URL Security + +Your `DATABASE_URL` should look like: +``` +postgresql://user:password@host:5432/dbname?sslmode=require +``` + +Key requirements: +- `sslmode=require` - Forces SSL/TLS encryption +- Strong password (20+ characters) +- Host should be internal Fly network (`.internal` domain) if using Fly Postgres + +Set it securely: +```bash +fly secrets set DATABASE_URL="postgresql://user:password@host:5432/dbname?sslmode=require" +``` + +### 2. Redis URL Security + +Your `REDIS_URL` should look like: +``` +rediss://default:password@host:6379 +``` + +Key requirements: +- `rediss://` protocol (TLS encrypted) +- Authentication password included +- Upstash Redis (recommended) includes TLS by default + +Set it securely: +```bash +fly secrets set REDIS_URL="rediss://default:password@host:6379" +``` + +### 3. CORS Configuration + +For production, set specific origins: + +```bash +# Single origin +fly secrets set CORS_ORIGIN="https://yourdomain.com" + +# Multiple origins (comma-separated) +fly secrets set CORS_ORIGIN="https://yourdomain.com,https://app.yourdomain.com" +``` + +Never use `*` in production - this allows any website to make requests to your API. + +### 4. Rate Limiting + +LinkForty includes built-in rate limiting. Verify it's enabled in your deployment: + +- Link creation: Limited by IP address +- Analytics queries: Limited by userId +- Redirect endpoints: Unlimited (by design for fast redirects) + +Monitor rate limit metrics: +```bash +fly logs | grep "rate limit" +``` + +### 5. Input Validation + +LinkForty uses Zod for input validation. Ensure validation errors are logged: + +```bash +fly logs | grep "validation" +``` + +Common validation issues to monitor: +- Invalid URLs +- Malformed userId +- Invalid UTM parameters +- Expired links + +--- + +## 🔄 Secret Rotation Policy + +Rotate secrets regularly to minimize compromise risk. + +### Quarterly Rotation (Every 3 months) + +1. **Database Password** + ```bash + # On Fly Postgres + fly postgres connect -a linkforty-db + ALTER USER your_user WITH PASSWORD 'new-strong-password'; + \q + + # Update secret + fly secrets set DATABASE_URL="postgresql://user:new-password@host:5432/dbname?sslmode=require" + ``` + +2. **Redis Password** + ```bash + # Generate new Upstash Redis credentials + # In Upstash dashboard: Reset password + + # Update secret + fly secrets set REDIS_URL="rediss://default:new-password@host:6379" + ``` + +3. **API Keys** (if you add API authentication) + ```bash + fly secrets set API_KEY="new-random-key" + ``` + +### After Security Incident + +Rotate ALL secrets immediately: +- Database credentials +- Redis credentials +- Any API keys +- Fly.io deploy tokens (if compromised) + +--- + +## 🔍 Security Monitoring + +### Log Monitoring + +Monitor these security-relevant events: + +```bash +# Failed database connections +fly logs | grep "connection refused" + +# Rate limit hits +fly logs | grep "rate limit" + +# Validation errors +fly logs | grep "validation error" + +# Suspicious activity patterns +fly logs | grep "error" +``` + +### Metrics to Watch + +- **Unusual traffic spikes** - Potential DDoS or abuse +- **High error rates** - Potential attack or misconfiguration +- **Database connection errors** - Credential issues or network problems +- **Redis connection failures** - Service degradation + +Access metrics: +```bash +fly dashboard # Web UI with graphs +fly status # Current health +``` + +### Automated Alerts (Recommended) + +Set up alerts for: +- High error rates (>5% of requests) +- Service downtime +- Database connection failures +- Unusual traffic patterns + +Fly.io integrates with: +- Sentry (error tracking) +- Datadog (monitoring) +- Prometheus (metrics) + +--- + +## 🛡️ Incident Response Plan + +### If Secrets Are Compromised + +1. **Immediately rotate all secrets** + ```bash + # See "Secret Rotation Policy" section above + ``` + +2. **Review access logs** + ```bash + fly logs --all + ``` + +3. **Check for unauthorized database changes** + ```bash + fly postgres connect -a linkforty-db + SELECT * FROM users ORDER BY created_at DESC LIMIT 100; + SELECT * FROM links ORDER BY created_at DESC LIMIT 100; + ``` + +4. **Review Fly.io access logs** + - Check Fly.io dashboard for recent deploys + - Review team member access + - Check for unknown IP addresses + +5. **Deploy with new secrets** + ```bash + fly deploy + ``` + +6. **Document the incident** + - What was compromised? + - How was it discovered? + - What actions were taken? + - How to prevent in the future? + +### If Database Is Compromised + +1. **Restore from backup** + ```bash + # See fly.postgres.md for backup/restore instructions + ``` + +2. **Analyze what data was accessed/modified** + +3. **Notify affected users** (if PII was compromised) + +4. **Review and strengthen security measures** + +--- + +## 🔒 Data Protection + +### Personal Identifiable Information (PII) + +LinkForty collects: +- IP addresses (for geolocation) +- User agent strings (for device detection) +- Referrer URLs (for analytics) +- User IDs (provided by you) + +**Your responsibilities:** +- Comply with GDPR, CCPA, or relevant privacy laws +- Implement data retention policies +- Provide user data deletion endpoints +- Maintain privacy policy +- Obtain user consent where required + +### Data Retention + +Consider implementing: +- Automatic deletion of click events older than X days +- User data export functionality +- Data anonymization for old analytics + +Example cleanup query: +```sql +DELETE FROM click_events WHERE created_at < NOW() - INTERVAL '90 days'; +``` + +Schedule via cron or Fly.io scheduled tasks. + +### Encryption + +- **In transit:** HTTPS/TLS for all connections (enforced by Fly.io) +- **At rest:** Fly Postgres encrypts data at rest automatically +- **Application level:** Consider encrypting sensitive user data in JSONB fields + +--- + +## 🚨 Security Vulnerabilities + +### Reporting Security Issues + +**DO NOT** open public GitHub issues for security vulnerabilities. + +Instead: +- Email security@yourdomain.com (set up a security contact) +- Use GitHub Security Advisories (private disclosure) +- Allow 90 days for responsible disclosure + +### Keeping Dependencies Updated + +```bash +# Check for vulnerabilities +npm audit + +# Fix automatically where possible +npm audit fix + +# Review and update dependencies quarterly +npm outdated +npm update +``` + +Subscribe to security advisories: +- Node.js security releases +- Fastify security updates +- PostgreSQL security announcements + +--- + +## ✅ Security Checklist Summary + +Print and complete this checklist before every production deployment: + +**Pre-Deploy:** +- [ ] Secrets set via `fly secrets set` (not in code) +- [ ] Database uses SSL (`sslmode=require`) +- [ ] CORS configured for specific domains +- [ ] HTTPS enforced in `fly.toml` +- [ ] No secrets in version control + +**Post-Deploy:** +- [ ] Health check passes +- [ ] HTTPS redirect works +- [ ] CORS allows only intended domains +- [ ] Database connection successful +- [ ] Redis connection successful +- [ ] Rate limiting active +- [ ] Error logging working + +**Ongoing:** +- [ ] Secrets rotated quarterly +- [ ] Dependencies updated monthly +- [ ] Logs monitored weekly +- [ ] Backups verified monthly +- [ ] Access control reviewed quarterly + +--- + +## 📚 Additional Resources + +- [OWASP Top 10](https://owasp.org/www-project-top-ten/) +- [Fly.io Security Best Practices](https://fly.io/docs/reference/security/) +- [PostgreSQL Security](https://www.postgresql.org/docs/current/runtime-config-connection.html#RUNTIME-CONFIG-CONNECTION-SSL) +- [Node.js Security Best Practices](https://nodejs.org/en/docs/guides/security/) +- [Fastify Security](https://www.fastify.io/docs/latest/Guides/Security/) + +--- + +**Last Updated:** 2025-01-13 + +Review and update this checklist quarterly or after any security incident. diff --git a/infra/fly.io/fly.postgres.md b/infra/fly.io/fly.postgres.md new file mode 100644 index 0000000..dac0d8a --- /dev/null +++ b/infra/fly.io/fly.postgres.md @@ -0,0 +1,561 @@ +# PostgreSQL Setup on Fly.io + +This guide covers setting up and managing PostgreSQL for LinkForty on Fly.io. + +## Overview + +LinkForty requires PostgreSQL 13 or higher. Fly.io offers managed PostgreSQL clusters with: +- Automated backups +- Point-in-time recovery +- High availability options +- Built-in monitoring +- Private networking + +## Creating a PostgreSQL Cluster + +### Option 1: Development/Small Production + +For testing or small deployments: + +```bash +fly postgres create --name linkforty-db \ + --region iad \ + --initial-cluster-size 1 \ + --vm-size shared-cpu-1x \ + --volume-size 10 +``` + +**Configuration:** +- 1 machine (no high availability) +- 1 CPU, 256MB RAM +- 10GB storage +- **Cost:** Free tier eligible + +### Option 2: Production with High Availability + +For production deployments: + +```bash +fly postgres create --name linkforty-db \ + --region iad \ + --initial-cluster-size 3 \ + --vm-size shared-cpu-2x \ + --volume-size 20 +``` + +**Configuration:** +- 3 machines (1 primary + 2 replicas) +- 2 CPUs, 512MB RAM per machine +- 20GB storage per machine +- Automatic failover +- **Cost:** ~$20-30/month + +### Interactive Creation + +Simply run without arguments for interactive prompts: + +```bash +fly postgres create +``` + +The CLI will ask: +1. App name (e.g., `linkforty-db`) +2. Region (choose closest to your app) +3. Configuration (Development vs Production) + +## Attaching Database to Your App + +After creating the cluster: + +```bash +fly postgres attach linkforty-db --app your-linkforty-app +``` + +This automatically: +- Creates a dedicated database and user for your app +- Sets the `DATABASE_URL` secret in your app +- Configures connection pooling +- Enables SSL connections + +## Manual Connection String Setup + +If you prefer manual setup or use external PostgreSQL: + +```bash +# Get connection details +fly postgres connect -a linkforty-db + +# In psql: +CREATE DATABASE linkforty; +CREATE USER linkforty_user WITH ENCRYPTED PASSWORD 'your-strong-password'; +GRANT ALL PRIVILEGES ON DATABASE linkforty TO linkforty_user; +\q + +# Set the DATABASE_URL secret +fly secrets set DATABASE_URL="postgresql://linkforty_user:your-strong-password@linkforty-db.internal:5432/linkforty?sslmode=require" --app your-linkforty-app +``` + +## Connection String Format + +Your `DATABASE_URL` should follow this format: + +``` +postgresql://username:password@host:port/database?sslmode=require +``` + +**Components:** +- `username` - Database user (created by `attach` or manually) +- `password` - Strong password (20+ characters) +- `host` - For Fly Postgres: `appname.internal` (private network) +- `port` - Usually `5432` +- `database` - Database name +- `sslmode=require` - **Required** for secure connections + +## Running Migrations + +### Automatic (Recommended) + +Migrations run automatically on deploy via `fly.toml`: + +```toml +[deploy] + release_command = "npm run migrate" +``` + +Every deployment: +1. Builds your app +2. Runs `npm run migrate` (before starting the app) +3. Deploys if migrations succeed + +### Manual Migration + +If you need to run migrations manually: + +```bash +# SSH into your app +fly ssh console --app your-linkforty-app + +# Run migrations +npm run migrate + +# Exit +exit +``` + +Or connect directly to the database: + +```bash +# Connect via psql +fly postgres connect -a linkforty-db + +# Run SQL manually +\i /path/to/migration.sql + +# Or use LinkForty's migration tool +\q +``` + +## Database Management + +### Viewing Database Info + +```bash +# Status of Postgres cluster +fly postgres status linkforty-db + +# List databases +fly postgres db list linkforty-db + +# List users +fly postgres users list linkforty-db + +# View connection info +fly postgres config view linkforty-db +``` + +### Connecting to Database + +```bash +# Interactive psql session +fly postgres connect -a linkforty-db + +# Once connected: +\l # List databases +\c linkforty # Connect to specific database +\dt # List tables +\d links # Describe table schema +SELECT COUNT(*) FROM links; # Query data +\q # Quit +``` + +### Monitoring + +```bash +# View Postgres logs +fly logs -a linkforty-db + +# Check CPU/memory usage +fly status -a linkforty-db + +# Dashboard with metrics +fly dashboard linkforty-db +``` + +## Backups and Recovery + +### Automatic Backups + +Fly Postgres includes automated backups: +- **Development tier:** Daily backups, 7-day retention +- **Production tier:** Daily backups, 30-day retention +- Point-in-time recovery available + +### Manual Backup + +```bash +# Create a snapshot +fly volumes snapshots create -a linkforty-db + +# List snapshots +fly volumes snapshots list -a linkforty-db + +# Get volume ID +fly volumes list -a linkforty-db +``` + +### Export Database (Manual Backup) + +```bash +# Dump entire database +fly postgres connect -a linkforty-db -c "pg_dump linkforty" > backup.sql + +# Dump only schema +fly postgres connect -a linkforty-db -c "pg_dump --schema-only linkforty" > schema.sql + +# Dump only data +fly postgres connect -a linkforty-db -c "pg_dump --data-only linkforty" > data.sql +``` + +### Restore from Backup + +```bash +# Restore from SQL dump +fly postgres connect -a linkforty-db + +# In psql: +DROP DATABASE linkforty; # ⚠️ CAUTION: Deletes all data +CREATE DATABASE linkforty; +\q + +# Import dump +cat backup.sql | fly postgres connect -a linkforty-db -d linkforty +``` + +### Point-in-Time Recovery + +Contact Fly.io support for point-in-time recovery: + +```bash +fly support create "Need PITR for linkforty-db to " +``` + +## Scaling PostgreSQL + +### Vertical Scaling (More Resources) + +```bash +# Scale VM size +fly postgres update linkforty-db --vm-size shared-cpu-2x + +# Increase storage +fly volumes extend --size 40 -a linkforty-db +``` + +Available VM sizes: +- `shared-cpu-1x` - 256MB RAM (free tier) +- `shared-cpu-2x` - 512MB RAM +- `shared-cpu-4x` - 1GB RAM +- `shared-cpu-8x` - 2GB RAM +- `performance-1x` - 2GB RAM (dedicated CPU) +- `performance-2x` - 4GB RAM (dedicated CPU) + +### Horizontal Scaling (Read Replicas) + +Add replicas for read scaling: + +```bash +# Add replica in same region +fly postgres update linkforty-db --add-replica + +# Add replica in different region (multi-region) +fly postgres update linkforty-db --add-replica --region lhr +``` + +**Note:** LinkForty's connection pool handles read/write routing automatically. + +### High Availability + +For production, use 3-node cluster: + +```bash +fly postgres update linkforty-db --initial-cluster-size 3 +``` + +Benefits: +- Automatic failover (30-60 seconds) +- 2 replicas for read scaling +- Higher durability + +## Performance Tuning + +### Connection Pooling + +LinkForty includes connection pooling (configured in `src/database/pool.ts`): + +```typescript +// Default configuration +min: 2, // Minimum connections +max: 10, // Maximum connections +``` + +Adjust based on your VM size: +- 256MB RAM: max 5-10 connections +- 512MB RAM: max 10-20 connections +- 1GB+ RAM: max 20-50 connections + +### Indexes + +LinkForty creates these indexes automatically (via migrations): + +```sql +-- Fast lookups by code +CREATE INDEX idx_links_code ON links(code); + +-- User isolation +CREATE INDEX idx_links_user_id ON links(user_id); + +-- Analytics queries +CREATE INDEX idx_click_events_link_id ON click_events(link_id); +CREATE INDEX idx_click_events_created_at ON click_events(created_at); +``` + +### Query Performance + +Monitor slow queries: + +```bash +fly postgres connect -a linkforty-db + +# Enable logging of slow queries (>1s) +ALTER DATABASE linkforty SET log_min_duration_statement = 1000; + +# View slow queries in logs +\q + +fly logs -a linkforty-db | grep "duration:" +``` + +### Vacuum and Analyze + +PostgreSQL automatically runs `VACUUM` and `ANALYZE`. To run manually: + +```bash +fly postgres connect -a linkforty-db + +VACUUM ANALYZE links; +VACUUM ANALYZE click_events; +``` + +## Security + +### SSL/TLS Connections + +Always use `sslmode=require` in production: + +```bash +DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=require" +``` + +Verify SSL is active: + +```bash +fly postgres connect -a linkforty-db + +SELECT ssl.* FROM pg_stat_ssl ssl, pg_stat_activity a +WHERE ssl.pid = a.pid AND a.usename = 'linkforty_user'; +``` + +### Password Rotation + +Rotate database passwords quarterly: + +```bash +fly postgres connect -a linkforty-db + +ALTER USER linkforty_user WITH PASSWORD 'new-strong-password'; +\q + +# Update secret in your app +fly secrets set DATABASE_URL="postgresql://linkforty_user:new-strong-password@linkforty-db.internal:5432/linkforty?sslmode=require" --app your-linkforty-app +``` + +### Network Isolation + +Fly Postgres is only accessible: +- Within your Fly.io private network (`.internal`) +- Via WireGuard VPN (for admin access) + +Not exposed to public internet. + +## Monitoring and Alerts + +### Key Metrics to Monitor + +```bash +# Connection count +fly postgres connect -a linkforty-db -c "SELECT count(*) FROM pg_stat_activity;" + +# Database size +fly postgres connect -a linkforty-db -c "SELECT pg_size_pretty(pg_database_size('linkforty'));" + +# Table sizes +fly postgres connect -a linkforty-db -c " +SELECT + schemaname, + tablename, + pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size +FROM pg_tables +WHERE schemaname = 'public' +ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; +" +``` + +### Set Up Alerts + +Monitor these conditions: +- Storage > 80% full +- Connection pool exhausted +- Replication lag > 10s +- CPU > 80% for 5+ minutes + +Use Fly.io monitoring or integrate with: +- Datadog +- New Relic +- Prometheus + Grafana + +## Troubleshooting + +### Connection Refused + +```bash +# Check Postgres status +fly postgres status linkforty-db + +# Check DATABASE_URL is correct +fly secrets list --app your-linkforty-app + +# Verify Postgres is running +fly logs -a linkforty-db +``` + +### Out of Connections + +Increase max connections or reduce pool size: + +```bash +# Check current connections +fly postgres connect -a linkforty-db -c "SELECT count(*) FROM pg_stat_activity;" + +# Check max_connections +fly postgres connect -a linkforty-db -c "SHOW max_connections;" +``` + +### Slow Queries + +```bash +# Enable query logging +fly postgres connect -a linkforty-db + +ALTER DATABASE linkforty SET log_min_duration_statement = 1000; + +# Analyze slow queries +EXPLAIN ANALYZE SELECT * FROM links WHERE code = 'abc123'; +``` + +### Storage Full + +```bash +# Check current usage +fly volumes list -a linkforty-db + +# Extend volume +fly volumes extend --size 40 -a linkforty-db +``` + +## Alternative: External PostgreSQL + +Instead of Fly Postgres, you can use: + +### Supabase + +```bash +# Create project at https://supabase.com +# Get connection string from project settings + +fly secrets set DATABASE_URL="postgresql://postgres:[YOUR-PASSWORD]@db.xxxxx.supabase.co:5432/postgres?sslmode=require" +``` + +### Neon + +```bash +# Create project at https://neon.tech +# Copy connection string + +fly secrets set DATABASE_URL="postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/neondb?sslmode=require" +``` + +### AWS RDS + +```bash +# Create RDS PostgreSQL instance +# Ensure security group allows Fly.io IPs + +fly secrets set DATABASE_URL="postgresql://admin:password@mydb.xxxxx.us-east-1.rds.amazonaws.com:5432/linkforty?sslmode=require" +``` + +## Cost Optimization + +### Free Tier Setup +- 1x shared-cpu-1x (256MB) +- 10GB storage +- 1 region +- **Cost: $0/month** + +### Small Production (~$7/month) +- 1x shared-cpu-2x (512MB) +- 20GB storage +- Daily backups +- **Cost: ~$7/month** + +### High Availability (~$20/month) +- 3x shared-cpu-2x (512MB each) +- 20GB storage per instance +- Auto-failover +- **Cost: ~$20/month** + +Check pricing: +```bash +fly pricing postgres +``` + +## Resources + +- [Fly.io Postgres Docs](https://fly.io/docs/postgres/) +- [PostgreSQL Documentation](https://www.postgresql.org/docs/) +- [Connection Pooling Best Practices](https://www.postgresql.org/docs/current/runtime-config-connection.html) + +--- + +**Need Help?** +- Fly.io Community: https://community.fly.io/ +- PostgreSQL Help: https://www.postgresql.org/support/ diff --git a/infra/fly.io/fly.redis.md b/infra/fly.io/fly.redis.md new file mode 100644 index 0000000..09fe7c0 --- /dev/null +++ b/infra/fly.io/fly.redis.md @@ -0,0 +1,464 @@ +# Redis Setup on Fly.io + +This guide covers setting up and managing Redis for LinkForty on Fly.io. + +## Overview + +Redis is **optional but highly recommended** for LinkForty. According to the project documentation, Redis caching can reduce database queries by ~90% for repeated link lookups. + +Fly.io partners with [Upstash](https://upstash.com/) to provide managed Redis with: +- Serverless pricing (pay per request) +- Global replication +- TLS encryption +- Automatic scaling +- No idle charges + +## Creating a Redis Instance + +### Option 1: Upstash Redis via Fly.io (Recommended) + +```bash +fly redis create --name linkforty-redis --region iad +``` + +You'll be prompted to choose a plan: + +**Free Tier:** +- 256MB storage +- 10,000 commands/day +- Perfect for development +- **Cost: $0/month** + +**Eviction-$10:** +- 1GB storage +- 100K commands/day +- Evicts least-recently-used keys when full +- **Cost: $10/month** + +**Eviction-$40:** +- 5GB storage +- 500K commands/day +- Evicts least-recently-used keys when full +- **Cost: $40/month** + +**No-Eviction-$120:** +- 5GB storage +- 1M commands/day +- Never evicts keys (blocks writes when full) +- **Cost: $120/month** + +### Option 2: Direct Upstash Setup + +Alternatively, create directly at [Upstash](https://upstash.com/): + +1. Sign up at https://upstash.com +2. Create a new Redis database +3. Choose region (match your Fly.io region) +4. Select pricing plan +5. Copy the connection string + +## Setting Redis URL + +After creation, you'll receive a `REDIS_URL`. Set it as a secret: + +```bash +fly secrets set REDIS_URL="rediss://default:password@host:6379" --app your-linkforty-app +``` + +**Important:** Use `rediss://` (with double 's') for TLS encryption. + +## Redis URL Format + +Your `REDIS_URL` should follow this format: + +``` +rediss://default:password@host:6379 +``` + +**Components:** +- `rediss://` - Redis with TLS (**required** for security) +- `default` - Username (Upstash default) +- `password` - Authentication token from Upstash +- `host` - Upstash endpoint (e.g., `xxx-us-east-1.upstash.io`) +- `6379` - Standard Redis port + +## Verifying Redis Connection + +After setting the `REDIS_URL`, deploy and check logs: + +```bash +fly deploy +fly logs +``` + +Look for: +``` +Redis connected successfully +``` + +If Redis is unavailable, LinkForty will still work but will query the database for every link lookup (slower). + +## How LinkForty Uses Redis + +### Cache Strategy + +LinkForty caches link lookups with this pattern: + +1. **Request:** User visits `https://your-app.fly.dev/abc123` +2. **Check Redis:** Look for cached link data + - **Cache hit:** Return immediately (< 5ms) + - **Cache miss:** Query database, cache result +3. **TTL:** Cached for 1 hour (configurable) + +### What Gets Cached + +- Link metadata (URLs, targeting rules, UTM params) +- Link expiration status +- User ID associations + +### What Doesn't Get Cached + +- Click events (always written to database) +- Analytics aggregations +- Link creation/updates + +## Performance Benefits + +With Redis enabled: +- **Link redirects:** ~5ms (vs ~50-200ms without cache) +- **Database load:** Reduced by ~90% for popular links +- **Scalability:** Handle 1000+ req/s on small instances + +Without Redis: +- Every redirect hits the database +- Higher latency for users +- More database resources needed +- Lower maximum throughput + +## Monitoring Redis + +### Check Connection + +```bash +# View app logs +fly logs --app your-linkforty-app | grep -i redis + +# Check if REDIS_URL is set +fly secrets list --app your-linkforty-app +``` + +### Monitor Usage (Upstash Dashboard) + +Visit [Upstash Console](https://console.upstash.com/): +- View daily command count +- Monitor memory usage +- Check hit/miss ratio +- See latency metrics + +### Redis CLI Access + +Connect to your Redis instance: + +```bash +# Install redis-cli if needed +brew install redis # macOS +apt-get install redis-tools # Linux + +# Connect using REDIS_URL +redis-cli -u "rediss://default:password@host:6379" + +# Once connected: +PING # Should return PONG +KEYS link:* # List cached links +GET link:abc123 # View specific cached link +TTL link:abc123 # Check time-to-live +INFO stats # View Redis stats +DBSIZE # Count of keys +FLUSHDB # ⚠️ Clear all cache (use with caution) +``` + +## Cache Invalidation + +### Automatic Invalidation + +LinkForty automatically invalidates cache when: +- Link is updated +- Link is deleted +- Link expires + +### Manual Invalidation + +If needed, clear cache for a specific link: + +```bash +redis-cli -u "rediss://default:password@host:6379" DEL link:abc123 +``` + +Clear all cached links: + +```bash +redis-cli -u "rediss://default:password@host:6379" FLUSHDB +``` + +### Cache TTL Configuration + +Default TTL is 1 hour. To adjust, modify the caching layer in LinkForty: + +```typescript +// In your Redis cache implementation +const TTL = 60 * 60; // 1 hour in seconds +await redis.set(key, value, 'EX', TTL); +``` + +Consider: +- **Short TTL (5-15 min):** More accurate, higher DB load +- **Long TTL (1-4 hours):** Lower DB load, potentially stale data +- **No TTL:** Manual invalidation only (not recommended) + +## Scaling Redis + +### When to Upgrade + +Monitor these metrics: +- **Commands/day approaching limit** → Upgrade plan +- **Memory usage > 80%** → Upgrade plan or enable eviction +- **High latency (>10ms)** → Consider global replication +- **Frequent cache misses** → Increase TTL or memory + +### Upgrade Plan + +```bash +# Via Upstash dashboard +# https://console.upstash.com/ → Select database → Change plan +``` + +No downtime during plan changes. + +### Global Replication (Multi-Region) + +For lower latency worldwide, enable global replication: + +1. In Upstash dashboard, enable "Read Replicas" +2. Choose replica regions (e.g., `us-east`, `eu-west`, `ap-southeast`) +3. Upstash automatically routes reads to nearest replica + +**Cost:** ~2x base price per replica region + +## Security + +### TLS Encryption + +Always use `rediss://` (TLS) in production: + +```bash +# ✅ Correct (encrypted) +rediss://default:password@host:6379 + +# ❌ Wrong (unencrypted) +redis://default:password@host:6379 +``` + +### Password Rotation + +Rotate Redis password quarterly: + +1. In Upstash dashboard: **Database → Settings → Reset Password** +2. Copy new password +3. Update secret: + ```bash + fly secrets set REDIS_URL="rediss://default:NEW_PASSWORD@host:6379" + ``` +4. Deploy: + ```bash + fly deploy + ``` + +### Network Security + +Upstash Redis is publicly accessible but: +- Requires authentication (password) +- Uses TLS encryption +- Supports IP allowlisting (in Upstash dashboard) + +For extra security, enable IP allowlisting: +1. Get your Fly.io app's public IPs: `fly ips list` +2. In Upstash dashboard: **Settings → IP Allowlist** +3. Add Fly.io IPs + +## Troubleshooting + +### Redis Connection Fails + +```bash +# Check REDIS_URL is set correctly +fly secrets list --app your-linkforty-app + +# Test connection manually +redis-cli -u "rediss://default:password@host:6379" PING + +# Check app logs for errors +fly logs --app your-linkforty-app | grep -i redis +``` + +Common issues: +- Wrong password → Reset in Upstash dashboard +- Missing `rediss://` protocol → Use TLS version +- Firewall blocking port 6379 → Check Upstash IP allowlist + +### High Latency + +```bash +# Test latency +redis-cli -u "rediss://default:password@host:6379" --latency + +# Check from your app +redis-cli -u "rediss://default:password@host:6379" SLOWLOG GET 10 +``` + +Solutions: +- Enable global replication for multi-region apps +- Check Upstash status page +- Verify region matches your app region + +### Memory Full (Eviction Errors) + +```bash +# Check memory usage +redis-cli -u "rediss://default:password@host:6379" INFO memory + +# Check eviction stats +redis-cli -u "rediss://default:password@host:6379" INFO stats | grep evicted +``` + +Solutions: +- Upgrade to larger plan +- Reduce cache TTL +- Implement selective caching (only cache popular links) + +### Cache Hit Rate Too Low + +```bash +# Check hit rate +redis-cli -u "rediss://default:password@host:6379" INFO stats +# Look for: keyspace_hits and keyspace_misses +``` + +Low hit rate causes: +- TTL too short +- Traffic pattern (mostly unique links) +- Not enough memory (evictions) + +Solutions: +- Increase TTL (if data staleness is acceptable) +- Increase memory (upgrade plan) +- Profile which links are being accessed + +## Running Without Redis + +LinkForty works without Redis, but with performance trade-offs: + +### Performance Without Redis + +- **Latency:** ~50-200ms per redirect (vs ~5ms with Redis) +- **Database load:** 10x higher +- **Throughput:** Lower maximum requests/second +- **Costs:** Higher database instance needed + +### Disabling Redis + +Simply don't set `REDIS_URL`: + +```bash +fly secrets unset REDIS_URL --app your-linkforty-app +``` + +LinkForty detects missing Redis and falls back to database-only mode. + +### When to Skip Redis + +Skip Redis if: +- Very low traffic (< 100 redirects/day) +- Budget is extremely tight +- All links are unique (no repeated lookups) +- Development/testing only + +## Cost Optimization + +### Free Tier (Good for Development) +- 256MB storage +- 10,000 commands/day +- ~300 redirects/day with caching +- **Cost: $0/month** + +### Small Production ($10/month) +- 1GB storage +- 100K commands/day +- ~3,000 redirects/day with caching +- **Cost: $10/month** + +### Estimate Your Needs + +Calculate commands/day: +- Link redirect with cache hit: 1 command (`GET`) +- Link redirect with cache miss: 2 commands (`GET` + `SET`) +- Assume 50% hit rate: 1.5 commands per redirect +- For 10,000 redirects/day: ~15,000 commands/day + +Choose plan accordingly. + +### Cost Monitoring + +```bash +# In Upstash dashboard +# View: Billing → Current Usage +``` + +Set up alerts for: +- 80% of command limit reached +- 80% of storage used + +## Alternative: Self-Hosted Redis + +Instead of Upstash, you can run Redis on Fly.io: + +### Create Redis Machine + +```bash +fly launch --image redis:7-alpine --name linkforty-redis --region iad +``` + +**Pros:** +- Lower cost for high traffic +- Full control + +**Cons:** +- You manage backups +- You manage security +- No automatic scaling +- More operational overhead + +**Not recommended** unless you have specific requirements or very high traffic (>1M commands/day). + +## Best Practices + +1. **Always use TLS** (`rediss://`) +2. **Set reasonable TTL** (1-4 hours for links) +3. **Monitor hit rate** (target >70%) +4. **Rotate passwords** quarterly +5. **Enable global replication** for multi-region deployments +6. **Set up alerts** for command limits +7. **Start small** (free tier) and scale up based on metrics + +## Resources + +- [Upstash Documentation](https://docs.upstash.com/redis) +- [Redis Documentation](https://redis.io/documentation) +- [Fly.io Redis Guide](https://fly.io/docs/reference/redis/) +- [Cache Invalidation Strategies](https://redis.io/docs/manual/keyspace-notifications/) + +--- + +**Need Help?** +- Upstash Support: https://upstash.com/docs/redis/support +- Redis Community: https://redis.io/community +- Fly.io Community: https://community.fly.io/ diff --git a/infra/fly.io/fly.toml b/infra/fly.io/fly.toml new file mode 100644 index 0000000..9f34057 --- /dev/null +++ b/infra/fly.io/fly.toml @@ -0,0 +1,71 @@ +# Fly.io configuration for LinkForty +# See https://fly.io/docs/reference/configuration/ for full documentation + +app = "linkforty-core" # Change this to your unique app name +primary_region = "iad" # Change to your preferred region (iad = Washington DC) + +# Build configuration +[build] + # Use the Dockerfile from the examples directory + dockerfile = "../../examples/Dockerfile" + +# Environment variables (non-sensitive) +[env] + NODE_ENV = "production" + PORT = "8080" + # DATABASE_URL and REDIS_URL should be set as secrets (see DEPLOYMENT.md) + +# HTTP service configuration +[http_service] + internal_port = 8080 + force_https = true + auto_stop_machines = false # Keep at least one machine always running + auto_start_machines = true + min_machines_running = 1 # Always keep at least one instance running + + # HTTP service concurrency + [http_service.concurrency] + type = "requests" + hard_limit = 250 # Maximum concurrent requests per machine + soft_limit = 200 # Target concurrent requests before scaling + +# Health checks +[[http_service.checks]] + grace_period = "10s" # Wait before starting health checks + interval = "30s" # Check every 30 seconds + method = "GET" + timeout = "5s" + path = "/health" # Ensure your app has a /health endpoint + +# VM resources +# Start with shared-cpu-1x (256MB RAM) - scale up as needed +[vm] + cpu_kind = "shared" + cpus = 1 + memory_mb = 256 + +# Metrics (optional but recommended) +[[metrics]] + port = 9091 # If you add Prometheus metrics + path = "/metrics" + +# Deploy configuration +[deploy] + release_command = "npm run migrate" # Run migrations before deploying + strategy = "rolling" # Rolling deployment strategy + +# Scaling configuration +# Uncomment and adjust for auto-scaling based on traffic +# [[services.autoscaling]] +# enabled = true +# min_count = 1 # Minimum number of machines +# max_count = 10 # Maximum number of machines + +# Multi-region deployment (optional) +# Uncomment to deploy to multiple regions for lower latency +# [[regions]] +# name = "iad" # Primary region (US East) +# [[regions]] +# name = "lhr" # Europe (London) +# [[regions]] +# name = "syd" # Asia-Pacific (Sydney)