Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 42 additions & 83 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,114 +1,73 @@
# Contributing to Outray

Thanks for your interest in contributing to Outray! This guide will help you get started.
Thanks for your interest in contributing to Outray! This guide will help you get set up and start shipping features quickly.

## Project Structure

Our monorepo is organized to keep logic decoupled and reusable:

```
outray/
├── apps/
│ ├── cli/ # CLI client for creating tunnels
│ ├── cron/ # Background jobs (tunnel snapshots)
│ ├── internal-check/ # Domain verification for Caddy on-demand TLS
│ ├── landing/ # Marketing website (Astro)
│ ├── tunnel/ # Tunnel server (HTTP, TCP, UDP proxying)
│ └── web/ # Dashboard & API (React + TanStack Router)
├── shared/ # Shared utilities and types
└── deploy/ # Deployment scripts and configs
│ ├── cli/ # Main Outray CLI (TypeScript)
│ ├── web/ # Dashboard & API (React + TanStack Start)
│ ├── tunnel/ # High-performance Tunnel Server
│ ├── cron/ # Maintenance snapshots & background tasks
│ └── internal-check/ # Caddy domain verification service
├── packages/
│ ├── core/ # Shared tunnel core logic
│ ├── vite-plugin/ # Outray integration for Vite
│ └── ...plugins/ # Next.js, Express, NestJS plugins
├── shared/ # Global types and utility helpers
└── deploy/ # Infrastructure-as-Code & scripts
```

## Prerequisites

- Node.js 20+
- npm
- Redis (for tunnel state)
- PostgreSQL (for user data)
- Tiger Data / TimescaleDB (for analytics)
## Quick Start (Local Setup)

## Getting Started

1. **Clone the repository**
Prepare your environment in three simple steps:

1. **Clone & Install**
```bash
git clone https://github.com/akinloluwami/outray.git
cd outray
```

2. **Install dependencies**

```bash
npm install
```

3. **Set up environment variables**

Copy `.env.example` to `.env` in each app directory and fill in the values:

```bash
cp apps/web/.env.example apps/web/.env
cp apps/tunnel/.env.example apps/tunnel/.env
cp apps/cron/.env.example apps/cron/.env
cp apps/internal-check/.env.example apps/internal-check/.env
```

4. **Run database migrations**

```bash
cd apps/web
npx drizzle-kit push
```

5. **Set up Tiger Data (TimescaleDB) tables**

Run the schema file against your TimescaleDB instance:

2. **Initialize Environment**
We provide a helper script to set up all `.env` files across the monorepo:
```bash
psql "$TIMESCALE_URL" -f deploy/setup_tigerdata.sql
npm run setup:envs
```

6. **Start development servers**

3. **Spin Up Infrastructure**
Outray requires Redis, Postgres, and TimescaleDB. Use Docker to start them instantly:
```bash
# Terminal 1: Web dashboard
cd apps/web && npm run dev

# Terminal 2: Tunnel server
cd apps/tunnel && npm run dev

# Terminal 3: CLI (for testing)
cd apps/cli && npm run dev
docker compose up -d
```

## Development

### Web Dashboard (`apps/web`)

- React with TanStack Router
- Drizzle ORM for database
- Better Auth for authentication
## Development Workflow

### Tunnel Server (`apps/tunnel`)
Instead of managing multiple terminals, use our root commands:

- Handles HTTP, TCP, and UDP tunneling
- WebSocket-based protocol for client communication
- Redis for tunnel state management
- **Full Application**: Run `npm run dev` in the root to start the Web Dashboard, Tunnel Server, and CLI in parallel.
- **Specific App**: Run `npm run dev -w apps/web` (or any other app).
- **Database Migrations**:
```bash
cd apps/web
npm run db:migrate
```

### CLI (`apps/cli`)
## Code Guidelines

- TypeScript CLI for creating tunnels
- Supports HTTP, TCP, and UDP protocols

## Code Style

- Use TypeScript
- Follow existing code patterns
- Run `npm run lint` before committing
- **TypeScript First**: All new code must be type-safe.
- **Consistency**: Follow the patterns in `packages/core` for any tunnel logic.
- **Linting**: Run `npm run lint` at the root before pushing.

## Pull Requests

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/my-feature`)
3. Make your changes
4. Test your changes locally
5. Commit with a descriptive message
6. Push and open a PR. Add a detailed description of your changes and attach a screenshot if you made UI changes.
1. **Branching**: Use `feat/`, `fix/`, or `docs/` prefixes (e.g., `feat/add-github-oauth`).
2. **Quality**: Ensure your code builds locally with `npm run build`.
3. **Description**: Describe *what* changed and *why*. Attach screenshots if you've touched the Dashboard UI.

---
*Stay sharp and happy coding!*
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ outray udp 53
```
outray/
├── apps/
│ ├── cli/ # CLI client
│ ├── cron/ # Background jobs
│ ├── internal-check/ # Domain verification for Caddy
│ ├── landing/ # Marketing website
│ ├── cli/ # Main CLI client
│ ├── web/ # Dashboard & API
│ ├── tunnel/ # Tunnel server
│ └── web/ # Dashboard & API
│ ├── cron/ # Background jobs
│ └── internal-check/ # Caddy domain verification
├── packages/ # Core logic & plugins
├── shared/ # Shared utilities
└── deploy/ # Deployment configs
```
Expand Down
34 changes: 34 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
version: '3.8'

services:
postgres:
image: postgres:16
ports:
- "5432:5432"
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: outray
volumes:
- postgres_data:/var/lib/postgresql/data

redis:
image: redis:alpine
ports:
- "6379:6379"

timescale:
image: timescale/timescaledb-ha:pg16
ports:
- "5433:5432"
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: outray_analytics
volumes:
- timescale_data:/var/lib/postgresql/data

volumes:
postgres_data:
redis_data:
timescale_data:
18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
"version": "1.0.0",
"private": true,
"workspaces": [
"apps/tunnel",
"apps/cli",
"apps/web"
"apps/*",
"packages/*",
"shared"
],
"scripts": {
"install:all": "npm install && cd apps/tunnel && npm install && cd ../cli && npm install && cd ../web && npm install",
"dev:tunnel": "cd apps/tunnel && npm run dev",
"dev:web": "cd apps/web && npm run dev",
"build": "cd apps/tunnel && npm run build && cd ../cli && npm run build && cd ../web && npm run build"
"dev": "concurrently \"npm run dev -w apps/web\" \"npm run dev -w apps/tunnel\" \"npm run dev -w apps/cli\"",
"install:all": "npm install",
"setup:envs": "node ./scripts/setup-envs.js",
"build": "npm run build --workspaces --if-present",
"lint": "npm run lint --workspaces --if-present"
},
"devDependencies": {
"concurrently": "^9.1.0",
"typescript": "^5.7.2"
},
"dependencies": {
"axios": "^1.13.2",
"zustand": "^5.0.9"
}
}
}
25 changes: 25 additions & 0 deletions scripts/setup-envs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const fs = require('fs');
const path = require('path');

const apps = [
'apps/web',
'apps/tunnel',
'apps/cron',
'apps/internal-check'
];

apps.forEach(appDir => {
const examplePath = path.join(appDir, '.env.example');
const envPath = path.join(appDir, '.env');

if (fs.existsSync(examplePath)) {
if (!fs.existsSync(envPath)) {
fs.copyFileSync(examplePath, envPath);
console.log(`✅ Created .env for ${appDir}`);
} else {
console.log(`ℹ️ .env already exists for ${appDir}, skipping.`);
}
}
});

console.log('✨ Environment variable setup complete!');