Skip to content

Commit 46febc8

Browse files
committed
chore: cleanup and readme
1 parent 42ca588 commit 46febc8

17 files changed

+435
-1439
lines changed

.dockerignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules/
2+
.env
3+
dist/
4+
build/
5+
db/
6+
.git/
7+
.gitignore
8+
.vscode/
9+
.idea/
10+
docker-compose.yml
11+
Dockerfile
12+
.next

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ yarn-error.log*
4040
*.tsbuildinfo
4141
next-env.d.ts
4242

43-
db
43+
db
44+
caddy_config
45+
caddy_data

Dockerfile

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
FROM node:20.14.0-alpine3.20 AS base
2+
RUN npm install -g pnpm
3+
WORKDIR /app
4+
5+
# dependencies stage
6+
FROM base AS dependencies
7+
COPY package.json pnpm-lock.yaml* ./
8+
RUN pnpm install --frozen-lockfile
9+
10+
# build stage
11+
FROM base AS builder
12+
WORKDIR /app
13+
COPY --from=dependencies /app/node_modules ./node_modules
14+
COPY . .
15+
RUN pnpx prisma generate
16+
RUN pnpm build
17+
18+
# production stage
19+
FROM base AS runner
20+
WORKDIR /app
21+
COPY --from=builder /app/prisma ./prisma
22+
COPY --from=builder /app/public ./public
23+
COPY --from=builder /app/.next/standalone ./
24+
COPY --from=builder /app/.next/static ./.next/static
25+
COPY --from=builder /app/start.sh ./start.sh
26+
RUN chmod +x /app/start.sh
27+
28+
# run the start script
29+
CMD ["/app/start.sh"]

README.md

+215-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,215 @@
1-
# Caddy Control
1+
<p align="center">
2+
<img style="width: 200px;height: 200px; margin: auto;" src="public/logo.png" alt="Caddy Control Logo">
3+
</p>
4+
5+
<p align="center" style="margin-top: 20px">
6+
<strong>Caddy Control</strong> - Open Source Domain Routing Control Service
7+
</p>
8+
9+
## About this project
10+
11+
This project was born out of the need to build a custom domain service with automated SSL management tailored for white-label SaaS platforms. There are managed services like Approximated, SaaS Custom Domains, etc., but limited open-source, self-hosted alternatives that provide a reliable experience. Leveraging Caddy, this project provides a way to programmatically integrate 'bring your own domain' features into SaaS products, as well as manage proxies for regular routing needs.
12+
13+
## Features
14+
- [x] Rest API Access
15+
- [x] Proxy management dashboard
16+
- [x] API Keys management dashboard
17+
- [ ] Domain redirection option
18+
- [ ] Multi user support
19+
20+
## Tech Stack
21+
- [Next.js](https://nextjs.org/) - Framework
22+
- [Caddy](https://caddyserver.com/) - Proxy Server
23+
- [Prisma](https://www.prisma.io/) - ORM
24+
- [Tailwind](https://tailwindcss.com/) - CSS
25+
- [shadcn/ui](https://ui.shadcn.com/) - Component Library
26+
27+
## Local Development
28+
To run caddy control locally, you will need to setup the following:
29+
- [Caddy](https://caddyserver.com/docs/install) - Recommended to run using the included development docker compose file.
30+
- [Docker](https://docs.docker.com/engine/install/) - Recommended but not mandatory.
31+
32+
**Recommended**: Run the caddy server docker image locally using the following command: <br>
33+
> pnpm dev:caddy
34+
35+
It uses the `docker-compose.dev.yml` compose file to start a local instance of caddy server.
36+
37+
Once the required setup is done, run the following commands to start caddy control locally.
38+
39+
1. Clone the repository
40+
```bash
41+
git clone https://github.com/avashForReal/caddy-control.git
42+
```
43+
44+
2. Create a .env file with the following content.
45+
```bash
46+
APP_HOST=<YOUR-APP_DOMAIN>
47+
CADDY_SERVER_IP=localhost
48+
CADDY_ADMIN_URL=http://localhost:2019
49+
JWT_SECRET="awesomesecret"
50+
```
51+
**Description:** <br>
52+
<strong>APP_HOST</strong>: Not relevant for local development. <br>
53+
<strong>CADDY_SERVER_IP</strong>: IP of the caddy server. If you are using a remote server then put the public IP of the server.<br>
54+
<strong>CADDY_ADMIN_URL</strong>: URL of the caddy admin API. If you are using a remote server then put the public IP of the server.<br>
55+
<strong>JWT_SECRET</strong>: JWT token secret.<br>
56+
57+
3. Install Dependencies
58+
```bash
59+
pnpm install
60+
```
61+
62+
4. Start development server
63+
```bash
64+
pnpm dev
65+
```
66+
67+
The first user will me seeded with `admin` username and `admin` password and will be prompted to change password after first login.
68+
69+
70+
## Self Hosting Using Docker
71+
End-to-end guide on how to self-host caddy control.
72+
73+
### Prerequisites
74+
To self host you will need a server with public a IP. Make sure to expose the ports `80` and `443` for incoming traffic.
75+
76+
Run the following script to get up and running using docker.
77+
78+
```bash
79+
docker compose up -d
80+
```
81+
82+
After the setup is complete, first user with following credentials will be created:
83+
84+
```bash
85+
username: admin
86+
password: admin
87+
```
88+
89+
This user will be prompted for password change in their first login.
90+
91+
## API Documentation
92+
93+
### Authentication
94+
95+
- All API requests must include an `x-api-key` header.
96+
97+
## Endpoints
98+
99+
### Add Domain
100+
101+
**Endpoint:** `/api/domains` <br>
102+
**Method:** `POST` <br>
103+
**Headers:** `{ "x-api-key": "your_api_key" }` <br>
104+
**Request Body:**
105+
106+
```json
107+
{
108+
"incomingAddress": "yourdomain.com",
109+
"destinationAddress": "backend.com",
110+
"port": 8080,
111+
"enableHttps": true
112+
}
113+
```
114+
115+
<br>
116+
117+
**Description:**
118+
119+
- `incomingAddress`: The domain name that users will access. This must be a valid domain (e.g., abc.mywhitelabledomain.com). This domain also should have an `A record` pointing to the IP address of server where caddy control is running.
120+
- `destinationAddress`: The backend server where requests should be routed. This must also be a valid domain (e.g., customer.saasprovider.com).
121+
- `port`: The port number of the backend server that will handle incoming traffic (e.g., 8080). If the desination has SSL enabled then this should probably be set to `443`.
122+
- `enableHttps` (optional): Determines whether HTTPS should be enabled for the domain. Defaults to true if not provided.
123+
124+
**Response:**
125+
126+
```json
127+
{ "message": "Domain added successfully!" }
128+
```
129+
130+
### Get Caddy Configuration
131+
132+
**Endpoint:** `/api/caddy/config` <br>
133+
**Method:** `GET` <br>
134+
**Headers:** `{ "x-api-key": "your_api_key" }` <br>
135+
**Response:**
136+
137+
```json
138+
{
139+
"config": {
140+
/* Caddy Configuration */
141+
}
142+
}
143+
```
144+
145+
### Get Registered Domains
146+
147+
**Endpoint:** `/api/domains` <br>
148+
**Method:** `GET` <br>
149+
**Headers:** `{ "x-api-key": "your_api_key" }` <br>
150+
**Response:**
151+
152+
```json
153+
{
154+
"data": [
155+
{
156+
"id": "cm8o9l9d50001sy2uorjt2wua",
157+
"incomingAddress": "demo.incomingaddress.com",
158+
"destinationAddress": "xyz.destination.com",
159+
"port": 443,
160+
"isLocked": false,
161+
"enableHttps": true,
162+
"createdAt": "2025-03-25T08:59:25.481Z",
163+
"checkResults": {
164+
"dnsCheck": {
165+
"result": false,
166+
"description": "Domain does not resolve to proxy IP."
167+
},
168+
"proxyReachability": {
169+
"result": false,
170+
"description": "Requests do not reach the proxy."
171+
}
172+
}
173+
}
174+
],
175+
"total": 1
176+
}
177+
```
178+
179+
### Delete Domain
180+
181+
**Endpoint:** `/api/domains` <br>
182+
**Method:** `DELETE` <br>
183+
**Headers:** `{ "x-api-key": "your_api_key" }` <br>
184+
**Request Body:**
185+
186+
```json
187+
{ "incomingAddress": "yourdomain.com" }
188+
```
189+
190+
**Description:**
191+
192+
- `incomingAddress`: The domain address that you want to delete.
193+
194+
**Response:**
195+
196+
```json
197+
{ "message": "Domain deleted successfully!" }
198+
```
199+
200+
### Error Handling
201+
202+
Responses follow this format:
203+
204+
```json
205+
{
206+
"error": "Error message",
207+
"details": [
208+
/* Validation errors */
209+
]
210+
}
211+
```
212+
213+
## License
214+
215+
MIT

deploy.sh

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/bin/bash
2+
3+
# ANSI color codes
4+
GREEN='\033[0;32m'
5+
BLUE='\033[0;34m'
6+
YELLOW='\033[1;33m'
7+
RED='\033[0;31m'
8+
NC='\033[0m'
9+
10+
echo -e "${BLUE}=========================================${NC}"
11+
echo -e "${GREEN}Caddy Control Setup${NC}"
12+
echo -e "${BLUE}=========================================${NC}"
13+
14+
echo -e "${YELLOW}Enter Caddy Server IP (required):${NC}"
15+
read -p "> " CADDY_SERVER_IP
16+
17+
if [[ -z "$CADDY_SERVER_IP" ]]; then
18+
echo -e "${RED}Error: Caddy Server IP is required. Exiting.${NC}"
19+
exit 1
20+
fi
21+
22+
echo -e "${YELLOW}Enter App Host domain (required):${NC}"
23+
read -p "> " APP_HOST
24+
25+
if [[ -z "$APP_HOST" ]]; then
26+
echo -e "${RED}Error: App Host is required. Exiting.${NC}"
27+
exit 1
28+
fi
29+
30+
echo -e "${YELLOW}Enter JWT secret (required):${NC}"
31+
read -p "> " JWT_SECRET
32+
33+
if [[ -z "$JWT_SECRET" ]]; then
34+
echo -e "${RED}Error: JWT secret is required. Exiting.${NC}"
35+
exit 1
36+
fi
37+
38+
echo "Creating docker-compose.yml file with your configuration..."
39+
40+
# replace values in the docker-compose.yml file
41+
if [[ "$OSTYPE" == "darwin"* ]]; then
42+
sed -i '' "s/APP_HOST=.*/APP_HOST=${APP_HOST}/" docker-compose.yml
43+
sed -i '' "s/CADDY_SERVER_IP=.*/CADDY_SERVER_IP=${CADDY_SERVER_IP}/" docker-compose.yml
44+
sed -i '' "s/JWT_SECRET=.*/JWT_SECRET=${JWT_SECRET}/" docker-compose.yml
45+
else
46+
sed -i "s/APP_HOST=.*/APP_HOST=${APP_HOST}/" docker-compose.yml
47+
sed -i "s/CADDY_SERVER_IP=.*/CADDY_SERVER_IP=${CADDY_SERVER_IP}/" docker-compose.yml
48+
sed -i "s/JWT_SECRET=.*/JWT_SECRET=${JWT_SECRET}/" docker-compose.yml
49+
fi
50+
51+
52+
echo -e "${GREEN}Configuration complete!${NC}"
53+
echo -e "${BLUE}----------------------------------------${NC}"
54+
echo -e "${GREEN}Settings:${NC}"
55+
echo -e " App Host: ${APP_HOST}"
56+
echo -e " Caddy Server IP: ${CADDY_SERVER_IP}"
57+
echo -e " JWT Secret: ${JWT_SECRET}"
58+
echo -e "${BLUE}----------------------------------------${NC}"
59+
echo -e "${GREEN}You can now run 'docker-compose up -d' to start your services${NC}"

docker-compose.dev.yml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
services:
2+
caddy-server:
3+
image: caddy:2.9.1-alpine
4+
container_name: caddy-server
5+
restart: unless-stopped
6+
ports:
7+
- "80:80"
8+
- "443:443"
9+
- "443:443/udp"
10+
# development only
11+
- "2019:2019"
12+
volumes:
13+
- caddy_data:/data
14+
- caddy_config:/config
15+
command: >
16+
sh -c "echo '
17+
{
18+
admin 0.0.0.0:2019
19+
}
20+
:80, :443 {
21+
respond \"Domain management service is running!\" 200
22+
}
23+
' > /etc/caddy/Caddyfile && caddy run --config /etc/caddy/Caddyfile"
24+
networks:
25+
- proxy-network
26+
27+
networks:
28+
proxy-network:
29+
driver: bridge
30+
31+
volumes:
32+
caddy_data:
33+
caddy_config:

0 commit comments

Comments
 (0)