A modern, full‑stack DNS management platform with Google OAuth, PostgreSQL, Redis caching and a sleek React/Tailwind UI.
Demo • Documentation • Issues • Pull Requests
EdgeDNS is a lightweight DNS record manager that lets you:
- Create, read, update and delete DNS zones and records via a clean web UI.
- Securely authenticate users with Google OAuth 2.0 (session stored in HTTP‑only cookies).
- Persist data in PostgreSQL using Prisma ORM, with Redis for fast lookup of frequently‑used records.
Designed for developers, DevOps engineers, and small‑to‑medium teams that need a self‑hosted alternative to commercial DNS dashboards.
Current stable version: v1.0.0 (released 2026‑02‑25)
| Feature | Description | Status |
|---|---|---|
| Google OAuth login | One‑click sign‑in using a Google account; JWT stored in an HTTP‑only cookie. | ✅ Stable |
| Domain CRUD | Create, list, edit, delete DNS zones and individual records (A, CNAME, TXT, etc.). | ✅ Stable |
| Redis cache | Cached DNS lookups for sub‑second response times. | ✅ Stable |
| Responsive UI | React 19 + Tailwind 4 UI works on desktop and mobile. | ✅ Stable |
| API versioning | All endpoints prefixed with /api/v1 (future‑proof). |
✅ Stable |
| Docker support | One‑command containers for dev and production. | ✅ Stable |
| Rate limiting & audit logs (planned) | Prevent abuse and keep change history. | 🚧 Beta |
| Multi‑tenant support (planned) | Isolate DNS zones per organization. | 🚧 Experimental |
| Layer | Technology | Reason |
|---|---|---|
| Frontend | React 19, Vite 7, Tailwind 4, Axios | Fast HMR, utility‑first styling, modern React APIs |
| Backend | Node 20, Express 5, JWT, Bcrypt, Cookie‑Parser, CORS | Minimalist, async‑first HTTP server |
| Database | PostgreSQL 15 + Prisma 7 (ORM) | Strong relational model, type‑safe queries |
| Cache | Redis 7 (via ioredis) | Low‑latency read‑through caching |
| Auth | Google OAuth 2.0 (passport‑style) | Secure, no password storage |
| Containerisation | Docker & Docker‑Compose | Consistent dev/prod environments |
| CI/CD (optional) | GitHub Actions (build, test, lint) | Automated quality gates |
┌─────────────────────┐ ┌─────────────────────┐
│ React Frontend │ HTTPS │ Express API │
│ (client/ folder) │◀──────▶│ (server/ folder) │
└─────────▲───────────┘ └───────▲─────────────┘
│ │
│ │
│ │
┌──────┴───────┐ ┌───────┴───────┐
│ Redis │ │ PostgreSQL │
│ (cache) │ │ (Prisma) │
└──────────────┘ └───────────────┘
- client/ – Vite‑powered React app, Tailwind styling, API calls via Axios.
- server/ – Express server, routes split into
auth.routes.jsanddomain.routes.js. - prisma/ – Schema definition (
schema.prisma) and migration files. - db/ – Helper to initialise PostgreSQL connection (
sqlDB.js).
All code uses native ES modules ("type": "module").
| Tool | Minimum version |
|---|---|
| Node.js | 20.x |
| npm | 10.x (or use pnpm/yarn) |
| Docker & Docker‑Compose | 2.20+ (optional, for containerised dev) |
| PostgreSQL | 15.x |
| Redis | 7.x |
| Google Cloud Console | OAuth client ID & secret |
-
Clone the repository
git clone https://github.com/kaihere14/EdgeDNS.git cd EdgeDNS -
Create a
.envfile in theserver/directory# Server PORT=3000 DATABASE_URL=postgresql://postgres:password@localhost:5432/edgedns REDIS_URL=redis://localhost:6379 JWT_SECRET=super-secret-key GOOGLE_CLIENT_ID=your-google-client-id GOOGLE_CLIENT_SECRET=your-google-client-secret FRONTEND_URL=http://localhost:5173 # Vite dev server
-
Start PostgreSQL & Redis (Docker Compose recommended)
# From the repository root docker compose up -d postgres redisIf you prefer local installations, ensure the services are reachable via the URLs above.
-
Install server dependencies
cd server npm ci -
Run Prisma migrations & generate client
npx prisma migrate dev --name init # creates tables npm run postinstall # runs `prisma generate`
-
Start the backend in watch mode
npm run dev
The API will be available at
http://localhost:3000/api. -
Install client dependencies
cd ../client npm ci -
Run the frontend
npm run dev
Open
http://localhost:5173in your browser. You should see the EdgeDNS UI and be able to log in with Google.
curl http://localhost:3000/→ should returnHello, World!- Visiting the frontend should redirect you to Google sign‑in, then back to the dashboard.
# 1️⃣ Login with Google (client handles the redirect flow)
# After successful login a cookie `token` is set.
# 2️⃣ List all domains (authenticated)
curl -X GET http://localhost:3000/api/domain \
-H "Cookie: token=YOUR_JWT_COOKIE"
# 3️⃣ Create a new domain
curl -X POST http://localhost:3000/api/domain \
-H "Content-Type: application/json" \
-H "Cookie: token=YOUR_JWT_COOKIE" \
-d '{
"name": "example.com",
"records": [
{ "type": "A", "value": "93.184.216.34", "ttl": 3600 }
]
}'import axios from "axios";
import { useEffect, useState } from "react";
function DomainList() {
const [domains, setDomains] = useState([]);
useEffect(() => {
axios
.get("/api/domain", { withCredentials: true })
.then((res) => setDomains(res.data))
.catch(console.error);
}, []);
return (
<ul>
{domains.map((d) => (
<li key={d.id}>{d.name}</li>
))}
</ul>
);
}The above component works out‑of‑the‑box because Vite proxies /api/* to http://localhost:3000 (see vite.config.js).
| Task | Command |
|---|---|
| Run backend with hot‑reload | cd server && npm run dev |
| Run frontend with hot‑reload | cd client && npm run dev |
| Lint the client | npm run lint (from client/) |
| Run Prisma Studio | npx prisma studio |
| Run tests | (none defined yet – add Jest/Mocha as needed) |
| Check code style | npm run lint (client) & npx eslint . (server) |
- Client – ESLint with
eslint-plugin-react-hooksandeslint-plugin-react-refresh. - Server – Standard Node.js style; feel free to add
eslintif desired.
- Use
console.logordebugstatements; the server logs each request whenNODE_ENV=development. - Prisma query logs can be enabled by adding
log: ["query", "info", "warn", "error"]to the Prisma client initialization.
# docker-compose.yml (root)
version: "3.9"
services:
postgres:
image: postgres:15
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: edgedns
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7
ports:
- "6379:6379"
server:
build: ./server
env_file: ./server/.env
depends_on:
- postgres
- redis
ports:
- "3000:3000"
client:
build: ./client
env_file: ./client/.env
depends_on:
- server
ports:
- "5173:5173"
volumes:
pgdata:Build & run
docker compose up --build -dThe UI will be reachable at http://localhost:5173, API at http://localhost:3000/api.
- Frontend – Deploy the
client/folder to Vercel (static build). SetVITE_API_URLenvironment variable to your backend URL. - Backend – Deploy the
server/folder to Render, Railway, or any Node‑compatible PaaS. Ensure the same environment variables as in the local.envare provided.
All endpoints are prefixed with /api. The server uses cookie‑based JWT authentication; include withCredentials: true in Axios or send the token cookie with curl -b.
| Method | Endpoint | Description | Protected |
|---|---|---|---|
GET |
/api/auth/google |
Initiates Google OAuth flow (handled by client). | ❌ |
GET |
/api/auth/google/callback |
Google redirects here; server creates JWT cookie. | ❌ |
POST |
/api/auth/logout |
Clears authentication cookie. | ✅ |
| Method | Endpoint | Body | Description | Protected |
|---|---|---|---|---|
GET |
/api/domain |
– | List all domains belonging to the authenticated user. | ✅ |
POST |
/api/domain |
{ name, records[] } |
Create a new domain with optional DNS records. | ✅ |
GET |
/api/domain/:id |
– | Retrieve a single domain with its records. | ✅ |
PUT |
/api/domain/:id |
{ name?, records? } |
Update domain name or its records. | ✅ |
DELETE |
/api/domain/:id |
– | Delete a domain and all associated records. | ✅ |
{
"type": "A | CNAME | TXT | MX | SRV",
"name": "subdomain (optional, defaults to @)",
"value": "IP address, hostname, or text",
"ttl": 300 // seconds
}| HTTP Code | Meaning |
|---|---|
400 |
Validation error (missing fields, invalid record type) |
401 |
Unauthorized – missing or invalid JWT |
403 |
Forbidden – trying to access another user's domain |
404 |
Not found – domain ID does not exist |
500 |
Internal server error (see logs) |
All error responses follow:
{
"error": "Human‑readable message",
"code": 400
}- Fork the repository.
- Create a feature branch (
git checkout -b feat/awesome-feature). - Install dependencies (see Getting Started).
- Make your changes and ensure the app still builds (
npm run build). - Write tests (if applicable) and run
npm test. - Commit with a clear message (
git commit -m "feat: add rate‑limiting middleware"). - Push to your fork and open a Pull Request against
main.
- Follow the existing folder structure (
client/for UI,server/for API). - Keep TypeScript out of the repo for now – all code is plain JavaScript/JSX.
- Linting must pass (
npm run lint). - Update the README if you add public‑facing features.
| Symptom | Likely Cause | Fix |
|---|---|---|
Error: connect ECONNREFUSED 127.0.0.1:5432 |
PostgreSQL not running or wrong DATABASE_URL. |
Start Docker compose or adjust URL. |
401 Unauthorized on API calls |
Missing token cookie or expired JWT. |
Re‑login via Google; ensure withCredentials: true. |
| Tailwind styles not applying | Vite not picking up tailwind.config.js. |
Run npm run dev from client/ and verify postcss plugins. |
| Prisma schema out of sync | New migration not applied. | Run npx prisma migrate dev. |
| CORS error in browser | FRONTEND_URL mismatch. |
Set FRONTEND_URL in .env to the exact dev URL (http://localhost:5173). |
For more help, open an issue or join the #support channel in the repository’s Discussions.
| Milestone | Target | Notes |
|---|---|---|
| v1.1 | Rate limiting, audit logs | Prevent abuse, add change history. |
| v2.0 | Multi‑tenant SaaS mode | Separate organizations, custom domains. |
| v2.1 | Webhooks for DNS changes | Integrate with external services (Cloudflare, Route53). |
| v3.0 | Full TypeScript migration | Better developer experience and safety. |
Contributions that align with any of the above are especially welcome!
License: ISC – see the LICENSE file.
Author: Kaihere14 (GitHub: kaihere14)
Contributors: (maintained automatically by GitHub)
Acknowledgments
- Prisma – for the type‑safe ORM.
- Tailwind CSS – for rapid UI development.
- Google OAuth – for secure authentication.
- ioredis – for performant Redis client.