Backend API server for NPort tunnel management, deployed as a Cloudflare Worker.
🎉 TypeScript Migration:
- ✅ Migrated entire codebase to TypeScript for better type safety
- ✅ Added comprehensive type definitions for all API responses
- ✅ Improved error handling with typed exceptions
- ✅ Updated tests to TypeScript
- ✅ Added TypeScript compiler checks in CI
See CHANGELOG.md for full details.
- 🚀 Creates and manages Cloudflare Tunnels via API
- 🌐 Automatically configures DNS records (supports A, AAAA, and CNAME records)
- 🧹 Auto-cleanup of inactive tunnels via cron job (every 30 minutes)
- 🔒 Secure authentication using Cloudflare credentials (API Token or API Key)
- 🛠️ Automatic orphaned DNS record cleanup
- ✅ Smart conflict resolution for existing tunnels and DNS records
- 📝 Full TypeScript support with type definitions
- Node.js >= 20.0.0
- Cloudflare Account
- A domain managed by Cloudflare
server/
├── src/
│ └── index.ts # Main worker code (TypeScript)
├── test/
│ └── index.spec.ts # Tests (TypeScript)
├── .dev.vars # Local environment variables (DO NOT COMMIT)
├── .dev.vars.example # Example environment variables
├── wrangler.jsonc # Cloudflare Worker configuration
├── tsconfig.json # TypeScript configuration
├── vitest.config.ts # Test configuration
└── package.json # Dependencies
- Go to Cloudflare Dashboard
- Select any website/domain
- Scroll down on the Overview page
- Copy Account ID from the right sidebar
- Go to your domain's dashboard in Cloudflare
- Click on your domain (e.g.,
nport.link) - Scroll down on the Overview page
- Copy Zone ID from the right sidebar
Option A: API Token (Recommended) 🌟
API Tokens are more secure and have granular permissions.
- Go to My Profile → API Tokens
- Click Create Token
- Choose Edit Cloudflare Workers template (or create custom token)
- Add the following permissions:
- Account → Cloudflare Tunnel → Edit
- Zone → DNS → Edit
- Set Zone Resources to include your domain
- Click Continue to summary → Create Token
- Copy the token (you won't see it again!)
Option B: Global API Key (Legacy)
- Go to My Profile → API Tokens
- Scroll down to API Keys section
- Click View next to "Global API Key"
- Enter your password to reveal the key
- Copy the API Key
- You'll also need your Cloudflare account email address
cd server
npm installCopy the example file:
cp .dev.vars.example .dev.varsEdit .dev.vars with your values:
Option A: Using API Token (Recommended):
CF_API_TOKEN=your_api_token_here
CF_ACCOUNT_ID=your_account_id_here
CF_ZONE_ID=your_zone_id_here
CF_DOMAIN=your_domain_hereOption B: Using Global API Key (Legacy):
CF_EMAIL=your-cloudflare-email@example.com
CF_API_KEY=your_global_api_key_here
CF_ACCOUNT_ID=your_account_id_here
CF_ZONE_ID=your_zone_id_here
CF_DOMAIN=your_domain_hereStart the development server:
npm run devThe server will be available at http://localhost:8787
Test the API:
# Create a tunnel
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{"subdomain":"test-123"}'
# Delete a tunnel
curl -X DELETE http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{"subdomain":"test-123","tunnelId":"your-tunnel-id"}'npm run deployAfter deploying, set the secrets in production:
# Set API Token
wrangler secret put CF_API_TOKEN
# Set Account ID
wrangler secret put CF_ACCOUNT_ID
# Set Zone ID
wrangler secret put CF_ZONE_ID
# Set Domain
wrangler secret put CF_DOMAINVerify secrets are set:
wrangler secret listIf you want to use a custom domain like api.nport.link:
- Go to your Cloudflare Workers Dashboard
- Click on your nport worker
- Go to Settings → Triggers
- Click Add Custom Domain
- Enter your subdomain (e.g.,
api.nport.link) - Click Add Custom Domain
Creates a new tunnel with optional custom subdomain.
Request:
curl -X POST https://api.nport.link \
-H "Content-Type: application/json" \
-d '{"subdomain":"my-app"}'Response:
{
"success": true,
"tunnelId": "abc123-def456-...",
"tunnelToken": "eyJh...",
"url": "https://my-app.nport.link"
}Deletes an existing tunnel and its DNS record.
Request:
curl -X DELETE https://api.nport.link \
-H "Content-Type: application/json" \
-d '{
"subdomain": "my-app",
"tunnelId": "abc123-def456-..."
}'Response:
{
"success": true
}Redirects to https://nport.link
The worker includes a scheduled job that runs every 30 minutes to clean up inactive tunnels.
Configuration in wrangler.jsonc:
{
"triggers": {
"crons": ["*/30 * * * *"]
}
}- Finds all tunnels with status
down,inactive, ordegraded - Deletes associated DNS records
- Removes the tunnels
Edit the CLEANUP_PREFIXES constant in src/index.ts:
// Clean up all tunnels (except protected ones)
const CLEANUP_PREFIXES: string[] = [];
// Or clean up only specific prefixes
const CLEANUP_PREFIXES: string[] = ['user-', 'temp-', 'test-'];Edit the PROTECTED_SUBDOMAINS constant to prevent certain subdomains from being created or cleaned up:
const PROTECTED_SUBDOMAINS: string[] = ['api', 'www', 'admin'];# Install dependencies
npm install
# Run local development server
npm run dev
# Run TypeScript type checking
npm run typecheck
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Deploy to production
npm run deploy
# View live logs
wrangler tail
# List secrets
wrangler secret listThe server includes comprehensive TypeScript types:
// Environment variables
interface Env {
CF_ACCOUNT_ID: string;
CF_ZONE_ID: string;
CF_DOMAIN: string;
CF_API_TOKEN?: string;
CF_EMAIL?: string;
CF_API_KEY?: string;
}
// API responses
interface ApiResponse {
success: boolean;
error?: string;
tunnelId?: string;
tunnelToken?: string;
url?: string;
}
// Tunnel status
interface Tunnel {
id: string;
name: string;
status: 'healthy' | 'degraded' | 'down' | 'inactive';
token?: string;
}wrangler tail- Go to Cloudflare Workers Dashboard
- Click on your nport worker
- Go to Logs tab
- Enable Real-time logs
- Problem: Environment variables not set in production
- Solution: Run
wrangler secret putfor each required variable
- Problem: Invalid or missing authentication credentials
- Solutions:
- Verify your API Token or API Key is correct
- Check that all required secrets are set:
wrangler secret list - If using API Token, ensure it has the correct permissions
- Problem: A tunnel with the same name is currently active
- Solution: Choose a different subdomain or wait for the existing tunnel to disconnect
- Problem: The subdomain is in the protected list
- Solution: Choose a different subdomain
- Never commit
.dev.varsto git (it's in.gitignore) - Use API Tokens instead of Global API Key when possible
- Use secrets for production (via
wrangler secret put) - Rotate keys regularly if you suspect they've been compromised
- Monitor usage - Regularly check Cloudflare audit logs
MIT