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
+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: