|
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 |
0 commit comments