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
4 changes: 1 addition & 3 deletions Dockerfile.backend
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ RUN pnpm install --frozen-lockfile

WORKDIR /app/apps/backend

COPY apps/backend/.env .env

RUN pnpm --filter backend exec prisma generate
RUN pnpm --filter backend exec prisma migrate deploy

RUN pnpm --filter backend build

CMD ["pnpm", "--filter", "backend", "start:prod"]
CMD ["pnpm", "--filter", "backend", "start:prod"]
1 change: 0 additions & 1 deletion Dockerfile.frontend
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ RUN corepack enable && corepack prepare pnpm@latest --activate

RUN pnpm install --frozen-lockfile

WORKDIR /app/apps/frontend

RUN pnpm build

Expand Down
63 changes: 60 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,68 @@

---

## 🚀 Getting Started (Coming Soon)
# 🚀 Getting Started



## 🐳 Docker & Production Deployment

Dyan ships with a production-ready Docker Compose setup that runs the backend, frontend, and SQLite database with a single command.

### 1. Prerequisites

- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/) installed

### 2. Environment Variables

Create a `.env` file in the project root (same directory as `docker-compose.yml`) with the following content:

```env
# .env
JWT_SECRET=your_super_secret_jwt_key
VITE_API_URL=http://backend:3000
```

- `JWT_SECRET` is required for backend authentication.
- `VITE_API_URL` should point to the backend service as seen by the frontend container (use `http://backend:3000` for Docker Compose).

You can also refer to `apps/backend/example.env` and `apps/frontend/example.env` for more details.

### 3. Build & Run

From the project root, run:

```bash
docker compose up --build
```

- The backend will be available at [http://localhost:3000](http://localhost:3000)
- The frontend will be available at [http://localhost:5173](http://localhost:5173)

### 4. Data Persistence

- The SQLite database is persisted in a Docker volume (`sqlite-data`), so your data survives container restarts.

### 5. Customization

- To change ports or add environment variables, edit `docker-compose.yml`.
- For production, consider setting up HTTPS and using a production-grade database.

### 6. Stopping & Cleaning Up

To stop the services:

```bash
docker compose down
```

To remove all data (including the SQLite database):

```bash
docker compose down -v
```

> Full setup instructions, Docker templates, and CLI will be available soon.

---

## 💡 Example Use Cases

Expand Down
1 change: 1 addition & 0 deletions apps/backend/example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
JWT_SECRET="YOUR_SECRET"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need only a single .env file in the root, both for the frontend and the backend.

16 changes: 15 additions & 1 deletion apps/backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,21 @@ async me(@Req() req: Request) {
});
}

return this.authService.sendMagicLink(email);
// Generate the magic link (modify sendMagicLink to return the link)
// const magicLink = await this.authService.sendMagicLink(email);
const mockMode = process.env.MOCK_EMAIL_ENABLED === 'true';
const magicLink = await this.authService.sendMagicLink(email, mockMode);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sendMagicLink(email, mockMode) only accepts email as an argument You did not implement the sendmagicLink funtion


// Check the environment variable
if (process.env.MOCK_EMAIL_ENABLED === 'true') {
console.log(`\n=== MOCK LOGIN LINK ===\n${magicLink}\n======================\n`);
return { message: 'Mock login link printed to console.' };
}

// Otherwise, proceed as normal (send email)
return { message: 'Login link sent to email.' };

// return this.authService.sendMagicLink(email);
}

@Get('verify')
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
*.local
.env

# Editor directories and files
.vscode/*
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_API_URL="http://localhost:3000"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need only a single .env file in the root, both for the frontend and the backend.

6 changes: 3 additions & 3 deletions apps/frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useEffect, useState } from 'react';
import './App.css'

function App() {
const [message, setMessage] = useState('');

const apiUrl = import.meta.env.VITE_API_URL;

useEffect(() => {
fetch('http://localhost:3000/api/hello')
fetch(`${apiUrl}/api/hello`)
.then(res => res.json())
.then(data => setMessage(data.message))
.catch(err => console.error(err));
Expand Down
4 changes: 2 additions & 2 deletions apps/frontend/src/components/SavedEndpointsSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Button } from "../components/ui/button";
import { Badge } from "../components/ui/badge";
import { ScrollArea } from "../components/ui/scroll-area";
import { Separator } from "../components/ui/separator";
// import { Separator } from "../components/ui/separator";
import {
PlusCircle,
Pencil,
Trash2,
Settings,
Code2,
// Code2,
Layers,
Globe,
} from "lucide-react";
Expand Down
14 changes: 8 additions & 6 deletions apps/frontend/src/pages/builder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ export default function BuilderPage() {
const [liveHeaders, setLiveHeaders] = useState("{}");
const [liveQuery, setLiveQuery] = useState("{}");

const apiUrl = import.meta.env.VITE_API_URL;

useEffect(() => {
const loadEndpoints = async () => {
try {
const res = await fetch("http://localhost:3000/dyan/endpoints", {
const res = await fetch(`${apiUrl}/dyan/endpoints`, {
credentials: "include",
});
const data = await res.json();
Expand Down Expand Up @@ -96,7 +98,7 @@ export default function BuilderPage() {

const methodType = existing ? "PUT" : "POST";

const res = await fetch("http://localhost:3000/dyan/endpoint", {
const res = await fetch(`${apiUrl}/dyan/endpoint`, {
method: methodType,
credentials: "include",
headers: { "Content-Type": "application/json" },
Expand Down Expand Up @@ -135,7 +137,7 @@ export default function BuilderPage() {

try {
const res = await fetch(
`http://localhost:3000/dyan/endpoint?path=${encodeURIComponent(ep.path)}&method=${ep.method}`,
`${apiUrl}/dyan/endpoint?path=${encodeURIComponent(ep.path)}&method=${ep.method}`,
{
credentials: "include",
}
Expand All @@ -152,7 +154,7 @@ export default function BuilderPage() {
const ep = endpoints[index];

try {
await fetch("http://localhost:3000/dyan/endpoint", {
await fetch(`${apiUrl}/dyan/endpoint`, {
method: "DELETE",
credentials: "include",
headers: { "Content-Type": "application/json" },
Expand Down Expand Up @@ -195,7 +197,7 @@ export default function BuilderPage() {

try {
const queryParams = new URLSearchParams(parsedQuery).toString();
const targetUrl = `http://localhost:3000${path}${queryParams ? `?${queryParams}` : ""}`;
const targetUrl = `${apiUrl}${path}${queryParams ? `?${queryParams}` : ""}`;

const res = await fetch(targetUrl, {
method,
Expand All @@ -222,7 +224,7 @@ export default function BuilderPage() {

const handleLogout = async () => {
try {
await fetch("http://localhost:3000/dyan/auth/logout", {
await fetch(`${apiUrl}/dyan/auth/logout`, {
method: "POST",
credentials: "include",
});
Expand Down
4 changes: 3 additions & 1 deletion apps/frontend/src/pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ export default function LoginPage() {
);
const [errorMessage, setErrorMessage] = useState("");

const apiUrl = import.meta.env.VITE_API_URL;

const requestLogin = async () => {
setStatus("loading");
setErrorMessage("");

try {
const res = await fetch("http://localhost:3000/dyan/auth/request", {
const res = await fetch(`${apiUrl}/dyan/auth/request`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/src/pages/mainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import BuilderPage from "./builder";
export default function MainPage() {
const [loading, setLoading] = useState(true);
const navigate = useNavigate();
const apiUrl = import.meta.env.VITE_API_URL;

useEffect(() => {
fetch("http://localhost:3000/dyan/auth/me", {
fetch(`${apiUrl}/dyan/auth/me`, {
credentials: "include",
})
.then((res) => {
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/src/pages/verify.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function VerifyPage() {
);
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const apiUrl = import.meta.env.VITE_API_URL;

useEffect(() => {
const verify = async () => {
Expand All @@ -20,7 +21,7 @@ export default function VerifyPage() {

try {
const res = await fetch(
`http://localhost:3000/dyan/auth/verify?token=${token}`,
`${apiUrl}/dyan/auth/verify?token=${token}`,
{
credentials: "include",
}
Expand Down
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@ services:
dockerfile: Dockerfile.backend
ports:
- "3000:3000"
environment:
- JWT_SECRET=${JWT_SECRET}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, we will add more environment variables, so we will need to use the .env file.

- BACKEND_PORT=3000
volumes:
- sqlite-data:/app/apps/backend/prisma

frontend:
build:
context: .
dockerfile: Dockerfile.frontend
ports:
- "5173:80"
environment:
- VITE_API_URL=${VITE_API_URL}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future, we will add more environment variables, so we will need to use the .env file to pass the values

depends_on:
- backend

volumes:
sqlite-data: