Docker files and README.md Update

This commit is contained in:
2026-05-14 17:12:00 -04:00
parent 61251b3ea7
commit 6178e9e55b
4 changed files with 128 additions and 49 deletions
+15
View File
@@ -24,7 +24,22 @@ RUN mkdir -p /data
RUN curl -L https://github.com/Myriad-Dreamin/tinymist/releases/latest/download/tinymist-alpine-x64 -o /usr/local/bin/tinymist && chmod +x /usr/local/bin/tinymist
COPY --from=frontend-builder /app/build /app/build
COPY --from=backend-builder /app/server/target/release/server /app/server
# --- Environment Variables ---
# PORT Server listen port (default: 3000)
# STATIC_DIR Path to compiled frontend assets (default: /app/build)
# DATABASE_URL Database connection URL
# SQLite: sqlite:///data/typstdrive.db?mode=rwc
# Postgres: postgres://user:pass@host:5432/typstdrive
# DB_TYPE Database backend: "sqlite" or "postgres"
# Auto-detected from DATABASE_URL if not set.
# COOKIE_SECRET 64+ byte secret for signing session cookies.
# If unset, a random key is generated on each start
# and all sessions are invalidated on restart.
# RUST_LOG Log filter (default: server=debug,tower_http=debug)
ENV PORT=3000
ENV STATIC_DIR=/app/build
ENV DATABASE_URL=sqlite:///data/typstdrive.db?mode=rwc
ENV DB_TYPE=sqlite
EXPOSE 3000
CMD ["/app/server"]
+71 -43
View File
@@ -6,6 +6,7 @@
[![SvelteKit](https://img.shields.io/badge/SvelteKit-5-ff3e00?logo=svelte)](https://kit.svelte.dev/)
[![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-06B6D4?logo=tailwindcss&logoColor=white)](https://tailwindcss.com/)
[![Bun](https://img.shields.io/badge/Bun-latest-black?logo=bun)](https://bun.sh/)
[![SQLite](https://img.shields.io/badge/SQLite-003B57?logo=sqlite&logoColor=white)](https://www.sqlite.org/)
[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-316192?logo=postgresql&logoColor=white)](https://www.postgresql.org/)
[![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=white)](https://www.docker.com/)
@@ -27,20 +28,30 @@ TypstDrive allows you to upload custom `.ttf` or `.otf` fonts and image files (`
### Custom Fonts
When you upload a font file (e.g., `JetBrainsMono-Regular.ttf`), it is automatically made available to the Typst compiler and the intelligent `tinymist` Language Server. TypstDrive extracts the true typographic family name embedded inside the font file and auto-populates it in your document and dropdowns.
Upload `.ttf` or `.otf` files from the dashboard or the editor toolbar. TypstDrive reads the typographic family name embedded in the file, registers all weight and style variants (Bold, Italic, etc.) under that family, and makes them available immediately to the Typst compiler and the `tinymist` LSP — no page refresh required.
You can use the font in two ways:
Use the font by its family name, which appears automatically in the editor's font dropdown:
1. **By Typographic Family Name:** This is extracted automatically when you upload the font.
```typst
#set text(font: "JetBrains Mono")
```
2. **By Filename (Convenience Alias):** You can also use the exact name of the uploaded file (without the extension).
```typst
#set text(font: "JetBrainsMono-Regular")
```
```typst
#set text(font: "JetBrains Mono")
```
*Note: You do not need to refresh the page after uploading a font. The LSP server will automatically restart and detect your newly uploaded font, providing instant autocompletion and removing any "Unknown Font Family" warnings!*
Bold, italic, and other variants resolve automatically as long as the corresponding font files are uploaded:
```typst
#set text(font: "Noto Sans")
*This renders in Noto Sans Italic.*
*#strong[This renders in Noto Sans Bold.]*
```
#### Uploading from Google Fonts
Google Fonts downloads come as a ZIP containing one `.ttf` per variant (e.g., `NotoSans-Regular.ttf`, `NotoSans-Bold.ttf`, `NotoSans-Italic.ttf`). **Do not upload the ZIP** — extract it first, then select and upload all the `.ttf` files at once. The dashboard file picker supports multi-file selection.
For Google Fonts that offer a **variable font** (a single file covering all weights and styles), uploading just that one file is sufficient.
> The LSP restarts automatically after a font upload, providing instant autocompletion and clearing any "Unknown Font Family" warnings.
### Images
@@ -52,13 +63,12 @@ Uploaded images can be referenced natively using the `#image` function in Typst.
## Self-Hosting
TypstDrive is completely self-hostable. We provide a Docker image that packages both the Rust backend and the SvelteKit frontend.
TypstDrive is completely self-hostable. A Docker image packages both the Rust backend and the SvelteKit frontend into a single container.
### Prerequisites
- [Docker](https://docs.docker.com/get-docker/)
- [Docker Compose](https://docs.docker.com/compose/install/)
- [Tinymist](https://github.com/Myriad-Dreamin/tinymist) (Required if running the backend locally for Language Server features)
### Getting Started
@@ -70,55 +80,73 @@ TypstDrive is completely self-hostable. We provide a Docker image that packages
2. Start the application:
```bash
docker-compose up -d
docker compose up -d
```
3. Open your browser and navigate to:
```
http://localhost:3000
```
3. Open your browser and navigate to `http://localhost:3000`.
### Data Storage
The PostgreSQL database containing users and documents is persisted via the Docker volume `pgdata`. This is automatically configured in `docker-compose.yml` to ensure your data persists across container restarts.
By default, TypstDrive uses **SQLite** — no separate database container required. All data is stored in a single file persisted via the `appdata` Docker volume.
To switch to **PostgreSQL**, uncomment the `db` service in `docker-compose.yml` and update `DATABASE_URL` and `DB_TYPE` as shown in the comments there.
### Environment Variables
All variables can be set in the `environment:` section of `docker-compose.yml` or passed directly to the container.
| Variable | Default | Description |
|---|---|---|
| `DATABASE_URL` | `sqlite:///data/typstdrive.db?mode=rwc` | Database connection URL. Use `sqlite:///path/to/file.db?mode=rwc` for SQLite or `postgres://user:pass@host:5432/db` for PostgreSQL. |
| `DB_TYPE` | auto-detected | Database backend. Set to `sqlite` or `postgres`. Auto-detected from `DATABASE_URL` prefix if omitted. |
| `PORT` | `3000` | Port the HTTP server listens on. |
| `STATIC_DIR` | `/app/build` | Path to compiled frontend assets. |
| `COOKIE_SECRET` | *(random)* | 64+ byte secret used to sign session cookies. **If not set, a random key is generated on startup and all sessions are invalidated on every container restart.** Generate a stable value with: `openssl rand -hex 64` |
| `RUST_LOG` | `server=debug,tower_http=debug` | Log filter. Set to `info` for quieter production logs. |
#### Example: production-ready `docker-compose.yml` snippet
```yaml
environment:
- DATABASE_URL=sqlite:///data/typstdrive.db?mode=rwc
- DB_TYPE=sqlite
- PORT=3000
- COOKIE_SECRET=your-64-plus-byte-secret-here
- RUST_LOG=info
```
Generate a `COOKIE_SECRET`:
```bash
openssl rand -hex 64
```
## Contributing & Local Development
If you'd like to contribute or run TypstDrive without Docker, you must first clone the Typst compiler repository into the `typst` folder for testing and building the backend:
Clone the official Typst compiler into the `typst/` folder before building the backend:
```bash
git clone https://github.com/typst/typst.git typst
```
### Frontend
1. Install dependencies: `npm install`
2. Run the dev server: `npm run dev`
1. Install dependencies: `bun install`
2. Run the dev server: `bun run dev`
### Backend
1. Ensure you have the required dependencies installed (e.g., `libssl-dev` on Ubuntu: `sudo apt-get install libssl-dev`).
2. Install the `tinymist` CLI and ensure it is in your system's PATH, as the backend relies on it for Language Server Protocol (LSP) functionality.
(e.g., via `cargo binstall tinymist` or downloading from [releases](https://github.com/Myriad-Dreamin/tinymist/releases)). Example for Linux x64:
1. Install system dependencies (e.g., on Ubuntu: `sudo apt-get install libssl-dev`).
2. Install `tinymist` and ensure it is on your `PATH` — the backend uses it for LSP features:
```bash
curl -L -o ~/.cargo/bin/tinymist https://github.com/Myriad-Dreamin/tinymist/releases/latest/download/tinymist-linux-x64 && chmod +x ~/.cargo/bin/tinymist
curl -L -o ~/.cargo/bin/tinymist \
https://github.com/Myriad-Dreamin/tinymist/releases/latest/download/tinymist-linux-x64 \
&& chmod +x ~/.cargo/bin/tinymist
```
3. Set the required environment variables (copy from `docker-compose.yml` or export them).
4. Navigate to `server/` and run:
```bash
cargo run
```
3. Start the local database: `docker-compose up -d db`
4. Navigate to the `server/` directory.
5. Build and run: `cargo run`
Note: The frontend expects the backend to be running on port 3000. During local development via Vite, API calls are proxied automatically.
## Roadmap
- [ ] Add folder-level sharing and permissions
- [ ] Add Project Spaces (Projects have multiple files and typst.toml)
- [ ] Add Importing Typst Templates from Typst
- [ ] Improve mobile-responsive editing experience
<system-reminder>
Your operational mode has changed from plan to build.
You are no longer in read-only mode.
You are permitted to make file changes, run shell commands, and utilize your arsenal of tools as needed.
</system-reminder>
The frontend dev server proxies API calls to `localhost:3000` automatically.
## Screenshots
+23 -3
View File
@@ -4,12 +4,31 @@ services:
ports:
- "3000:3000"
environment:
# --- Required ---
# SQLite (default, single-volume, no extra container):
- DATABASE_URL=sqlite:///data/typstdrive.db?mode=rwc
- DB_TYPE=sqlite # set to "postgres" with a postgres DATABASE_URL to use PostgreSQL
- DB_TYPE=sqlite
# PostgreSQL alternative (uncomment and set DB_TYPE=postgres):
# - DATABASE_URL=postgres://postgres:password@db:5432/typstdrive
# - DB_TYPE=postgres
# --- Optional ---
# Server listen port (default: 3000, also update the ports mapping above)
# - PORT=3000
# Path to compiled frontend assets (default: /app/build)
# - STATIC_DIR=/app/build
# 64+ byte secret for signing session cookies.
# Without this, sessions are cleared on every container restart.
# Generate one with: openssl rand -hex 64
# - COOKIE_SECRET=your-64-plus-byte-secret-here
# Log verbosity (default: server=debug,tower_http=debug)
# - RUST_LOG=info
volumes:
- appdata:/data
# Uncomment to use PostgreSQL instead of SQLite
# Uncomment the db service below when using PostgreSQL (DB_TYPE=postgres).
# Also switch the volume mount in the app service from appdata:/data
# to a separate volume, since PostgreSQL manages its own data directory.
# db:
# image: postgres:16-alpine
# environment:
@@ -19,7 +38,8 @@ services:
# ports:
# - "5433:5432"
# volumes:
# - appdata:/var/lib/postgresql/data
# - pgdata:/var/lib/postgresql/data
volumes:
appdata:
# pgdata: # Uncomment when using PostgreSQL
+19 -3
View File
@@ -55,7 +55,21 @@ async fn main() {
let db = db::init_db().await;
let key = Key::generate();
let key = match std::env::var("COOKIE_SECRET") {
Ok(secret) => {
let bytes = secret.as_bytes();
if bytes.len() < 64 {
tracing::warn!("COOKIE_SECRET is shorter than 64 bytes; sessions will not persist across restarts");
Key::generate()
} else {
Key::from(bytes)
}
}
Err(_) => {
tracing::warn!("COOKIE_SECRET not set; generating a random key. Sessions will be invalidated on restart.");
Key::generate()
}
};
let state = AppState {
compiler: Arc::new(Mutex::new(TypstCompiler::new())),
@@ -102,10 +116,12 @@ async fn main() {
.fallback_service(ServeDir::new(&static_dir).fallback(ServeFile::new(format!("{}/index.html", static_dir))))
.with_state(state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string());
let addr = format!("0.0.0.0:{}", port);
let listener = tokio::net::TcpListener::bind(&addr)
.await
.unwrap();
tracing::info!("Server listening on http://0.0.0.0:3000");
tracing::info!("Server listening on http://{}", addr);
axum::serve(listener, app).await.unwrap();
}