Skip to content

Commit b198e76

Browse files
committed
add self-hostable sources
1 parent d541a03 commit b198e76

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+9546
-2
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.pyc
2+
*/__pycache__/*
3+
node_modules
4+
dist
5+
src/excalidraw
6+
.env
7+
src/frontend/.env.local
8+
.vscode/settings.json

Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM node:20-slim as frontend-builder
2+
WORKDIR /app/frontend
3+
COPY src/frontend/package.json src/frontend/yarn.lock ./
4+
RUN yarn install --frozen-lockfile
5+
COPY src/frontend/ ./
6+
RUN yarn build
7+
8+
FROM python:3.11-slim
9+
WORKDIR /app
10+
COPY src/backend /app
11+
RUN pip install --no-cache-dir -r requirements.txt
12+
COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist
13+
14+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

README.md

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,102 @@
1-
# pad.ws
2-
Whiteboard as an IDE
1+
# 🚀 pad.ws: Your Whiteboard IDE 🚀
2+
3+
Welcome to `pad.ws`, the innovative whiteboard environment integrated right into your IDE!
4+
5+
## 🛠️ Self-Hosting Guide 🛠️
6+
7+
Ready to host your own `pad.ws` instance? Follow these steps:
8+
9+
### ✅ Prerequisites
10+
11+
* **Docker & Docker Compose:** Ensure you have both installed and running. [Install Docker](https://docs.docker.com/get-docker/) / [Install Docker Compose](https://docs.docker.com/compose/install/)
12+
13+
### 1️⃣ Step 1: Start PostgreSQL 🐘
14+
15+
* Run the PostgreSQL container using the provided configuration (e.g., in your `docker-compose.yml`).
16+
17+
```bash
18+
# Example command (adjust based on your setup)
19+
docker compose up -d postgres
20+
```
21+
22+
### 2️⃣ Step 2: Configure Keycloak 🔑
23+
24+
* Run the Keycloak container.
25+
* Access the Keycloak admin console.
26+
* **Create a Realm:** Name it appropriately (e.g., `pad-ws`).
27+
* **Create a Client:**
28+
* Give it a `Client ID` (e.g., `pad-ws-client`).
29+
* Enable **Client Authentication**.
30+
* Leave other settings as default for now.
31+
* **Get Credentials:**
32+
* Navigate to `Clients` -> `[Your Client ID]` -> `Credentials` tab.
33+
* Note the **Client secret**.
34+
* Update your environment variables file (`.env`) with:
35+
```dotenv
36+
OIDC_CLIENT_ID=your_client_id
37+
OIDC_CLIENT_SECRET=your_client_secret
38+
```
39+
* **Create a User:**
40+
* Navigate to `Users` -> `Create user`.
41+
* Fill in the details.
42+
* **Important:** Tick `Email verified`.
43+
* Go to the `Credentials` tab for the new user and set a password.
44+
45+
### 3️⃣ Step 3: Set Up Coder 🧑‍💻
46+
47+
* **Find Docker Group ID:** You'll need this to grant necessary permissions.
48+
```bash
49+
getent group docker | cut -d: -f3
50+
```
51+
* Update your `.env` file with the `DOCKER_GROUP_ID`:
52+
```dotenv
53+
DOCKER_GROUP_ID=your_docker_group_id
54+
```
55+
* Run the Coder container.
56+
* **Access Coder UI:** Open `http://localhost:7080` in your browser.
57+
* **First Login:** Create an administrator user (e.g., `admin`).
58+
* **Create a Template:**
59+
* Use the "Start from template" option.
60+
* Choose a base image (e.g., `docker-containers` or a simple Ubuntu). Configure it as needed.
61+
* **Generate API Key:**
62+
* Click your profile picture (top right) -> `Account` -> `API Keys`.
63+
* Generate a new token.
64+
* Update your `.env`:
65+
```dotenv
66+
CODER_API_KEY=your_coder_api_key
67+
```
68+
* **Get Template ID:**
69+
* Visit `http://localhost:7080/api/v2/templates` in your browser (or use `curl`).
70+
* Find the `id` of the template you created.
71+
* Update your `.env`:
72+
```dotenv
73+
CODER_TEMPLATE_ID=your_coder_template_id # Example: 85fb21ba-085b-47a6-9f4d-94ea979aaba9
74+
```
75+
* **Get Default Organization ID:**
76+
* Visit `http://localhost:7080/api/v2/organizations` in your browser (or use `curl`).
77+
* Find the `id` of your organization (usually the default one).
78+
* Update your `.env`:
79+
```dotenv
80+
CODER_DEFAULT_ORGANIZATION=your_organization_id # Example: 70f6af06-ef3a-4b4c-a663-c03c9ee423bb
81+
```
82+
83+
### 4️⃣ Step 4: Build & Run the Pad App 📝
84+
85+
* **Build the Docker Image:**
86+
```bash
87+
docker build -t pad .
88+
```
89+
* **Run the Application:**
90+
* Ensure all environment variables in your `.env` file are correctly set.
91+
* Run the `pad` application container (e.g., using `docker compose up pad`).
92+
93+
```bash
94+
# Example command (adjust based on your setup)
95+
docker compose up -d pad
96+
```
97+
98+
🎉 **Congratulations!** You should now have your self-hosted `pad.ws` instance up and running! 🎉
99+
100+
101+
102+

docker-compose.yml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
version: '3.8'
2+
3+
services:
4+
postgres:
5+
image: postgres:16
6+
container_name: postgres
7+
environment:
8+
POSTGRES_USER: ${POSTGRES_USER}
9+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
10+
POSTGRES_DB: ${POSTGRES_DB}
11+
volumes:
12+
- postgres_data:/var/lib/postgresql/data
13+
restart: unless-stopped
14+
network_mode: host
15+
16+
keycloak:
17+
image: quay.io/keycloak/keycloak:25.0
18+
container_name: keycloak
19+
command: start
20+
environment:
21+
KC_HOSTNAME: localhost
22+
KC_HOSTNAME_PORT: ${KEYCLOAK_PORT}
23+
KC_HTTP_ENABLED: "true"
24+
KC_HOSTNAME_STRICT_BACKCHANNEL: "false"
25+
KC_HOSTNAME_STRICT_HTTPS: "false"
26+
KC_HOSTNAME_URL: http://localhost:${KEYCLOAK_PORT}
27+
KC_HOSTNAME_ADMIN_URL: http://localhost:${KEYCLOAK_PORT}
28+
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
29+
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
30+
KC_PROXY: "edge"
31+
PROXY_ADDRESS_FORWARDING: "true"
32+
KC_DB: postgres
33+
KC_DB_URL: jdbc:postgresql://localhost:5432/${POSTGRES_DB}
34+
KC_DB_USERNAME: ${POSTGRES_USER}
35+
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
36+
restart: unless-stopped
37+
network_mode: host
38+
39+
coder:
40+
image: ghcr.io/coder/coder:latest
41+
container_name: coder
42+
environment:
43+
CODER_ACCESS_URL: http://localhost:${CODER_PORT}
44+
CODER_OIDC_ISSUER_URL: http://localhost:8080/realms/${OIDC_REALM}
45+
CODER_OIDC_CLIENT_ID: ${OIDC_CLIENT_ID}
46+
CODER_OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET}
47+
CODER_OIDC_SIGN_IN_TEXT: "Sign in for pad"
48+
CODER_ADDITIONAL_CSP_POLICY: ${CODER_ADDITIONAL_CSP_POLICY}
49+
CODER_OAUTH2_GITHUB_DEFAULT_PROVIDER_ENABLED: "false"
50+
CODER_PG_CONNECTION_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?sslmode=disable
51+
CODER_ADDRESS: 0.0.0.0:7080
52+
CODER_OIDC_IGNORE_EMAIL_VERIFIED: "true"
53+
volumes:
54+
- /var/run/docker.sock:/var/run/docker.sock
55+
group_add:
56+
- ${DOCKER_GROUP_ID}
57+
restart: unless-stopped
58+
network_mode: host
59+
60+
app:
61+
image: pad-ws
62+
container_name: pad
63+
environment:
64+
- STATIC_DIR=${STATIC_DIR}
65+
- ASSETS_DIR=${ASSETS_DIR}
66+
- OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
67+
- OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
68+
- OIDC_SERVER_URL=http://localhost:${KEYCLOAK_PORT}
69+
- OIDC_REALM=${OIDC_REALM}
70+
- REDIRECT_URI=http://localhost:${APP_PORT}/auth/callback
71+
- POSTGRES_USER=${POSTGRES_USER}
72+
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
73+
- POSTGRES_DB=${POSTGRES_DB}
74+
- POSTGRES_HOST=localhost
75+
- POSTGRES_PORT=${POSTGRES_PORT}
76+
- CODER_API_KEY=${CODER_API_KEY}
77+
- CODER_URL=http://localhost:${CODER_PORT}
78+
- CODER_TEMPLATE_ID=${CODER_TEMPLATE_ID}
79+
- CODER_DEFAULT_ORGANIZATION=${CODER_DEFAULT_ORGANIZATION}
80+
network_mode: host
81+
82+
volumes:
83+
postgres_data:
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Frontend-Backend Communication (React Query Architecture)
2+
3+
This document describes the current architecture and all communication points between the frontend and backend in the Pad.ws application, following the React Query refactor. All API interactions are now managed through React Query hooks, providing deduplication, caching, polling, and robust error handling.
4+
5+
---
6+
7+
## 1. Overview of Communication Architecture
8+
9+
- **All frontend-backend communication is handled via React Query hooks.**
10+
- **API calls are centralized in `src/frontend/src/api/hooks.ts` and `apiUtils.ts`.**
11+
- **No custom context providers for authentication or workspace state are used; hooks are called directly in components.**
12+
- **Error and loading states are managed by React Query.**
13+
- **Mutations (e.g., saving data, starting/stopping workspace) automatically invalidate relevant queries.**
14+
15+
---
16+
17+
## 2. Authentication
18+
19+
### 2.1. Authentication Status
20+
21+
- **Hook:** `useAuthCheck`
22+
- **Endpoint:** `GET /api/workspace/state`
23+
- **Usage:** Determines if the user is authenticated. Returns `true` if authenticated, `false` if 401 Unauthorized.
24+
- **Example:**
25+
```typescript
26+
import { useAuthCheck } from "./api/hooks";
27+
const { data: isAuthenticated = true } = useAuthCheck();
28+
```
29+
- **UI:** If `isAuthenticated` is `false`, the login modal (`AuthModal`) is displayed.
30+
31+
### 2.2. Login/Logout
32+
33+
- **Login:** Handled via OAuth redirects (e.g., `/auth/login?kc_idp_hint=google`).
34+
- **Logout:** Handled via redirect to `/auth/logout`.
35+
- **No direct API call from React Query; handled by browser navigation.**
36+
37+
---
38+
39+
## 3. User Profile
40+
41+
- **Hook:** `useUserProfile`
42+
- **Endpoint:** `GET /api/user/me`
43+
- **Usage:** Fetches the authenticated user's profile.
44+
- **Example:**
45+
```typescript
46+
import { useUserProfile } from "./api/hooks";
47+
const { data: userProfile, isLoading, error } = useUserProfile();
48+
```
49+
50+
---
51+
52+
## 4. Workspace Management
53+
54+
### 4.1. Workspace State
55+
56+
- **Hook:** `useWorkspaceState`
57+
- **Endpoint:** `GET /api/workspace/state`
58+
- **Usage:** Polls workspace state every 5 seconds.
59+
- **Example:**
60+
```typescript
61+
import { useWorkspaceState } from "./api/hooks";
62+
const { data: workspaceState, isLoading, error } = useWorkspaceState();
63+
```
64+
65+
### 4.2. Start/Stop Workspace
66+
67+
- **Hooks:** `useStartWorkspace`, `useStopWorkspace`
68+
- **Endpoints:** `POST /api/workspace/start`, `POST /api/workspace/stop`
69+
- **Usage:** Mutations to start/stop the workspace. On success, invalidate and refetch workspace state.
70+
- **Example:**
71+
```typescript
72+
import { useStartWorkspace, useStopWorkspace } from "./api/hooks";
73+
const { mutate: startWorkspace } = useStartWorkspace();
74+
const { mutate: stopWorkspace } = useStopWorkspace();
75+
// Usage: startWorkspace(); stopWorkspace();
76+
```
77+
78+
---
79+
80+
## 5. Canvas Data Management
81+
82+
### 5.1. Load Canvas
83+
84+
- **Hooks:** `useCanvas`, `useDefaultCanvas`
85+
- **Endpoints:** `GET /api/canvas`, `GET /api/canvas/default`
86+
- **Usage:** Loads user canvas data; falls back to default if not available or on error.
87+
- **Example:**
88+
```typescript
89+
import { useCanvas, useDefaultCanvas } from "./api/hooks";
90+
const { data: canvasData, isError } = useCanvas();
91+
const { data: defaultCanvasData } = useDefaultCanvas({ enabled: isError });
92+
```
93+
94+
### 5.2. Save Canvas
95+
96+
- **Hook:** `useSaveCanvas`
97+
- **Endpoint:** `POST /api/canvas`
98+
- **Usage:** Saves canvas data. Only called if user is authenticated.
99+
- **Example:**
100+
```typescript
101+
import { useSaveCanvas } from "./api/hooks";
102+
const { mutate: saveCanvas } = useSaveCanvas();
103+
// Usage: saveCanvas(canvasData);
104+
```
105+
106+
---
107+
108+
## 6. Error Handling
109+
110+
- **All API errors are handled by React Query and the `fetchApi` utility.**
111+
- **401 Unauthorized:** Triggers unauthenticated state; login modal is shown.
112+
- **Other errors:** Exposed via `error` property in hook results; components can display error messages or fallback UI.
113+
- **Example:**
114+
```typescript
115+
const { data, error, isLoading } = useWorkspaceState();
116+
if (error) { /* Show error UI */ }
117+
```
118+
119+
---
120+
121+
## 7. API Utility Functions
122+
123+
- **File:** `src/frontend/src/api/apiUtils.ts`
124+
- **Functions:** `fetchApi`, `handleResponse`
125+
- **Purpose:** Centralizes fetch logic, error handling, and credentials management for all API calls.
126+
127+
---
128+
129+
## 8. Summary
130+
131+
- **All frontend-backend communication is now declarative and managed by React Query hooks.**
132+
- **No legacy context classes or direct fetches remain.**
133+
- **API logic is centralized, maintainable, and testable.**
134+
- **Error handling, caching, and polling are handled automatically.**
135+
- **UI components react to hook state for loading, error, and data.**
136+
137+
This architecture ensures robust, efficient, and maintainable communication between the frontend and backend.

0 commit comments

Comments
 (0)