docs: migrate legacy docs to official site
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# Batch Operations
|
||||
|
||||
OxiCloud exposes batch endpoints for bulk file and folder operations under `/api/batch`. Batch requests reduce round-trips, run concurrently behind a semaphore, and return per-item success and failure details instead of aborting on the first error.
|
||||
|
||||
## What You Can Do
|
||||
|
||||
### File operations
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/batch/files/move` | Move multiple files into a target folder |
|
||||
| `POST` | `/api/batch/files/copy` | Copy multiple files into a target folder |
|
||||
| `POST` | `/api/batch/files/delete` | Delete multiple files |
|
||||
| `POST` | `/api/batch/files/get` | Fetch metadata for multiple files |
|
||||
|
||||
### Folder operations
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/batch/folders/delete` | Delete multiple folders |
|
||||
| `POST` | `/api/batch/folders/create` | Create multiple folders |
|
||||
| `POST` | `/api/batch/folders/get` | Fetch metadata for multiple folders |
|
||||
| `POST` | `/api/batch/folders/move` | Move multiple folders |
|
||||
|
||||
### Additional batch endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/batch/trash` | Trash multiple items in one request |
|
||||
| `POST` | `/api/batch/download` | Build a batch download |
|
||||
|
||||
## Request Shapes
|
||||
|
||||
### File move or copy
|
||||
|
||||
```json
|
||||
{
|
||||
"file_ids": ["id-1", "id-2", "id-3"],
|
||||
"target_folder_id": "folder-abc"
|
||||
}
|
||||
```
|
||||
|
||||
### Folder delete
|
||||
|
||||
```json
|
||||
{
|
||||
"folder_ids": ["folder-1", "folder-2"],
|
||||
"recursive": true
|
||||
}
|
||||
```
|
||||
|
||||
### Folder create
|
||||
|
||||
```json
|
||||
{
|
||||
"folders": [
|
||||
{ "name": "Documents", "parent_id": null },
|
||||
{ "name": "Photos", "parent_id": "folder-abc" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
All batch endpoints return the same envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"successful": [
|
||||
{ "id": "id-1" }
|
||||
],
|
||||
"failed": [
|
||||
{ "id": "bad-id", "error": "File not found" }
|
||||
],
|
||||
"stats": {
|
||||
"total": 5,
|
||||
"successful": 4,
|
||||
"failed": 1,
|
||||
"execution_time_ms": 245
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Status codes
|
||||
|
||||
| Code | Meaning |
|
||||
| --- | --- |
|
||||
| `200 OK` or `201 Created` | Every operation succeeded |
|
||||
| `206 Partial Content` | Some operations succeeded and some failed |
|
||||
| `400 Bad Request` | Every operation failed |
|
||||
|
||||
## Concurrency Model
|
||||
|
||||
Batch work is coordinated by `BatchOperationService` and a `tokio::sync::Semaphore`. By default, OxiCloud caps concurrent work with `max_concurrent_files = 10` so large batches do not starve the rest of the application.
|
||||
|
||||
Individual failures are collected in the `failed` array. One bad item does not cancel the whole request unless the batch cannot start at all.
|
||||
|
||||
## Example
|
||||
|
||||
```bash
|
||||
# Move three files into a folder
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"file_ids":["id-1","id-2","id-3"],"target_folder_id":"folder-abc"}' \
|
||||
"https://oxicloud.example.com/api/batch/files/move"
|
||||
|
||||
# Delete multiple folders recursively
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"folder_ids":["old-1","old-2"],"recursive":true}' \
|
||||
"https://oxicloud.example.com/api/batch/folders/delete"
|
||||
```
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Search](/guide/search)
|
||||
- [Trash & Recycle Bin](/guide/trash)
|
||||
- [ZIP and Compression](/guide/zip-and-compression)
|
||||
@@ -16,6 +16,33 @@ https://your-server:8086/caldav/
|
||||
- RFC 5545 (iCalendar format)
|
||||
- DAV capabilities: `1, 2, calendar-access`
|
||||
|
||||
### Route Structure
|
||||
|
||||
CalDAV is mounted at the top level, not under `/api`:
|
||||
|
||||
- `/caldav`
|
||||
- `/caldav/`
|
||||
- `/caldav/{*path}`
|
||||
|
||||
OxiCloud also exposes `/.well-known/caldav` and redirects it to `/caldav/`.
|
||||
|
||||
Typical resource shapes:
|
||||
|
||||
- `/caldav/` for the calendar home
|
||||
- `/caldav/{calendar_id}/` for one calendar
|
||||
- `/caldav/{calendar_id}/{ical_uid}.ics` for one event
|
||||
|
||||
### Supported Methods
|
||||
|
||||
- `OPTIONS`
|
||||
- `PROPFIND`
|
||||
- `REPORT`
|
||||
- `MKCALENDAR`
|
||||
- `PUT`
|
||||
- `GET`
|
||||
- `DELETE`
|
||||
- `PROPPATCH`
|
||||
|
||||
### Client Setup
|
||||
|
||||
| Client | URL |
|
||||
@@ -48,6 +75,31 @@ https://your-server:8086/carddav/
|
||||
- RFC 6352 (CardDAV)
|
||||
- RFC 6350 (vCard 4.0)
|
||||
|
||||
### Route Structure
|
||||
|
||||
CardDAV is also mounted at the top level:
|
||||
|
||||
- `/carddav`
|
||||
- `/carddav/`
|
||||
- `/carddav/{*path}`
|
||||
|
||||
Typical resource shapes:
|
||||
|
||||
- `/carddav/` for the address book home
|
||||
- `/carddav/{addressBookId}/` for one address book
|
||||
- `/carddav/{addressBookId}/{contactId}.vcf` for one contact
|
||||
|
||||
### Supported Methods
|
||||
|
||||
- `OPTIONS`
|
||||
- `PROPFIND`
|
||||
- `REPORT`
|
||||
- `MKCOL`
|
||||
- `PUT`
|
||||
- `GET`
|
||||
- `DELETE`
|
||||
- `PROPPATCH`
|
||||
|
||||
### Client Setup
|
||||
|
||||
| Client | URL |
|
||||
@@ -68,3 +120,7 @@ https://your-server:8086/carddav/
|
||||
::: info
|
||||
DAVx⁵ file sync works. CalDAV/CardDAV support on DAVx⁵ is still being refined.
|
||||
:::
|
||||
|
||||
## Client Setup
|
||||
|
||||
For platform-specific instructions, see [DAV Client Setup](/guide/dav-client-setup).
|
||||
|
||||
@@ -1,63 +1,107 @@
|
||||
# Chunked Uploads
|
||||
|
||||
OxiCloud supports TUS-like chunked uploads for large files. Uploads are parallel, resumable, and have MD5 integrity checks.
|
||||
OxiCloud exposes resumable chunked uploads under `/api/uploads`. The protocol is TUS-like in spirit, but the concrete API is OxiCloud-specific: create a session, stream chunks with `PATCH`, inspect progress with `HEAD`, then finalize the assembled file.
|
||||
|
||||
## How It Works
|
||||
## Upload Flow
|
||||
|
||||
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
|
||||
1. Create an upload session with `POST /api/uploads`
|
||||
2. Upload each chunk with `PATCH /api/uploads/{upload_id}?chunk_index=N`
|
||||
3. Optionally inspect progress with `HEAD /api/uploads/{upload_id}`
|
||||
4. Finalize with `POST /api/uploads/{upload_id}/complete`
|
||||
5. Cancel an in-flight upload with `DELETE /api/uploads/{upload_id}` if needed
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Initialize Upload
|
||||
### Create upload session
|
||||
|
||||
```http
|
||||
POST /api/files/upload/init
|
||||
POST /api/uploads
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"file_name": "large-video.mp4",
|
||||
"filename": "large-video.mp4",
|
||||
"folder_id": "folder-uuid",
|
||||
"content_type": "video/mp4",
|
||||
"total_size": 524288000,
|
||||
"chunk_size": 8388608,
|
||||
"total_chunks": 63
|
||||
"chunk_size": 8388608
|
||||
}
|
||||
```
|
||||
|
||||
### 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
|
||||
Typical response:
|
||||
|
||||
```json
|
||||
{
|
||||
"upload_id": "uuid"
|
||||
"upload_id": "uuid",
|
||||
"chunk_size": 8388608,
|
||||
"total_chunks": 63,
|
||||
"expires_at": 86400
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
### Upload a chunk
|
||||
|
||||
| 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 |
|
||||
Chunks are sent as raw bytes, not multipart form uploads.
|
||||
|
||||
## Frontend Behaviour
|
||||
```http
|
||||
PATCH /api/uploads/{upload_id}?chunk_index=0&checksum=md5-hex
|
||||
Content-Type: application/octet-stream
|
||||
Content-MD5: md5-hex
|
||||
|
||||
The OxiCloud web UI automatically selects chunked upload for large files. A progress bar shows overall completion and current chunk status.
|
||||
<binary chunk bytes>
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `chunk_index` is required and zero-based
|
||||
- `checksum` is optional and can also be supplied with the `Content-MD5` header
|
||||
- Successful responses include progress headers such as `Upload-Offset`, `Upload-Progress`, and `Upload-Complete`
|
||||
|
||||
### Inspect upload status
|
||||
|
||||
```http
|
||||
HEAD /api/uploads/{upload_id}
|
||||
```
|
||||
|
||||
The response includes upload metadata in headers such as:
|
||||
|
||||
- `Upload-Offset`
|
||||
- `Upload-Length`
|
||||
- `Upload-Progress`
|
||||
- `Upload-Chunks-Total`
|
||||
- `Upload-Chunks-Complete`
|
||||
|
||||
### Finalize upload
|
||||
|
||||
```http
|
||||
POST /api/uploads/{upload_id}/complete
|
||||
```
|
||||
|
||||
Successful responses return the created file metadata:
|
||||
|
||||
```json
|
||||
{
|
||||
"file_id": "uuid",
|
||||
"filename": "large-video.mp4",
|
||||
"size": 524288000,
|
||||
"path": "/Videos/large-video.mp4"
|
||||
}
|
||||
```
|
||||
|
||||
### Cancel upload
|
||||
|
||||
```http
|
||||
DELETE /api/uploads/{upload_id}
|
||||
```
|
||||
|
||||
This removes the in-progress session and temporary chunk data.
|
||||
|
||||
## Validation Rules
|
||||
|
||||
- `filename` is required
|
||||
- `total_size` must be greater than zero
|
||||
- `chunk_size` must be at least 1 MB when provided
|
||||
- Storage quota checks can reject the session before upload starts
|
||||
|
||||
## Frontend Behavior
|
||||
|
||||
The OxiCloud web UI can switch to chunked uploads for larger files, track aggregate progress, and retry individual chunks without restarting the full transfer.
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# DAV Client Setup
|
||||
|
||||
This page collects platform-specific connection steps for OxiCloud's WebDAV, CalDAV, and CardDAV endpoints.
|
||||
|
||||
## Connection Summary
|
||||
|
||||
| Use case | URL |
|
||||
| --- | --- |
|
||||
| WebDAV file access | `https://your-oxicloud-server/webdav/` |
|
||||
| CalDAV calendar sync | `https://your-oxicloud-server/caldav` |
|
||||
| CardDAV contact sync | `https://your-oxicloud-server/carddav` |
|
||||
|
||||
## WebDAV
|
||||
|
||||
### Windows Explorer
|
||||
|
||||
1. Open File Explorer
|
||||
2. Right-click This PC and choose Add a network location or Map network drive
|
||||
3. Enter `https://your-oxicloud-server/webdav/`
|
||||
4. Provide your OxiCloud username and password
|
||||
|
||||
If Windows refuses the connection, check the `WebClient` service and verify these registry values under `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters`:
|
||||
|
||||
- `BasicAuthLevel = 2` when Basic auth is required
|
||||
- `FileSizeLimitInBytes` if you need to allow larger transfers
|
||||
|
||||
### macOS Finder
|
||||
|
||||
1. Open Finder
|
||||
2. Choose Go -> Connect to Server or press Cmd+K
|
||||
3. Enter `https://your-oxicloud-server/webdav/`
|
||||
4. Sign in with your OxiCloud credentials
|
||||
|
||||
### Linux
|
||||
|
||||
- GNOME Files: use `davs://your-oxicloud-server/webdav/`
|
||||
- KDE Dolphin: use `webdavs://your-oxicloud-server/webdav/`
|
||||
- `davfs2`: mount `https://your-oxicloud-server/webdav/` to a local directory
|
||||
|
||||
## CalDAV
|
||||
|
||||
### Apple Calendar
|
||||
|
||||
Use an advanced CalDAV account and point it at `https://your-oxicloud-server/caldav`.
|
||||
|
||||
### Thunderbird
|
||||
|
||||
Create a network calendar and use a CalDAV location such as:
|
||||
|
||||
```text
|
||||
https://your-oxicloud-server/caldav/calendars/your-calendar-id
|
||||
```
|
||||
|
||||
### Android with DAVx5
|
||||
|
||||
Use Login with URL and username, then point the base URL at `https://your-oxicloud-server/caldav`.
|
||||
|
||||
### Outlook on Windows
|
||||
|
||||
Use a CalDAV plugin such as CalDAV Synchronizer and register the calendar endpoint explicitly.
|
||||
|
||||
## CardDAV
|
||||
|
||||
### Apple Contacts
|
||||
|
||||
Create a CardDAV account using `https://your-oxicloud-server/carddav`.
|
||||
|
||||
### Thunderbird
|
||||
|
||||
Use a remote address book with a URL such as:
|
||||
|
||||
```text
|
||||
https://your-oxicloud-server/carddav/address-books/your-address-book-id
|
||||
```
|
||||
|
||||
### Android with DAVx5
|
||||
|
||||
Use the CardDAV base URL `https://your-oxicloud-server/carddav`.
|
||||
|
||||
### Outlook on Windows
|
||||
|
||||
Use a CardDAV-capable synchronizer and configure the remote address book endpoint explicitly.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### WebDAV
|
||||
|
||||
- Make sure the URL includes `/webdav/`
|
||||
- Use HTTPS in production
|
||||
- Recheck credentials and the WebClient service on Windows
|
||||
|
||||
### CalDAV and CardDAV
|
||||
|
||||
- Use the full `/caldav` or `/carddav` base path
|
||||
- Verify the calendar or address book identifier when the client asks for one
|
||||
- If sync works on one client and not another, compare the exact URLs being used
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [WebDAV](/guide/webdav)
|
||||
- [CalDAV & CardDAV](/guide/caldav-carddav)
|
||||
+50
-31
@@ -1,46 +1,65 @@
|
||||
# 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.
|
||||
OxiCloud uses **content-defined chunking (CDC)** with **FastCDC** and **BLAKE3** to deduplicate files at the sub-file level. Instead of storing only whole-file blobs, OxiCloud can split a file into variable-size chunks, reuse unchanged chunks across versions, and track the ordered chunk list in PostgreSQL.
|
||||
|
||||
## 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
|
||||
1. OxiCloud analyzes the uploaded file with FastCDC
|
||||
2. The file is split into variable-size chunks from **64 KB** to **1 MB**, targeting an average of **256 KB**
|
||||
3. Each chunk is hashed with **BLAKE3** and checked against the blob index
|
||||
4. Only new chunks are written to the blob backend
|
||||
5. A manifest in PostgreSQL maps the whole-file hash to the ordered chunk hash list
|
||||
6. Reference counts are updated so identical chunks are stored once even across multiple files or edited versions
|
||||
|
||||
## Automatic Cleanup
|
||||
## Storage Model
|
||||
|
||||
```text
|
||||
storage.files -> metadata rows that reference content
|
||||
chunk_manifests -> file_hash -> [chunk_hashes] + chunk_sizes + ref_count
|
||||
storage.blobs -> per-chunk blob metadata and reference counts
|
||||
blob backend -> actual chunk bytes on disk or remote storage
|
||||
```
|
||||
|
||||
The manifest table is created in `migrations/20260414000000_chunk_manifests.sql` and keeps:
|
||||
|
||||
- `file_hash`
|
||||
- ordered `chunk_hashes`
|
||||
- `chunk_sizes`
|
||||
- `total_size`
|
||||
- `chunk_count`
|
||||
- `ref_count`
|
||||
|
||||
## Why CDC Matters
|
||||
|
||||
Whole-file dedup only helps when two files are byte-for-byte identical. CDC helps when files are similar but not identical, for example:
|
||||
|
||||
- edited office documents
|
||||
- versioned project archives
|
||||
- large media files with partial changes
|
||||
|
||||
In those cases, unchanged chunks can be reused and only the modified portions need new storage.
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
Older uploads stored before CDC are still readable. When OxiCloud does not find a matching manifest row, it falls back to legacy whole-file blob reads.
|
||||
|
||||
## Cleanup Behavior
|
||||
|
||||
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
|
||||
1. OxiCloud decrements the manifest reference count
|
||||
2. If the last manifest reference disappears, chunk refcounts are decremented
|
||||
3. Chunks with `ref_count = 0` are removed from the blob index and then deleted from the backend
|
||||
|
||||
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.
|
||||
This keeps storage correct even when multiple files share the same chunk set.
|
||||
|
||||
## 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
|
||||
- Better storage savings for edited and versioned files
|
||||
- Faster repeat uploads when many chunks already exist
|
||||
- BLAKE3 hashing for fast content verification
|
||||
- PostgreSQL-backed manifests for durable indexing and cleanup
|
||||
|
||||
## Limitations
|
||||
## Related Endpoints
|
||||
|
||||
- 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)
|
||||
The dedup subsystem is also exposed through helper endpoints under `/api/dedup` for hash checks, deduplicated uploads, statistics, and maintenance operations.
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Favorites and Recent Items
|
||||
|
||||
OxiCloud includes two per-user tracking features backed by PostgreSQL:
|
||||
|
||||
- Favorites for pinning files and folders you want to reach quickly
|
||||
- Recent items for tracking the files and folders you accessed most recently
|
||||
|
||||
Both features are enabled when the instance has a database connection.
|
||||
|
||||
## Favorites
|
||||
|
||||
### API
|
||||
|
||||
All routes live under `/api/favorites` and require authentication.
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/favorites/` | List all favorites for the current user |
|
||||
| `POST` | `/api/favorites/{item_type}/{item_id}` | Add a file or folder to favorites |
|
||||
| `DELETE` | `/api/favorites/{item_type}/{item_id}` | Remove a favorite |
|
||||
|
||||
`item_type` must be either `file` or `folder`.
|
||||
|
||||
### Behavior
|
||||
|
||||
- Adding the same item twice is idempotent
|
||||
- Results are ordered by `created_at DESC`
|
||||
- User identity comes from the JWT, not the request body
|
||||
|
||||
### Storage model
|
||||
|
||||
Favorites are stored in `auth.user_favorites` with a uniqueness constraint on `(user_id, item_id, item_type)`.
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS auth.user_favorites (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
item_id TEXT NOT NULL,
|
||||
item_type TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(user_id, item_id, item_type)
|
||||
);
|
||||
```
|
||||
|
||||
## Recent Items
|
||||
|
||||
### API
|
||||
|
||||
All routes live under `/api/recent` and require authentication.
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/recent/` | List recent items, optionally with `?limit=N` |
|
||||
| `POST` | `/api/recent/{item_type}/{item_id}` | Record an access |
|
||||
| `DELETE` | `/api/recent/{item_type}/{item_id}` | Remove one item from history |
|
||||
| `DELETE` | `/api/recent/clear` | Clear all recent items |
|
||||
|
||||
### Behavior
|
||||
|
||||
- Default maximum per user: 50 items
|
||||
- Re-accessing an item updates its `accessed_at` timestamp
|
||||
- Old items are automatically pruned after inserts
|
||||
- Results are ordered by `accessed_at DESC`
|
||||
|
||||
### Storage model
|
||||
|
||||
Recent items are stored in `auth.user_recent_files`.
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS auth.user_recent_files (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
item_id TEXT NOT NULL,
|
||||
item_type TEXT NOT NULL,
|
||||
accessed_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(user_id, item_id, item_type)
|
||||
);
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```bash
|
||||
# Add a file to favorites
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
||||
"https://oxicloud.example.com/api/favorites/file/abc-123"
|
||||
|
||||
# List recent items
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://oxicloud.example.com/api/recent/?limit=10"
|
||||
|
||||
# Clear recent history
|
||||
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
|
||||
"https://oxicloud.example.com/api/recent/clear"
|
||||
```
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Search](/guide/search)
|
||||
- [Trash & Recycle Bin](/guide/trash)
|
||||
- [Batch Operations](/guide/batch-operations)
|
||||
@@ -0,0 +1,65 @@
|
||||
# Internationalization
|
||||
|
||||
OxiCloud exposes a public translation API backed by JSON locale files on disk. Locales are loaded lazily, cached in memory, and served without authentication.
|
||||
|
||||
## Public API
|
||||
|
||||
All routes live under `/api/i18n`.
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/i18n/locales` | List available locales |
|
||||
| `GET` | `/api/i18n/translate?key=...&locale=...` | Resolve a single key |
|
||||
| `GET` | `/api/i18n/locales/{locale_code}` | Fetch all translations for one locale |
|
||||
|
||||
## Locale Files
|
||||
|
||||
Translations are stored as nested JSON files under `static/locales/`.
|
||||
|
||||
Example shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"app": {
|
||||
"title": "OxiCloud"
|
||||
},
|
||||
"nav": {
|
||||
"files": "Files",
|
||||
"trash": "Trash"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Keys are resolved with dot notation, so `nav.files` maps to `Files`.
|
||||
|
||||
## Fallback Rules
|
||||
|
||||
- If a key is missing in the requested locale, OxiCloud falls back to English
|
||||
- If the key is missing there as well, the API returns a not-found error for that key
|
||||
|
||||
## Caching Model
|
||||
|
||||
Translations are cached in memory with an `RwLock<HashMap<Locale, serde_json::Value>>` and loaded on first use for each locale.
|
||||
|
||||
## Frontend Usage
|
||||
|
||||
Typical frontend flow:
|
||||
|
||||
1. Detect the preferred locale
|
||||
2. Request `/api/i18n/locales/{code}`
|
||||
3. Apply translated strings to UI elements
|
||||
|
||||
## Example
|
||||
|
||||
```bash
|
||||
# List locales
|
||||
curl "https://oxicloud.example.com/api/i18n/locales"
|
||||
|
||||
# Fetch one translation
|
||||
curl "https://oxicloud.example.com/api/i18n/translate?key=app.title&locale=es"
|
||||
```
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Internal Architecture](/architecture/)
|
||||
- [Environment Variables](/config/env)
|
||||
+27
-16
@@ -3,8 +3,8 @@
|
||||
## Docker (recommended)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/DioCrafts/oxicloud.git
|
||||
cd oxicloud
|
||||
git clone https://github.com/DioCrafts/OxiCloud.git
|
||||
cd OxiCloud
|
||||
cp example.env .env
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -16,14 +16,15 @@ Open **http://localhost:8086**. That's it.
|
||||
```yaml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17.4-alpine
|
||||
image: postgres:18.2-alpine3.23
|
||||
environment:
|
||||
POSTGRES_DB: oxicloud
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- pg_data:/var/lib/postgresql/data
|
||||
- ./db/schema.sql:/docker-entrypoint-initdb.d/schema.sql
|
||||
- pg_data:/var/lib/postgresql/
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
@@ -32,6 +33,9 @@ services:
|
||||
|
||||
oxicloud:
|
||||
image: diocrafts/oxicloud:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "8086:8086"
|
||||
env_file:
|
||||
@@ -52,13 +56,19 @@ volumes:
|
||||
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
|
||||
git clone https://github.com/DioCrafts/OxiCloud.git
|
||||
cd OxiCloud
|
||||
cp example.env .env
|
||||
|
||||
# Edit .env and set OXICLOUD_DB_CONNECTION_STRING for runtime
|
||||
export DATABASE_URL=postgres://user:pass@localhost:5432/oxicloud
|
||||
|
||||
cargo build --release
|
||||
cargo run --release
|
||||
```
|
||||
|
||||
`OXICLOUD_DB_CONNECTION_STRING` is the runtime setting read by OxiCloud. `DATABASE_URL` is only needed for SQLx build-time checks.
|
||||
|
||||
## Kubernetes (Helm)
|
||||
|
||||
```bash
|
||||
@@ -77,14 +87,14 @@ kubectl logs statefulset/oxicloud -n oxicloud
|
||||
|
||||
| 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/` |
|
||||
| Windows Explorer | WebDAV | `https://host/webdav/` |
|
||||
| macOS Finder | WebDAV | `https://host/webdav/` |
|
||||
| Nautilus / Dolphin | WebDAV | `davs://host/webdav/` |
|
||||
| Thunderbird (calendar) | CalDAV | `https://host/caldav/` |
|
||||
| Thunderbird (contacts) | CardDAV | `https://host/carddav/` |
|
||||
| DAVx⁵ (Android) | CalDAV + CardDAV | `https://host/` |
|
||||
| GNOME Calendar | CalDAV | `https://host/caldav/` |
|
||||
| GNOME Contacts | CardDAV | `https://host/carddav/` |
|
||||
| Collabora / OnlyOffice | WOPI | See [WOPI configuration](/config/wopi) |
|
||||
|
||||
## What's Next?
|
||||
@@ -92,3 +102,4 @@ kubectl logs statefulset/oxicloud -n oxicloud
|
||||
- [Environment Variables →](/config/env)
|
||||
- [OIDC / SSO Setup →](/config/oidc)
|
||||
- [WebDAV Guide →](/guide/webdav)
|
||||
- [DAV Client Setup →](/guide/dav-client-setup)
|
||||
|
||||
+50
-27
@@ -1,43 +1,66 @@
|
||||
# Search
|
||||
|
||||
OxiCloud provides full-text search across your files with multiple filter options.
|
||||
OxiCloud provides authenticated file and folder search with simple query parameters, advanced JSON criteria, pagination, recursive traversal, and in-memory result caching.
|
||||
|
||||
## Endpoint
|
||||
## Endpoints
|
||||
|
||||
```http
|
||||
GET /api/search?q=report&type_filter=pdf,docx&recursive=true
|
||||
```
|
||||
| Method | Endpoint | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/search/` | Simple search using query parameters |
|
||||
| `POST` | `/api/search/advanced` | Advanced search with a JSON body |
|
||||
| `GET` | `/api/search/suggest` | Lightweight autocomplete suggestions |
|
||||
| `DELETE` | `/api/search/cache` | Clear the search results cache |
|
||||
|
||||
## Query Parameters
|
||||
All search endpoints require authentication.
|
||||
|
||||
## Simple Search 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 |
|
||||
| --- | --- |
|
||||
| `query` | Text to search in file and folder names |
|
||||
| `type` | Comma-separated file extensions |
|
||||
| `created_after` / `created_before` | Filter by creation time |
|
||||
| `modified_after` / `modified_before` | Filter by modification time |
|
||||
| `min_size` / `max_size` | Filter by file size in bytes |
|
||||
| `folder_id` | Restrict search scope to one folder |
|
||||
| `recursive` | Search subfolders, defaults to `true` |
|
||||
| `limit` | Maximum results, defaults to `100` |
|
||||
| `offset` | Pagination offset |
|
||||
| `sort_by` | `relevance`, `name`, `name_desc`, `date`, `date_desc`, `size`, or `size_desc` |
|
||||
|
||||
## How It Works
|
||||
### Example
|
||||
|
||||
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;
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://oxicloud.example.com/api/search/?query=report&type=pdf,docx&recursive=true&limit=20"
|
||||
```
|
||||
|
||||
## Frontend
|
||||
## Advanced Search
|
||||
|
||||
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.
|
||||
```json
|
||||
{
|
||||
"name_contains": "report",
|
||||
"file_types": ["pdf", "docx"],
|
||||
"min_size": 1024,
|
||||
"folder_id": "folder-uuid",
|
||||
"recursive": true,
|
||||
"limit": 50,
|
||||
"offset": 0
|
||||
}
|
||||
```
|
||||
|
||||
## Suggestions
|
||||
|
||||
Use `/api/search/suggest?query=rep&limit=10` for quick autocomplete-style results. Suggestions can also be scoped to a folder with `folder_id`.
|
||||
|
||||
## Result Caching
|
||||
|
||||
Search results are cached in memory using the search criteria and user ID as the cache key.
|
||||
|
||||
- Cache TTL: 5 minutes
|
||||
- Max entries: 1000
|
||||
- Manual invalidation: `DELETE /api/search/cache`
|
||||
|
||||
## Feature Flag
|
||||
|
||||
Search can be disabled via `OXICLOUD_ENABLE_SEARCH=false`.
|
||||
Search can be disabled with `OXICLOUD_ENABLE_SEARCH=false`.
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# Thumbnails and Transcoding
|
||||
|
||||
OxiCloud optimizes image delivery with two complementary features:
|
||||
|
||||
- WebP thumbnail generation in three sizes
|
||||
- On-the-fly image transcoding for browsers that advertise WebP support
|
||||
|
||||
Both features use a memory cache plus a persistent disk cache and are designed to stay off the request hot path whenever possible.
|
||||
|
||||
## Thumbnails
|
||||
|
||||
### Supported sizes
|
||||
|
||||
| Size | Dimensions | Directory |
|
||||
| --- | --- | --- |
|
||||
| `icon` | 150 x 150 | `.thumbnails/icon/` |
|
||||
| `preview` | 400 x 400 | `.thumbnails/preview/` |
|
||||
| `large` | 800 x 800 | `.thumbnails/large/` |
|
||||
|
||||
### Supported inputs
|
||||
|
||||
`image/jpeg`, `image/jpg`, `image/png`, `image/gif`, and `image/webp`
|
||||
|
||||
All thumbnail outputs are stored as WebP.
|
||||
|
||||
### API
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/files/{id}/thumbnail/{size}` | Fetch a thumbnail |
|
||||
| `POST` | `/api/files/upload` | Upload a file and pre-generate thumbnails for supported images |
|
||||
|
||||
Thumbnail responses include:
|
||||
|
||||
- `Content-Type: image/webp`
|
||||
- `Cache-Control: public, max-age=31536000, immutable`
|
||||
- `ETag: "thumb-{id}-{size}"`
|
||||
|
||||
### Generation flow
|
||||
|
||||
1. Upload succeeds through the file API
|
||||
2. If the MIME type is supported, OxiCloud starts thumbnail generation in a background task
|
||||
3. If a thumbnail is requested before pre-generation completes, the request can generate it lazily
|
||||
4. Future requests are served from memory or disk cache
|
||||
|
||||
## Image Transcoding
|
||||
|
||||
OxiCloud can serve a smaller WebP version of uploaded JPEG, PNG, or GIF files when the client advertises WebP support in the `Accept` header.
|
||||
|
||||
### Rules
|
||||
|
||||
- Files over 5 MB skip transcoding
|
||||
- Existing WebP files are not transcoded again
|
||||
- SVG and BMP are not transcoded
|
||||
- If the WebP output is larger than the original, OxiCloud serves the original file instead
|
||||
|
||||
### Storage layout
|
||||
|
||||
```text
|
||||
<storage_path>/
|
||||
.transcoded/
|
||||
webp/
|
||||
<file_id>.webp
|
||||
```
|
||||
|
||||
### Statistics tracked by the service
|
||||
|
||||
- Cache hits
|
||||
- Disk hits
|
||||
- Successful transcodes
|
||||
- Bytes saved
|
||||
- Transcode errors
|
||||
|
||||
## Caching
|
||||
|
||||
Both thumbnail and transcode services use:
|
||||
|
||||
- An in-memory LRU cache for hot assets
|
||||
- A disk cache for persistent reuse across restarts
|
||||
- Fire-and-forget background writes for cache warmup
|
||||
|
||||
For the broader cache model across metadata and listings, see [Caching Architecture](/architecture/caching).
|
||||
|
||||
## Example
|
||||
|
||||
```text
|
||||
Client: GET /api/files/abc-123/download
|
||||
Accept: image/webp, image/png, */*
|
||||
|
||||
Server: checks cache -> transcodes if needed -> returns the smaller asset
|
||||
```
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Caching Architecture](/architecture/caching)
|
||||
- [ZIP and Compression](/guide/zip-and-compression)
|
||||
+12
-1
@@ -9,12 +9,21 @@ OxiCloud provides a trash system that soft-deletes files and folders, allowing u
|
||||
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
|
||||
|
||||
## Storage Model
|
||||
|
||||
- files and folders keep their original rows in PostgreSQL
|
||||
- deletion into trash only flips soft-delete state and records the original parent location for restore
|
||||
- blob content is not moved when an item enters the trash
|
||||
- the unified `storage.trash_items` view is used to list trashed files and folders together
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/api/trash` | List trashed items |
|
||||
| POST | `/api/trash/restore/{id}` | Restore a trashed item |
|
||||
| DELETE | `/api/trash/files/{id}` | Move a file to the trash |
|
||||
| DELETE | `/api/trash/folders/{id}` | Move a folder to the trash |
|
||||
| POST | `/api/trash/{id}/restore` | Restore a trashed item |
|
||||
| DELETE | `/api/trash/{id}` | Permanently delete |
|
||||
| DELETE | `/api/trash/empty` | Empty the entire trash |
|
||||
|
||||
@@ -25,3 +34,5 @@ Permanent deletion decrements the blob reference count. If no other file points
|
||||
## Feature Flag
|
||||
|
||||
Trash can be disabled via `OXICLOUD_ENABLE_TRASH=false`. When disabled, deletions are permanent.
|
||||
|
||||
Retention is controlled by `OXICLOUD_TRASH_RETENTION_DAYS`.
|
||||
|
||||
@@ -33,6 +33,65 @@ Always use HTTPS in production — Basic auth sends credentials in every request
|
||||
| `DELETE` | Delete a file/folder |
|
||||
| `LOCK` / `UNLOCK` | File locking |
|
||||
|
||||
## Common Operations
|
||||
|
||||
### List a directory
|
||||
|
||||
Use `PROPFIND` with a `Depth` header:
|
||||
|
||||
```http
|
||||
PROPFIND /webdav/projects/ HTTP/1.1
|
||||
Depth: 1
|
||||
Content-Type: application/xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:propfind xmlns:D="DAV:">
|
||||
<D:allprop/>
|
||||
</D:propfind>
|
||||
```
|
||||
|
||||
Successful directory listings return `207 Multi-Status`.
|
||||
|
||||
### Download a file
|
||||
|
||||
```http
|
||||
GET /webdav/projects/document.pdf HTTP/1.1
|
||||
Authorization: Basic base64(username:password)
|
||||
```
|
||||
|
||||
### Upload or replace a file
|
||||
|
||||
```http
|
||||
PUT /webdav/projects/document.pdf HTTP/1.1
|
||||
Content-Type: application/pdf
|
||||
|
||||
<file bytes>
|
||||
```
|
||||
|
||||
### Create a folder
|
||||
|
||||
```http
|
||||
MKCOL /webdav/projects/new-folder HTTP/1.1
|
||||
```
|
||||
|
||||
### Move or copy
|
||||
|
||||
```http
|
||||
MOVE /webdav/old-location.pdf HTTP/1.1
|
||||
Destination: https://your-server/webdav/new-location.pdf
|
||||
```
|
||||
|
||||
```http
|
||||
COPY /webdav/original.pdf HTTP/1.1
|
||||
Destination: https://your-server/webdav/copy.pdf
|
||||
```
|
||||
|
||||
### Delete a resource
|
||||
|
||||
```http
|
||||
DELETE /webdav/projects/document.pdf HTTP/1.1
|
||||
```
|
||||
|
||||
## Client Setup
|
||||
|
||||
### Windows Explorer
|
||||
@@ -78,3 +137,16 @@ 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.
|
||||
|
||||
## Integration Notes
|
||||
|
||||
- the WebDAV handler is only an HTTP adapter; file and folder operations still go through the same application services used by the REST API
|
||||
- HTTP Basic Authentication is supported for DAV clients, while authorization rules remain the same as the rest of OxiCloud
|
||||
- delete operations integrate with trash when the trash feature is enabled
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Always use the `/webdav/` base path
|
||||
- Prefer HTTPS because WebDAV uses Basic Authentication
|
||||
- On Windows, make sure the `WebClient` service is enabled
|
||||
- OxiCloud rejects path traversal segments such as `.` and `..` at the HTTP boundary
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# ZIP and Compression
|
||||
|
||||
OxiCloud ships two compression-related features:
|
||||
|
||||
- ZIP download for folders
|
||||
- Gzip compression for suitable file responses
|
||||
|
||||
## ZIP Download
|
||||
|
||||
### Endpoint
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/folders/{id}/download` | Download a folder as a ZIP archive |
|
||||
|
||||
### How it works
|
||||
|
||||
- The ZIP archive is built in memory
|
||||
- Folder traversal uses an iterative queue rather than recursive async calls
|
||||
- Cycle detection prevents loops while walking nested folders
|
||||
- Entries are written with UNIX mode `0o755`
|
||||
- Compression uses the `Deflated` method from the `zip` crate
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://oxicloud.example.com/api/folders/abc-123/download" \
|
||||
-o my-folder.zip
|
||||
```
|
||||
|
||||
## Gzip Compression
|
||||
|
||||
OxiCloud can compress responses when it is worth doing so.
|
||||
|
||||
### Compression threshold
|
||||
|
||||
Files below 50 KB are skipped.
|
||||
|
||||
### Compression levels
|
||||
|
||||
| Level | Value |
|
||||
| --- | --- |
|
||||
| `None` | `0` |
|
||||
| `Fast` | `1` |
|
||||
| `Default` | `6` |
|
||||
| `Best` | `9` |
|
||||
|
||||
### Skip list
|
||||
|
||||
These types are not gzipped because they are already compressed or because compression provides poor returns:
|
||||
|
||||
- `image/*` except SVG and BMP
|
||||
- `audio/*`
|
||||
- `video/*`
|
||||
- archive formats such as ZIP, gzip, 7z, RAR, bzip2, and XZ
|
||||
|
||||
### Runtime behavior
|
||||
|
||||
- Compression and decompression run in `spawn_blocking`
|
||||
- The implementation uses `flate2`
|
||||
- Optional buffer pooling reduces allocation churn under load
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Batch Operations](/guide/batch-operations)
|
||||
- [Thumbnails and Transcoding](/guide/thumbnails-and-transcoding)
|
||||
Reference in New Issue
Block a user