fix(install): unblock self-hosting + drop forced wokwi clones
Resolves several install pain points reported by users (#108, #120) and removes the obligatory upstream-clone step that confused contributors and slowed down every Docker build. Install fixes: - nginx: server_name → catch-all default_server, drop Debian's stock site so reverse-proxied users no longer get the "Welcome to nginx" page. - entrypoint: auto-generate SECRET_KEY at first boot, persisted under data/.secret_key. backend/.env is now optional in docker-compose.yml. - backend: add greenlet>=3.0.0 (SQLAlchemy async dep that was missing on some Python builds — caused uvicorn startup failures on WSL). Wokwi libs come from npm: - @wokwi/elements 1.9.2, avr8js 0.21.0, rp2040js 1.3.2 are pinned in frontend/package.json. Vite aliases removed. - Dockerfile.standalone no longer clones avr8js / rp2040js / wokwi-elements / wokwi-boards. Frontend stage is just COPY + npm install + build:docker. - Board SVGs vendored under frontend/public/boards/ (10 deduped against existing files, 2 truly new). third-party/wokwi-* clones become reference- only credits — generate-component-metadata.ts skips gracefully when absent. Production config split out: - docker-compose.prod.yml, deploy/nginx.prod.conf, nginx-host-velxio*.conf, update-third-party.bat removed. Production deployment lives in its own repo: https://github.com/velxio/velxio-prod (host nginx + HTTPS + backups + pinned upstream commit). Verified locally: 1161 frontend tests pass, build:docker completes clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c2413f6384
commit
531c337d19
|
|
@ -12,8 +12,13 @@ env/
|
|||
# Build outputs (will be built inside Docker)
|
||||
frontend/dist/
|
||||
frontend/.vite/
|
||||
third-party/avr8js/dist/
|
||||
third-party/wokwi-elements/dist/
|
||||
|
||||
# Reference-only third-party clones — Velxio resolves these from npm at
|
||||
# build time, so the Docker image doesn't need the upstream sources.
|
||||
third-party/avr8js/
|
||||
third-party/rp2040js/
|
||||
third-party/wokwi-elements/
|
||||
third-party/wokwi-boards/
|
||||
|
||||
# QEMU source & Windows builds (only prebuilt/qemu/ .so files are needed)
|
||||
third-party/qemu-lcgamboa/
|
||||
|
|
|
|||
|
|
@ -13,55 +13,26 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
# Do NOT use submodules: recursive — the submodule pointers in this repo
|
||||
# are stale and predate package.json. We clone the libs fresh below.
|
||||
|
||||
- name: Setup Node.js 22
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
# Clone third-party fresh (stale submodule pointers can't be used)
|
||||
- name: Clone third-party
|
||||
run: |
|
||||
git clone --depth=1 https://github.com/wokwi/avr8js.git third-party/avr8js
|
||||
git clone --depth=1 https://github.com/wokwi/rp2040js.git third-party/rp2040js
|
||||
git clone --depth=1 https://github.com/wokwi/wokwi-elements.git third-party/wokwi-elements
|
||||
# The metadata generator scans wokwi-elements/src/, which only ships in
|
||||
# the upstream repo (not on npm). Clone it once for the freshness check.
|
||||
- name: Clone wokwi-elements (for metadata regeneration only)
|
||||
run: git clone --depth=1 https://github.com/wokwi/wokwi-elements.git third-party/wokwi-elements
|
||||
|
||||
# Cache third-party node_modules to speed up repeated runs
|
||||
- name: Cache third-party node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
third-party/avr8js/node_modules
|
||||
third-party/rp2040js/node_modules
|
||||
third-party/wokwi-elements/node_modules
|
||||
key: third-party-${{ hashFiles('third-party/avr8js/package-lock.json', 'third-party/rp2040js/package-lock.json', 'third-party/wokwi-elements/package-lock.json') }}
|
||||
|
||||
# Cache frontend node_modules
|
||||
- name: Cache frontend node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: frontend/node_modules
|
||||
key: frontend-${{ hashFiles('frontend/package-lock.json') }}
|
||||
|
||||
# Build avr8js (referenced as file: dep in frontend/package.json)
|
||||
- name: Build avr8js
|
||||
run: cd third-party/avr8js && npm install && npm run build
|
||||
|
||||
# Build rp2040js (referenced via vite alias to dist/esm)
|
||||
- name: Build rp2040js
|
||||
run: cd third-party/rp2040js && npm install && npm run build
|
||||
|
||||
# Build wokwi-elements (referenced as file: dep in frontend/package.json)
|
||||
- name: Build wokwi-elements
|
||||
run: cd third-party/wokwi-elements && npm install && npm run build
|
||||
|
||||
# Install frontend deps (picks up file: references to avr8js and wokwi-elements)
|
||||
- name: Install frontend dependencies
|
||||
run: cd frontend && npm ci
|
||||
|
||||
# Install root deps (typescript, tsx) so the metadata generator runs
|
||||
- name: Install root dev dependencies
|
||||
run: npm ci
|
||||
|
||||
|
|
@ -78,6 +49,5 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Run all vitest tests
|
||||
- name: Run tests
|
||||
run: cd frontend && npm test
|
||||
|
|
|
|||
65
CLAUDE.md
65
CLAUDE.md
|
|
@ -72,25 +72,25 @@ npm run lint
|
|||
**Access:**
|
||||
- App: http://localhost:5173
|
||||
|
||||
### Wokwi Libraries (Local Repositories)
|
||||
### Wokwi Libraries (npm)
|
||||
|
||||
The project uses local clones of Wokwi repositories in `third-party/`:
|
||||
- `wokwi-elements/` - Web Components for electronic parts
|
||||
- `avr8js/` - AVR8 CPU emulator
|
||||
- `rp2040js/` - RP2040 emulator
|
||||
`@wokwi/elements`, `avr8js` and `rp2040js` are pulled directly from the npm
|
||||
registry — see version pins in `frontend/package.json`. No clone or local
|
||||
build is required for the Docker image, manual install, or CI.
|
||||
|
||||
**Update libraries:**
|
||||
```bash
|
||||
update-third-party.bat
|
||||
```
|
||||
The folders under `third-party/` are reference-only (credits / offline
|
||||
hacking). The one exception is `qemu-lcgamboa` (real source dependency for
|
||||
ESP32 emulation when rebuilding QEMU).
|
||||
|
||||
Or manually:
|
||||
```bash
|
||||
cd third-party/wokwi-elements
|
||||
git pull origin main
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
**Bump a wokwi lib version:** edit the version string in
|
||||
`frontend/package.json` and run `npm install` in `frontend/`.
|
||||
|
||||
**Adding new components to wokwi-elements:** the metadata generator
|
||||
(`scripts/generate-component-metadata.ts`) scans the upstream `src/`,
|
||||
which the npm package doesn't ship. Clone wokwi-elements once into
|
||||
`third-party/wokwi-elements/` and run `npm run generate:metadata`. The
|
||||
script gracefully skips when the clone is absent — `components-metadata.json`
|
||||
is committed.
|
||||
|
||||
### External Dependencies
|
||||
|
||||
|
|
@ -116,17 +116,11 @@ arduino-cli core install arduino:avr
|
|||
|
||||
### Critical Architecture Patterns
|
||||
|
||||
**1. Vite Aliases for Local Wokwi Libs**
|
||||
**1. Wokwi libs come from npm**
|
||||
|
||||
The `frontend/vite.config.ts` uses path aliases to import from local repositories:
|
||||
```typescript
|
||||
resolve: {
|
||||
alias: {
|
||||
'avr8js': path.resolve(__dirname, '../third-party/avr8js/dist/esm'),
|
||||
'@wokwi/elements': path.resolve(__dirname, '../third-party/wokwi-elements/dist/esm'),
|
||||
},
|
||||
}
|
||||
```
|
||||
`@wokwi/elements`, `avr8js` and `rp2040js` are listed as regular
|
||||
dependencies in `frontend/package.json`. Vite resolves them from
|
||||
`node_modules` like any other package — no aliases, no `file:` references.
|
||||
|
||||
**2. Multi-File Workspace (useEditorStore)**
|
||||
|
||||
|
|
@ -404,15 +398,14 @@ There are known pre-existing TS errors that do NOT block the app from running:
|
|||
|
||||
### 8. Docker Build — third-party
|
||||
|
||||
The git submodule pointers for `rp2040js` and `wokwi-elements` in this repo are stale (point to very old commits that predate `package.json`). The `Dockerfile.standalone` works around this by **cloning the libs fresh from GitHub** at build time instead of COPYing from the build context:
|
||||
`Dockerfile.standalone` does NOT clone any wokwi-* repos. The frontend stage
|
||||
just does `COPY frontend/ scripts/` then `npm install && npm run build:docker`,
|
||||
which pulls `@wokwi/elements`, `avr8js`, `rp2040js` from npm. Board SVGs live
|
||||
in `frontend/public/boards/`, component SVGs in `frontend/public/component-svgs/`,
|
||||
and `components-metadata.json` is committed.
|
||||
|
||||
```dockerfile
|
||||
RUN git clone --depth=1 https://github.com/wokwi/avr8js.git third-party/avr8js \
|
||||
&& git clone --depth=1 https://github.com/wokwi/rp2040js.git third-party/rp2040js \
|
||||
&& git clone --depth=1 https://github.com/wokwi/wokwi-elements.git third-party/wokwi-elements
|
||||
```
|
||||
|
||||
The GitHub Actions workflow does NOT use `submodules: recursive` for this reason.
|
||||
The frontend-tests CI workflow only clones `wokwi-elements` (for the
|
||||
metadata staleness check), not the other two.
|
||||
|
||||
### 9. Backend Gotchas
|
||||
|
||||
|
|
@ -445,7 +438,9 @@ npm test
|
|||
|
||||
### Adding a New Electronic Component
|
||||
|
||||
1. Check if wokwi-elements has the component (see `third-party/wokwi-elements/src/`)
|
||||
1. Check if wokwi-elements has the component — either browse
|
||||
https://github.com/wokwi/wokwi-elements or `ls third-party/wokwi-elements/src/`
|
||||
if the optional clone is present
|
||||
2. Create React wrapper in `frontend/src/components/components-wokwi/`
|
||||
3. Add component type to `useSimulatorStore` interface
|
||||
4. Update SimulatorCanvas to render the component
|
||||
|
|
|
|||
|
|
@ -69,29 +69,10 @@ FROM node:20 AS frontend-builder
|
|||
|
||||
WORKDIR /app
|
||||
|
||||
# Clone third-party fresh from upstream to avoid stale submodule pointers.
|
||||
# git is pre-installed in the node:20 Debian image.
|
||||
RUN git clone --depth=1 https://github.com/wokwi/avr8js.git third-party/avr8js \
|
||||
&& git clone --depth=1 https://github.com/wokwi/rp2040js.git third-party/rp2040js \
|
||||
&& git clone --depth=1 https://github.com/wokwi/wokwi-elements.git third-party/wokwi-elements \
|
||||
&& git clone --depth=1 https://github.com/wokwi/wokwi-boards.git third-party/wokwi-boards
|
||||
|
||||
# Build avr8js
|
||||
WORKDIR /app/third-party/avr8js
|
||||
RUN npm install && npm run build --if-present
|
||||
|
||||
# Build rp2040js (may have no build script on some commits)
|
||||
WORKDIR /app/third-party/rp2040js
|
||||
RUN npm install && npm run build --if-present
|
||||
|
||||
# Build wokwi-elements
|
||||
WORKDIR /app/third-party/wokwi-elements
|
||||
RUN npm install && npm run build --if-present
|
||||
|
||||
# Build frontend
|
||||
# components-metadata.json is already committed; skip generate:metadata
|
||||
# (it requires wokwi-elements/src which isn't needed at runtime)
|
||||
WORKDIR /app
|
||||
# avr8js, rp2040js and @wokwi/elements are pulled directly from the npm
|
||||
# registry (see frontend/package.json) — no upstream git clones needed.
|
||||
# Board SVGs live in frontend/public/boards/, component SVGs in
|
||||
# frontend/public/component-svgs/, and components-metadata.json is committed.
|
||||
COPY frontend/ frontend/
|
||||
COPY scripts/ scripts/
|
||||
WORKDIR /app/frontend
|
||||
|
|
@ -144,7 +125,10 @@ COPY backend/app/ ./app/
|
|||
# run with: docker exec velxio-app python /app/scripts/<script> --apply
|
||||
COPY backend/scripts/ ./scripts/
|
||||
|
||||
# Setup Nginx configuration
|
||||
# Setup Nginx configuration. Remove Debian's stock site so it doesn't shadow
|
||||
# ours as the default_server (was Issue #108: users behind reverse proxies got
|
||||
# the "Welcome to nginx" page because the stock site claimed default_server).
|
||||
RUN rm -f /etc/nginx/sites-enabled/default
|
||||
COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy built frontend assets from builder stage
|
||||
|
|
|
|||
75
README.md
75
README.md
|
|
@ -267,15 +267,22 @@ The `/app/data` volume contains:
|
|||
```bash
|
||||
git clone https://github.com/davidmonterocrespo24/velxio.git
|
||||
cd velxio
|
||||
cp backend/.env.example backend/.env # edit as needed
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
#### Environment variables (`backend/.env`)
|
||||
That's it — open <http://localhost:3080>. The container generates a random
|
||||
`SECRET_KEY` on first boot and persists it in `./data/`, so no `.env` is
|
||||
required to get going.
|
||||
|
||||
#### Optional: customize environment
|
||||
|
||||
Create `backend/.env` (copy from `backend/.env.example`) only when you need
|
||||
OAuth, a fixed `SECRET_KEY`, or HTTPS-only cookies. The compose file picks it
|
||||
up automatically if it exists.
|
||||
|
||||
| Variable | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `SECRET_KEY` | *(required)* | JWT signing secret |
|
||||
| `SECRET_KEY` | *(auto-generated)* | JWT signing secret. If unset the entrypoint creates one and saves it under `data/.secret_key`. |
|
||||
| `DATABASE_URL` | `sqlite+aiosqlite:////app/data/velxio.db` | SQLite path |
|
||||
| `DATA_DIR` | `/app/data` | Directory for project files |
|
||||
| `FRONTEND_URL` | `http://localhost:5173` | Used for OAuth redirect |
|
||||
|
|
@ -284,6 +291,15 @@ docker compose -f docker-compose.prod.yml up -d
|
|||
| `GOOGLE_REDIRECT_URI` | `http://localhost:8001/api/auth/google/callback` | Must match Google Console |
|
||||
| `COOKIE_SECURE` | `false` | Set `true` when serving over HTTPS |
|
||||
|
||||
> **Deploying behind a reverse proxy?** The container listens on plain HTTP
|
||||
> on port 80 and accepts any `Host` header. If you used to get the
|
||||
> "Welcome to nginx" page through your proxy, that bug was fixed in the
|
||||
> master image.
|
||||
|
||||
> **Running velxio.dev itself?** Production-only configuration (host nginx
|
||||
> + HTTPS, backups, pinned upstream commit) lives in its own repo:
|
||||
> [github.com/velxio/velxio-prod](https://github.com/velxio/velxio-prod).
|
||||
|
||||
### Option C: Manual Setup
|
||||
|
||||
**Prerequisites:** Node.js 18+, Python 3.12+, arduino-cli
|
||||
|
|
@ -293,31 +309,24 @@ git clone --recurse-submodules https://github.com/davidmonterocrespo24/velxio.gi
|
|||
cd velxio
|
||||
```
|
||||
|
||||
> **Already cloned without `--recurse-submodules`?** The `third-party/` directories will be empty. Run:
|
||||
> ```bash
|
||||
> git submodule update --init --recursive
|
||||
> ```
|
||||
> If that fails because the submodule pointers are stale, clone the libs fresh:
|
||||
> ```bash
|
||||
> cd third-party
|
||||
> git clone --depth=1 https://github.com/wokwi/avr8js.git avr8js
|
||||
> git clone --depth=1 https://github.com/wokwi/wokwi-elements.git wokwi-elements
|
||||
> git clone --depth=1 https://github.com/wokwi/rp2040js.git rp2040js
|
||||
> git clone --depth=1 https://github.com/wokwi/wokwi-boards.git wokwi-boards
|
||||
> cd ..
|
||||
> ```
|
||||
> `wokwi-boards` ships static SVG assets imported by the ESP32 / Pi Pico W
|
||||
> board components — without it the frontend build fails on missing
|
||||
> `board.svg` imports.
|
||||
|
||||
**Build the Wokwi libraries** (required before running the frontend):
|
||||
|
||||
```bash
|
||||
cd third-party/avr8js && npm install && npm run build && cd ../..
|
||||
cd third-party/wokwi-elements && npm install && npm run build && cd ../..
|
||||
cd third-party/rp2040js && npm install && npm run build && cd ../..
|
||||
# wokwi-boards is asset-only (SVGs) — no install or build needed.
|
||||
```
|
||||
> **Heads-up about `third-party/`:** `avr8js`, `rp2040js` and
|
||||
> `@wokwi/elements` are now resolved from the npm registry — `npm install`
|
||||
> in `frontend/` pulls them automatically, no clone or build of upstream
|
||||
> repos is required. The folders under `third-party/` are kept as
|
||||
> reference-only credits for the upstream projects; you only need to clone
|
||||
> them if you are *adding new components to wokwi-elements* (the metadata
|
||||
> generator scans its `src/`).
|
||||
>
|
||||
> Board SVGs live directly in `frontend/public/boards/`, so `wokwi-boards`
|
||||
> is also not required to clone.
|
||||
>
|
||||
> The one third-party folder that DOES need to be present for ESP32
|
||||
> emulation is `qemu-lcgamboa` — but only if you're rebuilding QEMU from
|
||||
> source. Otherwise the prebuilt `.so` files come from the
|
||||
> [qemu-prebuilt release](https://github.com/davidmonterocrespo24/velxio/releases/tag/qemu-prebuilt).
|
||||
>
|
||||
> Older guides may mention a `wokwi-libs/` directory; that was renamed to
|
||||
> `third-party/` long ago, so any path containing `wokwi-libs/` is stale.
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
|
|
@ -371,17 +380,19 @@ velxio/
|
|||
│ ├── models/ # User, Project (SQLAlchemy)
|
||||
│ ├── services/ # arduino_cli, esp32_worker, qemu_manager, gpio_shim
|
||||
│ └── core/ # config, security, dependencies
|
||||
├── third-party/ # Local clones of Wokwi repos
|
||||
├── third-party/ # Local clones of upstream repos
|
||||
│ ├── wokwi-elements/ # Web Components for electronic parts
|
||||
│ ├── avr8js/ # AVR8 CPU emulator
|
||||
│ ├── rp2040js/ # RP2040 emulator
|
||||
│ └── qemu-lcgamboa/ # QEMU fork for ESP32 Xtensa emulation
|
||||
│ # NB: board SVGs live directly in frontend/public/boards/
|
||||
├── img/ # Raspberry Pi 3 boot images (kernel8.img, dtb, OS image)
|
||||
├── deploy/ # nginx.conf, entrypoint.sh
|
||||
├── docs/ # Technical documentation
|
||||
├── Dockerfile.standalone # Single-container production image
|
||||
├── docker-compose.yml # Development compose
|
||||
└── docker-compose.prod.yml # Production compose
|
||||
└── docker-compose.yml # Self-hosting compose
|
||||
# (production deployment lives in
|
||||
# https://github.com/velxio/velxio-prod)
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ fastapi==0.115.0
|
|||
uvicorn[standard]==0.32.0
|
||||
websockets>=12.0
|
||||
sqlalchemy==2.0.36
|
||||
# greenlet is needed by SQLAlchemy async on some Python builds (notably Windows
|
||||
# Python 3.12+ wheels). Without it, app startup fails with
|
||||
# "ValueError: the greenlet library is required". See issue #120.
|
||||
greenlet>=3.0.0
|
||||
aiosqlite==0.20.0
|
||||
pydantic>=2.11.0
|
||||
pydantic-settings>=2.6.0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,19 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Auto-generate SECRET_KEY if not provided so the app boots out-of-the-box
|
||||
# without requiring the user to create backend/.env first. Persists in the
|
||||
# data volume so JWTs survive container restarts.
|
||||
if [ -z "$SECRET_KEY" ]; then
|
||||
SECRET_FILE="${DATA_DIR:-/app/data}/.secret_key"
|
||||
mkdir -p "$(dirname "$SECRET_FILE")"
|
||||
if [ ! -f "$SECRET_FILE" ]; then
|
||||
echo "🔑 No SECRET_KEY provided — generating one (saved to $SECRET_FILE)"
|
||||
head -c 48 /dev/urandom | base64 | tr -d '\n' > "$SECRET_FILE"
|
||||
fi
|
||||
export SECRET_KEY="$(cat "$SECRET_FILE")"
|
||||
fi
|
||||
|
||||
# Ensure arduino-cli config and board manager URLs are set up
|
||||
if [ ! -f /root/.arduino15/arduino-cli.yaml ]; then
|
||||
echo "📦 Initializing arduino-cli config..."
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost velxio.dev www.velxio.dev;
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name velxio.dev www.velxio.dev;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Frontend SPA routing
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
proxy_pass http://127.0.0.1:8001/health;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# Proxy /api requests to localhost backend
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8001/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_connect_timeout 75s;
|
||||
}
|
||||
|
||||
# Proxy websockets/docs if needed
|
||||
location /docs {
|
||||
proxy_pass http://127.0.0.1:8001/docs;
|
||||
}
|
||||
|
||||
location /openapi.json {
|
||||
proxy_pass http://127.0.0.1:8001/openapi.json;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied any;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml;
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
services:
|
||||
velxio:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.standalone
|
||||
container_name: velxio-app
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3080:80"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
environment:
|
||||
- DATABASE_URL=sqlite+aiosqlite:////app/data/velxio.db
|
||||
- DATA_DIR=/app/data
|
||||
- IDF_PATH=/opt/esp-idf
|
||||
- IDF_TOOLS_PATH=/root/.espressif
|
||||
- ARDUINO_ESP32_PATH=/opt/arduino-esp32
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- arduino-libs:/root/.arduino15
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 90s
|
||||
|
||||
volumes:
|
||||
arduino-libs:
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
# Development Docker Compose — same image as production for full parity.
|
||||
# Usage: docker compose up --build
|
||||
# Access: http://localhost:3080
|
||||
#
|
||||
# backend/.env is OPTIONAL. If missing, the entrypoint generates a random
|
||||
# SECRET_KEY so the app boots out-of-the-box. Create backend/.env (copy
|
||||
# backend/.env.example) only when you need OAuth or want a fixed SECRET_KEY.
|
||||
services:
|
||||
velxio:
|
||||
build:
|
||||
|
|
@ -11,7 +15,8 @@ services:
|
|||
ports:
|
||||
- "3080:80"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
- path: ./backend/.env
|
||||
required: false
|
||||
environment:
|
||||
- DATABASE_URL=sqlite+aiosqlite:////app/data/velxio.db
|
||||
- DATA_DIR=/app/data
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ Complete documentation of the Docker build system, CI/CD pipelines, multi-archit
|
|||
- [Security Headers](#security-headers)
|
||||
9. [Docker Compose](#docker-compose)
|
||||
- [Development (docker-compose.yml)](#development-docker-composeyml)
|
||||
- [Production (docker-compose.prod.yml)](#production-docker-composeprodyml)
|
||||
- [Production deployment](#production-deployment)
|
||||
- [Environment Variables](#environment-variables)
|
||||
- [Volumes](#volumes)
|
||||
- [Health Checks](#health-checks)
|
||||
|
|
@ -705,11 +705,14 @@ docker compose logs -f velxio # Follow logs
|
|||
|
||||
**Access:** `http://localhost:3080`
|
||||
|
||||
### Production (docker-compose.prod.yml)
|
||||
### Production deployment
|
||||
|
||||
**Location:** `docker-compose.prod.yml`
|
||||
Production-only configuration (host nginx with HTTPS, deploy/backup scripts,
|
||||
pinned upstream commit) lives in a separate repo:
|
||||
**[github.com/velxio/velxio-prod](https://github.com/velxio/velxio-prod)**.
|
||||
|
||||
Nearly identical to the dev compose file, with `container_name: velxio-app` instead of `velxio-dev`. In production, the image is typically pulled from a registry rather than built locally:
|
||||
For self-hosters who don't need the velxio.dev-specific bits, the easiest
|
||||
approach is the prebuilt image from the registry:
|
||||
|
||||
```bash
|
||||
# Pull and run the pre-built image
|
||||
|
|
@ -993,7 +996,6 @@ For ESP32 first-time compilation (cold build cache), this may still not be enoug
|
|||
| `third-party/qemu-lcgamboa/.github/workflows/build-libqemu.yml` | CI/CD: builds QEMU .so for amd64 + arm64 |
|
||||
| `deploy/entrypoint.sh` | Container startup script |
|
||||
| `deploy/nginx.conf` | Nginx reverse proxy configuration |
|
||||
| `docker-compose.yml` | Development compose file |
|
||||
| `docker-compose.prod.yml` | Production compose file |
|
||||
| `docker-compose.yml` | Self-hosting compose file (production at github.com/velxio/velxio-prod) |
|
||||
| `prebuilt/qemu/` | Local QEMU prebuilt files (optional, for dev) |
|
||||
| `backend/.env` | Backend environment variables (not committed) |
|
||||
|
|
|
|||
|
|
@ -1,33 +1,15 @@
|
|||
# ---- Stage 1: Build third-party and frontend ----
|
||||
# ---- Stage 1: Build frontend ----
|
||||
# avr8js / rp2040js / @wokwi/elements are resolved from npm by frontend's
|
||||
# package.json — no upstream clones needed.
|
||||
FROM node:20-slim AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy root package.json (needed for metadata generation via tsx)
|
||||
COPY package.json .
|
||||
RUN npm install
|
||||
|
||||
# Copy metadata generation script
|
||||
COPY scripts/ scripts/
|
||||
|
||||
# Copy and build avr8js
|
||||
COPY third-party/avr8js/ third-party/avr8js/
|
||||
WORKDIR /app/third-party/avr8js
|
||||
RUN npm install && npm run build
|
||||
|
||||
# Copy and build wokwi-elements
|
||||
COPY third-party/wokwi-elements/ third-party/wokwi-elements/
|
||||
WORKDIR /app/third-party/wokwi-elements
|
||||
RUN npm install && npm run build
|
||||
|
||||
# Copy frontend source and install dependencies
|
||||
WORKDIR /app
|
||||
COPY frontend/ frontend/
|
||||
WORKDIR /app/frontend
|
||||
RUN npm install
|
||||
|
||||
# Generate component metadata and build frontend for production
|
||||
RUN npm run build
|
||||
WORKDIR /app/frontend
|
||||
RUN npm install && npm run build:docker
|
||||
|
||||
# ---- Stage 2: Serve with nginx ----
|
||||
FROM nginx:alpine
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -27,10 +27,10 @@
|
|||
"dependencies": {
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@wokwi/elements": "file:../third-party/wokwi-elements",
|
||||
"@wokwi/elements": "1.9.2",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"avr8js": "file:../third-party/avr8js",
|
||||
"avr8js": "0.21.0",
|
||||
"axios": "^1.13.6",
|
||||
"eecircuit-engine": "^1.7.0",
|
||||
"idb-keyval": "^6.2.2",
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-router-dom": "^7.13.1",
|
||||
"rp2040js": "1.3.2",
|
||||
"react-syntax-highlighter": "^16.1.1",
|
||||
"recharts": "^3.8.1",
|
||||
"zustand": "^5.0.11"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
<svg width="28.2mm" height="53mm" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="pin-pattern" height="2.54mm" width="2.54mm" patternUnits="userSpaceOnUse">
|
||||
<circle fill="#9a916c" cx="1.27mm" cy="1.27mm" r="1mm" />
|
||||
<circle fill="white" cx="1.27mm" cy="1.27mm" r="0.6mm" />
|
||||
</pattern>
|
||||
<pattern id="esp-pin-pattern" height="4.6" width="5" patternUnits="userSpaceOnUse">
|
||||
<path d="m3.5 2.85c0.268 1.82e-4 0.525-0.106 0.716-0.296 0.187-0.19 0.296-0.445 0.297-0.713l5.7e-4 -0.836c1.82e-4 -0.268-0.109-0.525-0.296-0.716-0.19-0.19-0.447-0.296-0.715-0.297l-3.5-0.0024-0.0019 2.85z" fill="#d1c479" stroke-width="3.11"></path>
|
||||
</pattern>
|
||||
<pattern id="small-pin-pattern" height="4.6" width="2.5" patternUnits="userSpaceOnUse">
|
||||
<path d="m1.4 1.32c0-0.138-0.0547-0.271-0.153-0.37-0.098-0.0965-0.23-0.153-0.368-0.153h-0.432c-0.138 0-0.271 0.0563-0.37 0.153-0.098 0.098-0.153 0.231-0.153 0.37v1.81h1.47z" fill="#f5f9f0" stroke-width="1.61"></path>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<!-- Board -->
|
||||
<path d="m7.56 0c-4.19 0-7.56 3.37-7.56 7.56v181c0 4.19 3.37 7.56 7.56 7.56h91.5c4.19 0 7.56-3.37 7.56-7.56v-181c0-4.19-3.37-7.56-7.56-7.56h-91.5zm1.11 2.5a6.24 6.24 0 0 1 6.24 6.24 6.24 6.24 0 0 1-6.24 6.24 6.24 6.24 0 0 1-6.24-6.24 6.24 6.24 0 0 1 6.24-6.24zm88.9 0.217a6.24 6.24 0 0 1 6.24 6.24 6.24 6.24 0 0 1-6.24 6.24 6.24 6.24 0 0 1-6.24-6.24 6.24 6.24 0 0 1 6.24-6.24zm-88.9 176a6.24 6.24 0 0 1 0.141 0 6.24 6.24 0 0 1 6.24 6.24 6.24 6.24 0 0 1-6.24 6.24 6.24 6.24 0 0 1-6.24-6.24 6.24 6.24 0 0 1 6.1-6.24zm88.7 0.217a6.24 6.24 0 0 1 0.00195 0 6.24 6.24 0 0 1 0.139 0 6.24 6.24 0 0 1 6.24 6.24 6.24 6.24 0 0 1-6.24 6.24 6.24 6.24 0 0 1-6.24-6.24 6.24 6.24 0 0 1 6.1-6.24z" fill="#1a1a1a" fill-rule="evenodd"></path>
|
||||
|
||||
<!-- Pins -->
|
||||
<rect transform="translate(0, 17.12)" width="2.54mm" height="38.1mm" fill="url(#pin-pattern)" />
|
||||
<rect transform="translate(96.5, 17.12)" width="2.54mm" height="38.1mm" fill="url(#pin-pattern)" />
|
||||
|
||||
<!-- ESP32 Chip -->
|
||||
<rect transform="translate(85,34)" width="4.8" height="55" fill="url(#esp-pin-pattern)"></rect>
|
||||
<rect transform="translate(20.5,87) rotate(180)" width="4.8" height="55" fill="url(#esp-pin-pattern)"></rect>
|
||||
<rect transform="translate(80,98) rotate(90)" width="4.8" height="55" fill="url(#esp-pin-pattern)"></rect>
|
||||
<rect x="20" y="24.8" width="65.6" height="73.3" fill="#808080" fill-rule="evenodd"></rect>
|
||||
|
||||
<!-- Regulator -->
|
||||
<g fill="#ececec" fill-rule="evenodd">
|
||||
<rect x="19.3" y="143" width="7.21" height="11.5"></rect>
|
||||
<rect x="39.8" y="139" width="6.59" height="3.07"></rect>
|
||||
<rect x="40" y="147" width="6.59" height="3.07"></rect>
|
||||
<rect x="40.2" y="156" width="6.59" height="3.07"></rect>
|
||||
</g>
|
||||
<rect x="26.3" y="137" width="14" height="24.7" fill="#4d4d4d" fill-rule="evenodd"></rect>
|
||||
|
||||
<!-- Buttons -->
|
||||
<g stroke-width="1.24">
|
||||
<rect x="77.6" y="177" width="11.1" height="9.96" fill="#cecccb"></rect>
|
||||
<circle cx="83.2" cy="182" r="3.48" fill="#ffdc8e"></circle>
|
||||
<g fill="#cecccb">
|
||||
<path d="m80.7 190h-1.34v1.64c0 0.734 0.595 1.33 1.33 1.33h0.0099z"></path>
|
||||
<path d="m80.7 175h-1.34v-1.64c0-0.734 0.595-1.33 1.33-1.33h0.0099z"></path>
|
||||
<rect x="81" y="190" width="5.23" height="2.59"></rect>
|
||||
<rect x="81" y="173" width="5.23" height="2.59"></rect>
|
||||
<path d="m84.5 175c0.062 0 0.122 0.0248 0.166 0.0682 0.0434 0.0446 0.0682 0.104 0.0682 0.167 0 0.134 0.0533 0.263 0.149 0.358 0.0955 0.0942 0.224 0.148 0.358 0.148h0.0236c0.141 0 0.277-0.0558 0.376-0.155s0.155-0.234 0.155-0.374v-0.564h2.16v3.09h-1.69v0.744h-2.16v-0.392h-1.87v0.392h-2.16v-0.744h-1.69v-3.09h2.16v0.564c0 0.14 0.0558 0.275 0.155 0.374s0.234 0.155 0.376 0.155h0.0236c0.135 0 0.263-0.0533 0.358-0.148 0.0955-0.0955 0.149-0.224 0.149-0.358 0-0.0632 0.0248-0.123 0.0682-0.167 0.0446-0.0434 0.104-0.0682 0.167-0.0682z"></path>
|
||||
<path d="m81.8 190c-0.0632 0-0.123-0.0248-0.167-0.0694-0.0434-0.0434-0.0682-0.103-0.0682-0.166 0-0.134-0.0533-0.263-0.149-0.358-0.0955-0.0955-0.223-0.149-0.358-0.149h-0.0236c-0.141 0-0.277 0.0558-0.376 0.156-0.0992 0.0992-0.155 0.234-0.155 0.374v0.564h-2.16v-3.09h1.69v-0.745h2.16v0.393h1.87v-0.393h2.16v0.745h1.69v3.09h-2.16v-0.564c0-0.14-0.0558-0.275-0.155-0.374-0.0992-0.1-0.234-0.156-0.376-0.156h-0.0236c-0.134 0-0.263 0.0533-0.358 0.149s-0.149 0.224-0.149 0.358c0 0.0632-0.0248 0.123-0.0682 0.166-0.0446 0.0446-0.104 0.0694-0.166 0.0694z"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g stroke-width="1.24">
|
||||
<rect x="17.7" y="177" width="11.1" height="9.96" fill="#cecccb"></rect>
|
||||
<circle cx="23.3" cy="182" r="3.48" fill="#ffdc8e"></circle>
|
||||
<g fill="#cecccb">
|
||||
<path d="m20.8 189h-1.34v1.64c0 0.734 0.595 1.33 1.33 1.33h0.0099z"></path>
|
||||
<path d="m20.8 175h-1.34v-1.64c0-0.734 0.595-1.33 1.33-1.33h0.0099z"></path>
|
||||
<rect x="21" y="189" width="5.23" height="2.59"></rect>
|
||||
<rect x="21" y="172" width="5.23" height="2.59"></rect>
|
||||
<path d="m24.5 175c0.062 0 0.122 0.0248 0.166 0.0682 0.0434 0.0446 0.0682 0.104 0.0682 0.167 0 0.134 0.0533 0.263 0.149 0.358 0.0955 0.0942 0.224 0.148 0.358 0.148h0.0236c0.141 0 0.277-0.0558 0.376-0.155s0.155-0.234 0.155-0.374v-0.564h2.16v3.09h-1.69v0.744h-2.16v-0.392h-1.87v0.392h-2.16v-0.744h-1.69v-3.09h2.16v0.564c0 0.14 0.0558 0.275 0.155 0.374s0.234 0.155 0.376 0.155h0.0236c0.135 0 0.263-0.0533 0.358-0.148 0.0955-0.0955 0.149-0.224 0.149-0.358 0-0.0632 0.0248-0.123 0.0682-0.167 0.0446-0.0434 0.104-0.0682 0.167-0.0682z"></path>
|
||||
<path d="m21.9 189c-0.0632 0-0.123-0.0248-0.167-0.0694-0.0434-0.0434-0.0682-0.103-0.0682-0.166 0-0.134-0.0533-0.263-0.149-0.358-0.0955-0.0955-0.223-0.149-0.358-0.149h-0.0236c-0.141 0-0.277 0.0558-0.376 0.156-0.0992 0.0992-0.155 0.234-0.155 0.374v0.564h-2.16v-3.09h1.69v-0.745h2.16v0.393h1.87v-0.393h2.16v0.745h1.69v3.09h-2.16v-0.564c0-0.14-0.0558-0.275-0.155-0.374-0.0992-0.1-0.234-0.156-0.376-0.156h-0.0236c-0.134 0-0.263 0.0533-0.358 0.149s-0.149 0.224-0.149 0.358c0 0.0632-0.0248 0.123-0.0682 0.166-0.0446 0.0446-0.104 0.0694-0.166 0.0694z"></path>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- USB Connection -->
|
||||
<path d="m66.4 197 1.06 2.24c0.0651 0.142 0.0731 0.302 0.0207 0.448-0.0525 0.147-0.16 0.266-0.301 0.332-0.14 0.0665-0.302 0.0744-0.448 0.022-0.146-0.0525-0.266-0.16-0.332-0.301l-0.724-1.54-3e-3 0.207c-6e-3 0.488-0.206 0.955-0.556 1.3-0.35 0.342-0.821 0.529-1.31 0.522l-22.2-0.29c-0.489-6e-3 -0.955-0.206-1.3-0.556-0.341-0.35-0.529-0.821-0.522-1.31l3e-3 -0.207-0.764 1.52c-0.0701 0.14-0.192 0.244-0.34 0.292-0.147 0.0486-0.307 0.0365-0.446-0.0336l-1e-3 -1e-5c-0.138-0.0701-0.244-0.192-0.292-0.34-0.0486-0.147-0.0365-0.307 0.0336-0.447l1.11-2.21-0.602-8e-3 0.269-20.6 28.2 0.369-0.269 20.6z" fill="#cecccb" stroke-width="1.26"></path>
|
||||
<path d="m60.7 177-0.0236 1.8c-7.9e-4 0.0607 0.0301 0.116 0.0802 0.148 0.522 0.329 3.38 2.12 3.38 2.12l-0.0217 1.66-1.74-0.0227-0.0156 1.19-2.63-0.0344 0.0156-1.19-1.66-0.0217 0.0413-3.16c2e-3 -0.136-0.0496-0.265-0.143-0.361-0.0948-0.096-0.223-0.151-0.357-0.152l-1.58-0.0207-0.0172 1.31-6.46-0.0845 0.0172-1.31-1.58-0.0207c-0.134-2e-3 -0.264 0.0496-0.36 0.143-0.0973 0.0936-0.152 0.221-0.154 0.357l-0.0413 3.16-1.66-0.0217-0.0156 1.19-2.63-0.0344 0.0156-1.19-1.74-0.0228 0.0217-1.66s2.91-1.73 3.43-2.03c0.0522-0.0309 0.0833-0.0848 0.0841-0.146l0.0236-1.8z" fill="#989898" stroke-width="1.26"></path>
|
||||
|
||||
<!-- LEDs -->
|
||||
<g stroke-width="1.44">
|
||||
<rect x="35" y="108" width="3.83" height="9.3" fill="#e5e5e5"></rect>
|
||||
<rect x="35" y="110" width="3.83" height="5.31" fill="#f5ecde"></rect>
|
||||
|
||||
<rect x="69.5" y="108" width="3.83" height="9.3" fill="#e5e5e5"></rect>
|
||||
<rect x="69.5" y="110" width="3.83" height="5.31" fill="#f5ecde"></rect>
|
||||
</g>
|
||||
|
||||
<!-- Small Chip-->
|
||||
<rect transform="translate(69,137)" width="13.9" height="3" fill="url(#small-pin-pattern)"></rect>
|
||||
<rect transform="translate(82.8,160.5) rotate(180)" width="13.9" height="3" fill="url(#small-pin-pattern)"></rect>
|
||||
<rect transform="translate(87.2,142) rotate(90)" width="13.9" height="3" fill="url(#small-pin-pattern)"></rect>
|
||||
<rect transform="translate(64,155.8) rotate(270)" width="13.9" height="3" fill="url(#small-pin-pattern)"></rect>
|
||||
<rect x="66.9" y="140" width="17.4" height="17.4" fill="#333" stroke-width="1.61"></rect>
|
||||
|
||||
<!-- Texts -->
|
||||
<text fill="#ffffff" font-family="sans-serif" font-size="3.72px" transform="rotate(270)">
|
||||
<tspan x="-160.21" y="12.8">VIN</tspan>
|
||||
<tspan x="-151.57" y="12.8">GND</tspan>
|
||||
<tspan x="-141.43" y="12.8">D13</tspan>
|
||||
<tspan x="-131.41" y="12.8">D12</tspan>
|
||||
<tspan x="-121.9" y="12.8">D14</tspan>
|
||||
<tspan x="-112.82" y="12.8">D27</tspan>
|
||||
<tspan x="-102.75" y="12.8">D26</tspan>
|
||||
<tspan x="-93.604" y="12.8">D25</tspan>
|
||||
<tspan x="-84.082" y="12.8">D33</tspan>
|
||||
<tspan x="-73.939" y="12.8">D32</tspan>
|
||||
<tspan x="-64.263" y="12.8">D35</tspan>
|
||||
<tspan x="-54.385" y="12.8">D34</tspan>
|
||||
<tspan x="-44.529" y="12.8">VN</tspan>
|
||||
<tspan x="-35.205" y="12.8">VP</tspan>
|
||||
<tspan x="-25.439" y="12.8">EN</tspan>
|
||||
<tspan x="-161.01" y="95.8">3V3</tspan>
|
||||
<tspan x="-151.84" y="95.8">GND</tspan>
|
||||
<tspan x="-140.12" y="95.8">D15</tspan>
|
||||
<tspan x="-129.96" y="95.8">D2</tspan>
|
||||
<tspan x="-121.33" y="95.8">D4</tspan>
|
||||
<tspan x="-113.75" y="95.8">RX2</tspan>
|
||||
<tspan x="-104.04" y="95.8">TX2</tspan>
|
||||
<tspan x="-93.3" y="95.8">D5</tspan>
|
||||
<tspan x="-85.06" y="95.8">D18</tspan>
|
||||
<tspan x="-75.215" y="95.8">D19</tspan>
|
||||
<tspan x="-65.796" y="95.8">D21</tspan>
|
||||
<tspan x="-55.802" y="95.8">RX0</tspan>
|
||||
<tspan x="-45.850" y="95.8">TX0</tspan>
|
||||
<tspan x="-36.582" y="95.8">D22</tspan>
|
||||
<tspan x="-26.250" y="95.8">D23</tspan>
|
||||
</text>
|
||||
<text x="30" y="59" fill="#cecccb" font-family="sans-serif" font-size="15px">ESP32</text>
|
||||
|
||||
<!-- Antenna -->
|
||||
<path d="m24.3 22.1v-18.8h8v11.5h10.2v-11h8.5v10.5h10v-10.5h17.8v20.2" fill="none" stroke="#4f4c48" stroke-width="1px"></path>
|
||||
<path d="m69.7 4.16v19.5" fill="none" stroke="#4f4c48" stroke-width="1px"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.8 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 60 KiB |
File diff suppressed because it is too large
Load Diff
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* ESP32 Web Components
|
||||
*
|
||||
* Uses the official wokwi-boards SVG assets for realistic board rendering.
|
||||
* Pin positions are derived from board.json definitions (mm × 5 px/mm).
|
||||
* Uses board SVG assets for realistic rendering.
|
||||
* Pin positions are in mm × 5 px/mm.
|
||||
*
|
||||
* Supports three variants via the `board-kind` attribute:
|
||||
* - esp32 → ESP32 DevKit V1 (141 × 265 px)
|
||||
|
|
@ -10,16 +10,19 @@
|
|||
* - esp32-c3 → ESP32-C3 DevKitM-1 (127 × 215 px)
|
||||
*/
|
||||
|
||||
import esp32SvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-devkit-v1/board.svg?url';
|
||||
import esp32S3SvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-s3-devkitc-1/board.svg?url';
|
||||
import esp32C3SvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-c3-devkitm-1/board.svg?url';
|
||||
import esp32DevkitCV4SvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-devkit-c-v4/board.svg?url';
|
||||
import esp32CamSvgUrl from '../../../../third-party/wokwi-boards/boards/esp32-cam/board.svg?url';
|
||||
import wemosLolin32SvgUrl from '../../../../third-party/wokwi-boards/boards/wemos-lolin32-lite/board.svg?url';
|
||||
import xiaoEsp32S3SvgUrl from '../../../../third-party/wokwi-boards/boards/xiao-esp32-s3/board.svg?url';
|
||||
import arduinoNanoEsp32SvgUrl from '../../../../third-party/wokwi-boards/boards/arduino-nano-esp32/board.svg?url';
|
||||
import xiaoEsp32C3SvgUrl from '../../../../third-party/wokwi-boards/boards/xiao-esp32-c3/board.svg?url';
|
||||
import aitewinC3SvgUrl from '../../../../third-party/wokwi-boards/boards/aitewinrobot-esp32c3-supermini/board.svg?url';
|
||||
// Board SVG assets are served from frontend/public/boards/ as static URLs.
|
||||
// No Vite import resolution needed — these resolve at runtime against the
|
||||
// static asset root.
|
||||
const esp32SvgUrl = '/boards/esp32-devkit-v1.svg';
|
||||
const esp32S3SvgUrl = '/boards/esp32-s3.svg';
|
||||
const esp32C3SvgUrl = '/boards/esp32-c3.svg';
|
||||
const esp32DevkitCV4SvgUrl = '/boards/esp32-devkit-c-v4.svg';
|
||||
const esp32CamSvgUrl = '/boards/esp32-cam.svg';
|
||||
const wemosLolin32SvgUrl = '/boards/wemos-lolin32-lite.svg';
|
||||
const xiaoEsp32S3SvgUrl = '/boards/xiao-esp32-s3.svg';
|
||||
const arduinoNanoEsp32SvgUrl = '/boards/arduino-nano-esp32.svg';
|
||||
const xiaoEsp32C3SvgUrl = '/boards/xiao-esp32-c3.svg';
|
||||
const aitewinC3SvgUrl = '/boards/esp32c3-supermini.svg';
|
||||
|
||||
// ─── Pin positions (mm × 5 px/mm, from board.json) ───────────────────────────
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
/**
|
||||
* Raspberry Pi Pico W Web Component
|
||||
*
|
||||
* Uses the official wokwi-boards SVG for the Pi Pico W.
|
||||
* Dimensions: 20.9 mm × 52.75 mm → 105 × 264 px (× 5 px/mm)
|
||||
*/
|
||||
|
||||
import piPicoWSvgUrl from '../../../../third-party/wokwi-boards/boards/pi-pico-w/board.svg?url';
|
||||
const piPicoWSvgUrl = '/boards/pi-pico-w.svg';
|
||||
|
||||
// Pi Pico W pins: 1.6 mm → x=8 (left), 19.3 mm → x=97 (right)
|
||||
const PINS_PI_PICO_W = [
|
||||
|
|
|
|||
|
|
@ -1112,11 +1112,11 @@ const WokwiLibsSection: React.FC = () => (
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
wokwi-elements
|
||||
@wokwi/elements
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<code>third-party/wokwi-elements/</code>
|
||||
<code>npm</code>
|
||||
</td>
|
||||
<td>48+ Lit Web Components (LEDs, LCDs, sensors, buttons…)</td>
|
||||
</tr>
|
||||
|
|
@ -1127,7 +1127,7 @@ const WokwiLibsSection: React.FC = () => (
|
|||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<code>third-party/avr8js/</code>
|
||||
<code>npm</code>
|
||||
</td>
|
||||
<td>ATmega328p / ATmega2560 CPU emulator at 16 MHz</td>
|
||||
</tr>
|
||||
|
|
@ -1138,46 +1138,36 @@ const WokwiLibsSection: React.FC = () => (
|
|||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<code>third-party/rp2040js/</code>
|
||||
<code>npm</code>
|
||||
</td>
|
||||
<td>Raspberry Pi Pico (RP2040) emulator</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Vite Configuration</h2>
|
||||
<p>
|
||||
<code>frontend/vite.config.ts</code> uses path aliases so imports resolve to the local builds:
|
||||
All three libraries are pulled directly from the npm registry by{' '}
|
||||
<code>frontend/package.json</code>. Reference clones may live under{' '}
|
||||
<code>third-party/</code> for credits and offline hacking, but the build
|
||||
does not require them.
|
||||
</p>
|
||||
<CodeBlock language="typescript">{`resolve: {
|
||||
alias: {
|
||||
'avr8js': '../third-party/avr8js/dist/esm',
|
||||
'@wokwi/elements': '../third-party/wokwi-elements/dist/esm',
|
||||
},
|
||||
}`}</CodeBlock>
|
||||
|
||||
<h2>Updating the Libraries</h2>
|
||||
<h3>All at once (recommended)</h3>
|
||||
<CodeBlock language="bash">{`# Windows
|
||||
update-third-party.bat`}</CodeBlock>
|
||||
<p>
|
||||
Edit the version pins in <code>frontend/package.json</code>{' '}
|
||||
(<code>@wokwi/elements</code>, <code>avr8js</code>, <code>rp2040js</code>)
|
||||
and run <code>npm install</code> in <code>frontend/</code>.
|
||||
</p>
|
||||
|
||||
<h3>Manually</h3>
|
||||
<CodeBlock language="bash">{`cd third-party/wokwi-elements
|
||||
git pull origin main
|
||||
npm install && npm run build
|
||||
|
||||
cd ../avr8js
|
||||
git pull origin main
|
||||
npm install && npm run build
|
||||
|
||||
cd ../rp2040js
|
||||
git pull origin main
|
||||
npm install && npm run build`}</CodeBlock>
|
||||
|
||||
<h3>After updating wokwi-elements</h3>
|
||||
<p>Regenerate component metadata so new components appear in the picker:</p>
|
||||
<CodeBlock language="bash">{`cd frontend
|
||||
npx tsx ../scripts/generate-component-metadata.ts`}</CodeBlock>
|
||||
<h3>After bumping wokwi-elements (only when adding new components)</h3>
|
||||
<p>
|
||||
The metadata generator scans the upstream <code>src/</code>, which the
|
||||
npm package doesn't ship. Clone wokwi-elements once into{' '}
|
||||
<code>third-party/</code> and regenerate:
|
||||
</p>
|
||||
<CodeBlock language="bash">{`git clone --depth=1 https://github.com/wokwi/wokwi-elements.git \\
|
||||
third-party/wokwi-elements
|
||||
cd frontend && npm run generate:metadata`}</CodeBlock>
|
||||
|
||||
<h2>Available Wokwi Components (48)</h2>
|
||||
<table>
|
||||
|
|
@ -1776,25 +1766,18 @@ const SetupSection: React.FC = () => (
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Components not displayed</td>
|
||||
<td>
|
||||
<code>Cannot find module '@wokwi/elements'</code> /{' '}
|
||||
<code>'avr8js'</code>
|
||||
</td>
|
||||
<td>
|
||||
<pre style={{ margin: 0 }}>
|
||||
<code>cd third-party/wokwi-elements{'\n'}npm run build</code>
|
||||
<code>cd frontend{'\n'}npm install</code>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>Cannot find module 'avr8js'</code>
|
||||
</td>
|
||||
<td>
|
||||
<pre style={{ margin: 0 }}>
|
||||
<code>cd third-party/avr8js{'\n'}npm install && npm run build</code>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>LED doesn't blink</td>
|
||||
<td>LED doesn't blink</td>
|
||||
<td>
|
||||
Compile first, then click Run. Check pin assignment in the component property dialog.
|
||||
</td>
|
||||
|
|
@ -1802,9 +1785,9 @@ const SetupSection: React.FC = () => (
|
|||
<tr>
|
||||
<td>New component not in picker</td>
|
||||
<td>
|
||||
<pre style={{ margin: 0 }}>
|
||||
<code>cd frontend{'\n'}npx tsx ../scripts/generate-component-metadata.ts</code>
|
||||
</pre>
|
||||
See "After bumping wokwi-elements" above — clone
|
||||
wokwi-elements into <code>third-party/</code> then run{' '}
|
||||
<code>npm run generate:metadata</code>.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { AppHeader } from '../components/layout/AppHeader';
|
|||
import { useSEO } from '../utils/useSEO';
|
||||
import { getSeoMeta } from '../seoRoutes';
|
||||
import { trackClickCTA } from '../utils/analytics';
|
||||
import esp32C3SvgUrl from '../../../third-party/wokwi-boards/boards/esp32-c3-devkitm-1/board.svg?url';
|
||||
const esp32C3SvgUrl = '/boards/esp32-c3.svg';
|
||||
import './SEOPage.css';
|
||||
|
||||
const META = getSeoMeta('/esp32-c3-simulator')!;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { AppHeader } from '../components/layout/AppHeader';
|
|||
import { useSEO } from '../utils/useSEO';
|
||||
import { getSeoMeta } from '../seoRoutes';
|
||||
import { trackClickCTA } from '../utils/analytics';
|
||||
import esp32S3SvgUrl from '../../../third-party/wokwi-boards/boards/esp32-s3-devkitc-1/board.svg?url';
|
||||
const esp32S3SvgUrl = '/boards/esp32-s3.svg';
|
||||
import './SEOPage.css';
|
||||
|
||||
const META = getSeoMeta('/esp32-s3-simulator')!;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { AppHeader } from '../components/layout/AppHeader';
|
|||
import { useSEO } from '../utils/useSEO';
|
||||
import { getSeoMeta } from '../seoRoutes';
|
||||
import { trackClickCTA } from '../utils/analytics';
|
||||
import esp32SvgUrl from '../../../third-party/wokwi-boards/boards/esp32-devkit-v1/board.svg?url';
|
||||
const esp32SvgUrl = '/boards/esp32-devkit-v1.svg';
|
||||
import './SEOPage.css';
|
||||
|
||||
const META = getSeoMeta('/esp32-simulator')!;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { AppHeader } from '../components/layout/AppHeader';
|
|||
import { useSEO } from '../utils/useSEO';
|
||||
import { getSeoMeta } from '../seoRoutes';
|
||||
import { trackClickCTA } from '../utils/analytics';
|
||||
import piPicoSvgUrl from '../../../third-party/wokwi-boards/boards/pi-pico/board.svg?url';
|
||||
const piPicoSvgUrl = '/boards/pi-pico.svg';
|
||||
import './SEOPage.css';
|
||||
|
||||
const META = getSeoMeta('/raspberry-pi-pico-simulator')!;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from 'path'
|
||||
|
||||
// https://vite.dev/config/
|
||||
// avr8js / rp2040js / @wokwi/elements are resolved from npm via package.json.
|
||||
// (The third-party/ clones are reference-only — keep them updated for credits.)
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'avr8js': path.resolve(__dirname, '../third-party/avr8js/dist/esm'),
|
||||
'rp2040js': path.resolve(__dirname, '../third-party/rp2040js/dist/esm'),
|
||||
'@wokwi/elements': path.resolve(__dirname, '../third-party/wokwi-elements/dist/esm'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
|
|
@ -23,8 +17,6 @@ export default defineConfig({
|
|||
assetsInclude: ['**/*.wasm'],
|
||||
optimizeDeps: {
|
||||
include: ['avr8js', 'rp2040js', '@wokwi/elements', 'littlefs'],
|
||||
// Force Vite to re-bundle local wokwi-elements after adding new components
|
||||
force: true,
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
# Configuración temporal SIN SSL para obtener certificado
|
||||
server {
|
||||
listen 80;
|
||||
server_name velxio.dev www.velxio.dev;
|
||||
|
||||
# Let's Encrypt challenge
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
# Proxy temporal al contenedor
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
# Configuración de Nginx para velxio.dev
|
||||
# Copiar a: /etc/nginx/sites-available/velxio.dev
|
||||
# Luego: sudo ln -s /etc/nginx/sites-available/velxio.dev /etc/nginx/sites-enabled/
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name velxio.dev www.velxio.dev;
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# Let's Encrypt challenge
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name velxio.dev www.velxio.dev;
|
||||
|
||||
# SSL certificates (generados por certbot)
|
||||
ssl_certificate /etc/letsencrypt/live/velxio.dev/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/velxio.dev/privkey.pem;
|
||||
|
||||
# SSL configuration
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 1d;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Proxy to Docker container
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
proxy_connect_timeout 75s;
|
||||
proxy_buffering off;
|
||||
}
|
||||
}
|
||||
|
|
@ -109,11 +109,19 @@ class MetadataGenerator {
|
|||
async generate(): Promise<void> {
|
||||
console.log('🔍 Scanning wokwi-elements directory...');
|
||||
|
||||
// Source-only step: needs the wokwi-elements TypeScript src/ to scan
|
||||
// @property decorators. The npm package ships dist/ only, so this only
|
||||
// works when the upstream repo has been cloned into third-party/. If it
|
||||
// isn't there, fall back to the committed JSON — adding new components
|
||||
// requires the clone, but day-to-day dev does not.
|
||||
if (!fs.existsSync(this.wokwiElementsPath)) {
|
||||
console.error(`❌ wokwi-elements not found at: ${this.wokwiElementsPath}`);
|
||||
console.log('💡 Make sure to initialize the git submodule:');
|
||||
console.log(' git submodule update --init --recursive');
|
||||
process.exit(1);
|
||||
console.log(`ℹ️ wokwi-elements src/ not found at: ${this.wokwiElementsPath}`);
|
||||
console.log(' Skipping metadata regeneration — using the committed');
|
||||
console.log(' frontend/public/components-metadata.json as-is.');
|
||||
console.log(' To regenerate (only needed when adding new components):');
|
||||
console.log(' git clone https://github.com/wokwi/wokwi-elements.git \\');
|
||||
console.log(' third-party/wokwi-elements');
|
||||
return;
|
||||
}
|
||||
|
||||
const components: ComponentMetadata[] = [];
|
||||
|
|
|
|||
|
|
@ -23,7 +23,25 @@ const Module = require('module');
|
|||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const CJS_DIR = path.resolve(__dirname, '../third-party/wokwi-elements/dist/cjs');
|
||||
// Resolve @wokwi/elements from frontend/node_modules (preferred — installed
|
||||
// from npm) with a fallback to the third-party/ clone if someone built the
|
||||
// dist locally for development. Either way we want the CJS dist.
|
||||
function findCjsDir() {
|
||||
const candidates = [
|
||||
path.resolve(__dirname, '../frontend/node_modules/@wokwi/elements/dist/cjs'),
|
||||
path.resolve(__dirname, '../node_modules/@wokwi/elements/dist/cjs'),
|
||||
path.resolve(__dirname, '../third-party/wokwi-elements/dist/cjs'),
|
||||
];
|
||||
for (const p of candidates) {
|
||||
if (fs.existsSync(p)) return p;
|
||||
}
|
||||
console.error('[generate-component-svgs] Could not find @wokwi/elements/dist/cjs.');
|
||||
console.error('Tried:\n ' + candidates.join('\n '));
|
||||
console.error('Run `npm install` in frontend/ first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const CJS_DIR = findCjsDir();
|
||||
const OUT_DIR = path.resolve(__dirname, '../frontend/public/component-svgs');
|
||||
const BOARDS_DIR = path.resolve(__dirname, '../frontend/public/boards');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
@echo off
|
||||
echo ========================================
|
||||
echo Actualizando Wokwi Libraries
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
cd third-party
|
||||
|
||||
echo [1/3] Actualizando wokwi-elements...
|
||||
cd wokwi-elements
|
||||
git pull origin main
|
||||
call npm install --legacy-peer-deps
|
||||
call npm run build
|
||||
cd ..
|
||||
|
||||
echo.
|
||||
echo [2/3] Actualizando avr8js...
|
||||
cd avr8js
|
||||
git pull origin main
|
||||
call npm install --legacy-peer-deps
|
||||
call npm run build
|
||||
cd ..
|
||||
|
||||
echo.
|
||||
echo [3/3] Actualizando rp2040js...
|
||||
cd rp2040js
|
||||
git pull origin main
|
||||
call npm install --legacy-peer-deps
|
||||
call npm run build
|
||||
cd ..
|
||||
|
||||
cd ..
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo Actualizacion completada!
|
||||
echo ========================================
|
||||
echo.
|
||||
echo Los repositorios de Wokwi han sido actualizados.
|
||||
echo Puedes reiniciar el servidor de desarrollo si estaba corriendo.
|
||||
echo.
|
||||
pause
|
||||
Loading…
Reference in New Issue