This commit is contained in:
2026-07-31 11:47:31 +09:00
parent d338b91e42
commit 3271693051
14 changed files with 3024 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

+45
View File
@@ -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"]
+329
View File
@@ -0,0 +1,329 @@
<div align="center">
<img src="../assets/logo.png" alt="LinkForty Logo" width="150"/>
# @linkforty/core Examples
This directory contains example implementations for deploying LinkForty Core.
</div>
## 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
+46
View File
@@ -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);
+56
View File
@@ -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:
+425
View File
@@ -0,0 +1,425 @@
<div align="center">
<img src="../assets/logo.png" alt="LinkForty Logo" width="150"/>
# 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.
</div>
## 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.
+121
View File
@@ -0,0 +1,121 @@
<div align="center">
<img src="../assets/logo.png" alt="LinkForty Logo" width="150"/>
# LinkForty Infrastructure
This directory contains infrastructure-as-code templates and deployment guides for running LinkForty in production environments.
</div>
## 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.
+143
View File
@@ -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
#
# ============================================================================
+372
View File
@@ -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 <version>
```
## 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/)
+391
View File
@@ -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.
+561
View File
@@ -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 <volume-id> -a linkforty-db
# List snapshots
fly volumes snapshots list <volume-id> -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 <timestamp>"
```
## 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 <volume-id> --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 <volume-id> --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/
+464
View File
@@ -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/
+71
View File
@@ -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)