feat: add VitePress docs site, music file picker modal, Dockerfile optimization, i18n keys for 14 locales
- Add docs/ with VitePress site (19 pages): guide, config, architecture, FAQ - Add GitHub Actions workflow for auto-deploy to GitHub Pages - Replace music 'Add Tracks' upload picker with in-app audio file browser modal - Add music picker CSS styles with dark theme support - Add missing i18n keys (search_audio, no_audio_files, etc.) to all 14 locales - Optimize Dockerfile: shared base stage, COPY --chmod, consolidated RUN, HEALTHCHECK - Improve README: docs links, updated stats (222+ tests, 14 languages), feature status
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# CalDAV & CardDAV
|
||||
|
||||
OxiCloud provides built-in CalDAV (calendar) and CardDAV (contacts) servers — no extra apps or plugins needed.
|
||||
|
||||
## CalDAV (Calendars)
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
https://your-server:8086/caldav/
|
||||
```
|
||||
|
||||
### Protocol Compliance
|
||||
|
||||
- RFC 4791 (Calendar Access)
|
||||
- RFC 5545 (iCalendar format)
|
||||
- DAV capabilities: `1, 2, calendar-access`
|
||||
|
||||
### Client Setup
|
||||
|
||||
| Client | URL |
|
||||
|--------|-----|
|
||||
| Thunderbird | `https://your-server:8086/caldav/` |
|
||||
| GNOME Calendar | `https://your-server:8086/caldav/` |
|
||||
| Apple Calendar (macOS/iOS) | `https://your-server:8086/caldav/` |
|
||||
| DAVx⁵ (Android) | `https://your-server:8086/` (auto-discovery) |
|
||||
|
||||
### Thunderbird Setup
|
||||
|
||||
1. Open Thunderbird → **Calendar** tab
|
||||
2. Right-click → **New Calendar** → **On the Network**
|
||||
3. Format: **CalDAV**
|
||||
4. URL: `https://your-server:8086/caldav/`
|
||||
5. Enter your OxiCloud credentials
|
||||
|
||||
---
|
||||
|
||||
## CardDAV (Contacts)
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
https://your-server:8086/carddav/
|
||||
```
|
||||
|
||||
### Protocol Compliance
|
||||
|
||||
- RFC 6352 (CardDAV)
|
||||
- RFC 6350 (vCard 4.0)
|
||||
|
||||
### Client Setup
|
||||
|
||||
| Client | URL |
|
||||
|--------|-----|
|
||||
| Thunderbird | `https://your-server:8086/carddav/` |
|
||||
| GNOME Contacts | `https://your-server:8086/carddav/` |
|
||||
| Apple Contacts (macOS/iOS) | `https://your-server:8086/carddav/` |
|
||||
| DAVx⁵ (Android) | `https://your-server:8086/` (auto-discovery) |
|
||||
|
||||
### DAVx⁵ (Android) Setup
|
||||
|
||||
1. Install [DAVx⁵](https://www.davx5.com/) from F-Droid or Play Store
|
||||
2. Add account → **Login with URL and user name**
|
||||
3. Base URL: `https://your-server:8086/`
|
||||
4. Enter your OxiCloud credentials
|
||||
5. DAVx⁵ auto-discovers both CalDAV and CardDAV endpoints
|
||||
|
||||
::: info
|
||||
DAVx⁵ file sync works. CalDAV/CardDAV support on DAVx⁵ is still being refined.
|
||||
:::
|
||||
@@ -0,0 +1,63 @@
|
||||
# Chunked Uploads
|
||||
|
||||
OxiCloud supports TUS-like chunked uploads for large files. Uploads are parallel, resumable, and have MD5 integrity checks.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Client sends `POST /api/files/upload/init` with file metadata → receives an `upload_id`
|
||||
2. Client splits the file into chunks and uploads them in parallel via `POST /api/files/upload/chunk`
|
||||
3. Each chunk includes its index, MD5 hash, and the `upload_id`
|
||||
4. When all chunks are uploaded, client calls `POST /api/files/upload/complete`
|
||||
5. Server reassembles the file, verifies integrity, and runs deduplication
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Initialize Upload
|
||||
|
||||
```http
|
||||
POST /api/files/upload/init
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"file_name": "large-video.mp4",
|
||||
"folder_id": "folder-uuid",
|
||||
"total_size": 524288000,
|
||||
"chunk_size": 8388608,
|
||||
"total_chunks": 63
|
||||
}
|
||||
```
|
||||
|
||||
### Upload Chunk
|
||||
|
||||
```http
|
||||
POST /api/files/upload/chunk
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
upload_id: "uuid"
|
||||
chunk_index: 0
|
||||
chunk_hash: "md5-hex"
|
||||
file: <binary>
|
||||
```
|
||||
|
||||
### Complete Upload
|
||||
|
||||
```http
|
||||
POST /api/files/upload/complete
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"upload_id": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|---|---|---|
|
||||
| Max parallel chunks | 8 | Concurrent chunk uploads |
|
||||
| Min size for chunking | 200 MB | Below this, single-shot upload is used |
|
||||
| Chunk size | 8 MB | Default chunk size |
|
||||
|
||||
## Frontend Behaviour
|
||||
|
||||
The OxiCloud web UI automatically selects chunked upload for large files. A progress bar shows overall completion and current chunk status.
|
||||
@@ -0,0 +1,46 @@
|
||||
# File Deduplication
|
||||
|
||||
OxiCloud uses **SHA-256 content-addressable storage** to avoid storing duplicate files. If two users upload the same file, only one copy is stored on disk.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. When a file is uploaded, its SHA-256 hash is computed
|
||||
2. The hash is checked against the blob store (`.blobs/{prefix}/{hash}.blob`)
|
||||
3. If a blob with that hash already exists, the file metadata points to the existing blob (no extra disk usage)
|
||||
4. If not, the content is saved as a new blob
|
||||
5. A reference counter tracks how many files point to each blob
|
||||
|
||||
## Automatic Cleanup
|
||||
|
||||
When a file is permanently deleted:
|
||||
|
||||
1. The blob's reference count is decremented
|
||||
2. If the reference count reaches zero, the blob is removed from disk
|
||||
|
||||
This means disk space is only freed when the **last** reference to a blob is removed.
|
||||
|
||||
## Storage Layout
|
||||
|
||||
```
|
||||
storage/
|
||||
├── .blobs/
|
||||
│ ├── a1/
|
||||
│ │ └── a1b2c3d4...sha256.blob
|
||||
│ ├── f8/
|
||||
│ │ └── f8e7d6c5...sha256.blob
|
||||
│ └── ...
|
||||
```
|
||||
|
||||
The first two hex characters of the hash are used as a directory prefix to avoid having millions of files in a single directory.
|
||||
|
||||
## Benefits
|
||||
|
||||
- **Disk savings** — identical files across users consume storage only once
|
||||
- **Instant uploads** — if the blob already exists, the upload completes immediately
|
||||
- **Integrity** — SHA-256 ensures bit-for-bit correctness
|
||||
|
||||
## Limitations
|
||||
|
||||
- Deduplication is based on exact content match (byte-identical files)
|
||||
- Near-duplicate files (e.g., a JPEG re-saved at slightly different quality) are stored separately
|
||||
- Encryption at rest would require per-user keys, which breaks deduplication (planned as opt-in)
|
||||
@@ -0,0 +1,81 @@
|
||||
# What is OxiCloud?
|
||||
|
||||
OxiCloud is a self-hosted cloud platform written in Rust. It provides file storage, calendar sync (CalDAV), contacts sync (CardDAV), and office document editing (WOPI) — all from a single binary.
|
||||
|
||||
NextCloud was too slow on a home server. So OxiCloud was built to run on minimal hardware and stay out of the way.
|
||||
|
||||
## OxiCloud vs NextCloud
|
||||
|
||||
| Metric | OxiCloud | NextCloud |
|
||||
|--------|----------|-----------|
|
||||
| **Language** | Rust (compiled, zero-cost abstractions) | PHP (interpreted) |
|
||||
| **Docker image** | ~40 MB (Alpine, static binary) | ~1 GB+ (Apache + PHP + modules) |
|
||||
| **Idle RAM** | ~30–50 MB | ~250–512 MB |
|
||||
| **Cold start** | < 1 s | 5–15 s |
|
||||
| **CPU at idle** | ~0 % | 1–5 % (cron, background jobs) |
|
||||
| **Min. hardware** | 1 vCPU / 512 MB RAM | 2 vCPU / 2 GB RAM |
|
||||
| **File dedup** | SHA-256 content-addressable | None |
|
||||
| **Dependencies** | Single binary + PostgreSQL | PHP, Apache/Nginx, Redis, Cron, … |
|
||||
| **WebDAV** | Built-in (RFC 4918) | Built-in |
|
||||
| **CalDAV / CardDAV** | Built-in | Via apps |
|
||||
| **WOPI** | Built-in | Via apps |
|
||||
| **OIDC / SSO** | Built-in | Via apps |
|
||||
|
||||
> NextCloud is a mature, feature-rich ecosystem. OxiCloud targets users who prioritise raw performance, simplicity, and low resource usage over plugin breadth.
|
||||
|
||||
## Key Features
|
||||
|
||||
### Storage & Files
|
||||
- Drag-and-drop upload, multi-file, grid & list views
|
||||
- Chunked uploads (TUS-like, parallel, resumable, MD5 integrity)
|
||||
- SHA-256 content-addressable file deduplication with ref-counting
|
||||
- Adaptive compression (zstd / gzip per MIME type)
|
||||
- Trash bin with soft-delete and auto-purge
|
||||
- Favourites, recent files, full-text search
|
||||
- Inline preview for images, PDF, text, audio & video
|
||||
- On-the-fly thumbnails & transcoding (WebP / AVIF)
|
||||
|
||||
### Protocols
|
||||
- **WebDAV** — RFC 4918, streaming PROPFIND, locking
|
||||
- **CalDAV** — calendar sync (Thunderbird, GNOME Calendar, iOS, DAVx⁵)
|
||||
- **CardDAV** — contacts sync with vCard support
|
||||
- **WOPI** — Collabora Online / OnlyOffice
|
||||
- **REST API** — complete JSON API
|
||||
|
||||
### Security
|
||||
- JWT + Argon2id password hashing
|
||||
- OIDC / SSO (Keycloak, Authentik, Authelia, Google, Azure AD)
|
||||
- Role-based access, per-folder permissions, storage quotas
|
||||
- Shared links with optional password protection
|
||||
|
||||
### Infrastructure
|
||||
- Single binary, ~40 MB Docker image
|
||||
- Dual DB pool (user queries never starved by background tasks)
|
||||
- Write-behind caching (moka) for sub-millisecond reads
|
||||
- LTO-optimised release builds
|
||||
- 222+ automated tests
|
||||
|
||||
## Feature Status
|
||||
|
||||
| Feature | Status |
|
||||
|---------|--------|
|
||||
| File storage & upload | ✅ Working |
|
||||
| WebDAV | ✅ Working |
|
||||
| CalDAV | ✅ Working |
|
||||
| CardDAV | ✅ Working |
|
||||
| WOPI / Office editing | ✅ Working |
|
||||
| OIDC / SSO | ✅ Working |
|
||||
| Trash / recycle bin | ✅ Working |
|
||||
| Full-text search | ✅ Working |
|
||||
| Shared links | ✅ Working |
|
||||
| Music library & playlists | ✅ Working |
|
||||
| Photo gallery | ✅ Working |
|
||||
| Desktop sync client | ❌ Planned |
|
||||
| Android / iOS app | ❌ Planned |
|
||||
| E2E encryption | ❌ Planned |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Quick Start →](/guide/installation)
|
||||
- [Deployment & Docker →](/config/deployment)
|
||||
- [Architecture →](/architecture/)
|
||||
@@ -0,0 +1,94 @@
|
||||
# Quick Start
|
||||
|
||||
## Docker (recommended)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/DioCrafts/oxicloud.git
|
||||
cd oxicloud
|
||||
cp example.env .env
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Open **http://localhost:8086**. That's it.
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```yaml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17.4-alpine
|
||||
environment:
|
||||
POSTGRES_DB: oxicloud
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- pg_data:/var/lib/postgresql/data
|
||||
- ./db/schema.sql:/docker-entrypoint-initdb.d/schema.sql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
oxicloud:
|
||||
image: oxicloud:latest
|
||||
ports:
|
||||
- "8086:8086"
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- storage_data:/app/storage
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
pg_data:
|
||||
storage_data:
|
||||
```
|
||||
|
||||
## From Source
|
||||
|
||||
Requires **Rust 1.93+** and **PostgreSQL 13+**.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/DioCrafts/oxicloud.git
|
||||
cd oxicloud
|
||||
echo "DATABASE_URL=postgres://user:pass@localhost/oxicloud" > .env
|
||||
cargo build --release
|
||||
cargo run --release
|
||||
```
|
||||
|
||||
## Kubernetes (Helm)
|
||||
|
||||
```bash
|
||||
helm upgrade --install oxicloud charts/oxicloud \
|
||||
-f charts/oxicloud/values.yaml
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
kubectl get pods -n oxicloud
|
||||
kubectl logs statefulset/oxicloud -n oxicloud
|
||||
```
|
||||
|
||||
## Client Setup
|
||||
|
||||
| Client | Protocol | URL |
|
||||
|--------|----------|-----|
|
||||
| Windows Explorer | WebDAV | `http://host:8086/webdav/` |
|
||||
| macOS Finder | WebDAV | `http://host:8086/webdav/` |
|
||||
| Nautilus / Dolphin | WebDAV | `dav://host:8086/webdav/` |
|
||||
| Thunderbird (calendar) | CalDAV | `http://host:8086/caldav/` |
|
||||
| Thunderbird (contacts) | CardDAV | `http://host:8086/carddav/` |
|
||||
| DAVx⁵ (Android) | CalDAV + CardDAV | `http://host:8086/` |
|
||||
| GNOME Calendar | CalDAV | `http://host:8086/caldav/` |
|
||||
| GNOME Contacts | CardDAV | `http://host:8086/carddav/` |
|
||||
| Collabora / OnlyOffice | WOPI | See [WOPI configuration](/config/wopi) |
|
||||
|
||||
## What's Next?
|
||||
|
||||
- [Environment Variables →](/config/env)
|
||||
- [OIDC / SSO Setup →](/config/oidc)
|
||||
- [WebDAV Guide →](/guide/webdav)
|
||||
@@ -0,0 +1,43 @@
|
||||
# Search
|
||||
|
||||
OxiCloud provides full-text search across your files with multiple filter options.
|
||||
|
||||
## Endpoint
|
||||
|
||||
```http
|
||||
GET /api/search?q=report&type_filter=pdf,docx&recursive=true
|
||||
```
|
||||
|
||||
## Query Parameters
|
||||
|
||||
| Parameter | Description |
|
||||
|---|---|
|
||||
| `q` | Search query (matches file name) |
|
||||
| `type_filter` | Comma-separated file extensions to filter by |
|
||||
| `folder_id` | Restrict search to a specific folder |
|
||||
| `recursive` | `true` to search subfolders |
|
||||
| `date_from` / `date_to` | Filter by modification date |
|
||||
| `size_min` / `size_max` | Filter by file size (bytes) |
|
||||
| `limit` | Maximum results to return |
|
||||
| `offset` | Pagination offset |
|
||||
|
||||
## How It Works
|
||||
|
||||
OxiCloud stores file metadata in PostgreSQL with an `ltree` path column, enabling efficient recursive subtree queries:
|
||||
|
||||
```sql
|
||||
SELECT * FROM storage.files
|
||||
WHERE path <@ 'root.folder_id'
|
||||
AND LOWER(name) LIKE '%query%'
|
||||
AND LOWER(extension) = ANY('{pdf,docx}')
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 50;
|
||||
```
|
||||
|
||||
## Frontend
|
||||
|
||||
The web UI includes a search bar in the toolbar. Results appear instantly with file name, path, size, and type. Clicking a result navigates to the file's location.
|
||||
|
||||
## Feature Flag
|
||||
|
||||
Search can be disabled via `OXICLOUD_ENABLE_SEARCH=false`.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Trash & Recycle Bin
|
||||
|
||||
OxiCloud provides a trash system that soft-deletes files and folders, allowing users to restore or permanently remove them.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. When a file or folder is deleted, it's **soft-deleted** — a flag (`is_trashed`) is set and a `trashed_at` timestamp is recorded
|
||||
2. Trashed items are hidden from normal file listings but remain on disk and in the database
|
||||
3. Users can browse the trash, restore items, or permanently delete them
|
||||
4. Items older than the retention period (default: **30 days**) are automatically purged
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/api/trash` | List trashed items |
|
||||
| POST | `/api/trash/restore/{id}` | Restore a trashed item |
|
||||
| DELETE | `/api/trash/{id}` | Permanently delete |
|
||||
| DELETE | `/api/trash/empty` | Empty the entire trash |
|
||||
|
||||
## Deduplication Interaction
|
||||
|
||||
Permanent deletion decrements the blob reference count. If no other file points to the same blob, the blob is removed from disk.
|
||||
|
||||
## Feature Flag
|
||||
|
||||
Trash can be disabled via `OXICLOUD_ENABLE_TRASH=false`. When disabled, deletions are permanent.
|
||||
@@ -0,0 +1,80 @@
|
||||
# WebDAV
|
||||
|
||||
OxiCloud exposes a fully RFC 4918 compliant WebDAV interface at `/webdav/`. It works with all major file managers and sync clients.
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
https://your-server:8086/webdav/
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
HTTP Basic Authentication:
|
||||
|
||||
```
|
||||
Authorization: Basic base64(username:password)
|
||||
```
|
||||
|
||||
::: tip
|
||||
Always use HTTPS in production — Basic auth sends credentials in every request.
|
||||
:::
|
||||
|
||||
## Supported Methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `PROPFIND` | List directory contents / get file properties |
|
||||
| `GET` | Download a file |
|
||||
| `PUT` | Upload a file |
|
||||
| `MKCOL` | Create a folder |
|
||||
| `MOVE` | Move or rename a file/folder |
|
||||
| `COPY` | Copy a file/folder |
|
||||
| `DELETE` | Delete a file/folder |
|
||||
| `LOCK` / `UNLOCK` | File locking |
|
||||
|
||||
## Client Setup
|
||||
|
||||
### Windows Explorer
|
||||
|
||||
1. Open **This PC** → **Map network drive**
|
||||
2. Enter: `https://your-server:8086/webdav/`
|
||||
3. Check **Connect using different credentials**
|
||||
4. Enter your OxiCloud username and password
|
||||
|
||||
### macOS Finder
|
||||
|
||||
1. **Go** → **Connect to Server** (⌘K)
|
||||
2. Enter: `https://your-server:8086/webdav/`
|
||||
3. Enter credentials when prompted
|
||||
|
||||
### Linux (Nautilus / Files)
|
||||
|
||||
1. Open Files → **Other Locations**
|
||||
2. In the address bar, type: `davs://your-server:8086/webdav/`
|
||||
3. Enter credentials
|
||||
|
||||
### Linux (Dolphin / KDE)
|
||||
|
||||
1. In the address bar, type: `webdavs://your-server:8086/webdav/`
|
||||
|
||||
### Command Line (curl)
|
||||
|
||||
```bash
|
||||
# List root directory
|
||||
curl -u user:pass -X PROPFIND https://your-server:8086/webdav/ \
|
||||
-H "Depth: 1"
|
||||
|
||||
# Download a file
|
||||
curl -u user:pass https://your-server:8086/webdav/document.pdf -o document.pdf
|
||||
|
||||
# Upload a file
|
||||
curl -u user:pass -T localfile.txt https://your-server:8086/webdav/remotefile.txt
|
||||
|
||||
# Create a folder
|
||||
curl -u user:pass -X MKCOL https://your-server:8086/webdav/new-folder/
|
||||
```
|
||||
|
||||
## Streaming PROPFIND
|
||||
|
||||
OxiCloud streams PROPFIND responses, so listing directories with thousands of files doesn't consume excessive memory.
|
||||
Reference in New Issue
Block a user