docs: migrate legacy docs to official site
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# Database Transactions
|
||||
|
||||
OxiCloud uses explicit PostgreSQL transactions for multi-step operations that must either commit together or fail together.
|
||||
|
||||
## ACID Guarantees
|
||||
|
||||
- Atomicity: all work succeeds or the entire transaction rolls back
|
||||
- Consistency: constraints and invariants remain valid before and after commit
|
||||
- Isolation: concurrent work behaves predictably
|
||||
- Durability: committed writes survive process and system failures
|
||||
|
||||
## Transaction Helper
|
||||
|
||||
The PostgreSQL repositories use a helper like `with_transaction` to standardize the transaction lifecycle:
|
||||
|
||||
```rust
|
||||
pub async fn with_transaction<F, T, E>(
|
||||
pool: &Arc<PgPool>,
|
||||
operation_name: &str,
|
||||
operation: F,
|
||||
) -> Result<T, E>
|
||||
where
|
||||
F: for<'c> FnOnce(&'c mut Transaction<'_, Postgres>) -> futures::future::BoxFuture<'c, Result<T, E>>,
|
||||
E: From<SqlxError> + std::fmt::Display,
|
||||
{ /* ... */ }
|
||||
```
|
||||
|
||||
That wrapper handles begin, commit, rollback, and lifecycle logging so repository code can focus on the actual domain operation.
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### User management
|
||||
|
||||
Transactions keep related user changes together, such as creating a user and attaching the dependent records required for a valid account.
|
||||
|
||||
### Session management
|
||||
|
||||
Session creation and session revocation can update multiple tables in a single logical step, which avoids stale or mismatched security state.
|
||||
|
||||
### File and folder workflows
|
||||
|
||||
Moves, renames, trash operations, and other multi-step metadata changes rely on transactions so the tree stays consistent.
|
||||
|
||||
## Isolation Levels
|
||||
|
||||
OxiCloud can use different isolation levels depending on the operation.
|
||||
|
||||
| Level | Use case |
|
||||
| --- | --- |
|
||||
| `Read Committed` | Default application work |
|
||||
| `Repeatable Read` | Stable reads during a longer unit of work |
|
||||
| `Serializable` | Highest safety for conflict-prone critical operations |
|
||||
|
||||
Higher isolation can introduce retries or contention, so it should be reserved for the few flows that need it.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Keep transactions short
|
||||
- Avoid heavy I/O inside a transaction when possible
|
||||
- Group only operations that must commit together
|
||||
- Choose the lowest isolation level that preserves correctness
|
||||
- Log and surface rollback causes clearly
|
||||
|
||||
## Why It Matters
|
||||
|
||||
- Prevents partial metadata updates
|
||||
- Keeps concurrent user activity predictable
|
||||
- Reduces race conditions in critical operations
|
||||
- Makes failures recoverable and easier to reason about
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Storage Safety](/architecture/file-system-safety)
|
||||
- [Internal Architecture](/architecture/)
|
||||
@@ -0,0 +1,77 @@
|
||||
# Storage Safety
|
||||
|
||||
OxiCloud protects file integrity with two layers working together:
|
||||
|
||||
- PostgreSQL transactions for metadata
|
||||
- Atomic blob writes for content
|
||||
|
||||
The result is a simple guarantee: operations either complete fully or fail cleanly enough to recover without corrupting user data.
|
||||
|
||||
## Storage Model
|
||||
|
||||
- Metadata such as names, folders, MIME types, quotas, and trash state lives in PostgreSQL
|
||||
- File content is stored as content-addressed blobs under the storage backend
|
||||
- Deduplication metadata is tracked separately so multiple files can reference the same content safely
|
||||
|
||||
## Metadata Safety
|
||||
|
||||
PostgreSQL protects metadata with ACID transactions.
|
||||
|
||||
- Single-row writes are atomic by default
|
||||
- Multi-step operations use explicit transactions
|
||||
- Foreign keys prevent orphaned references
|
||||
- Unique constraints prevent illegal duplicates in the same scope
|
||||
- Trash uses soft-delete semantics until permanent deletion is requested
|
||||
|
||||
## Content Safety
|
||||
|
||||
Blob writes rely on an atomic write pattern:
|
||||
|
||||
1. Write new content to a temporary file
|
||||
2. `fsync` the file to push data and metadata to durable storage
|
||||
3. Rename the temp file into its final content-addressed path
|
||||
4. Sync parent directory metadata when needed
|
||||
|
||||
This prevents partially written blobs from appearing as valid stored content.
|
||||
|
||||
## Deduplication Notes
|
||||
|
||||
OxiCloud's deduplication pipeline uses BLAKE3 hashing and chunk manifest tracking. The storage layer can therefore reuse identical content while still keeping metadata changes transactional.
|
||||
|
||||
If content is stored successfully but the later metadata transaction fails, the content may remain as an unreferenced blob. That is a space leak, not a consistency leak, and can be cleaned up later.
|
||||
|
||||
## Upload Flow
|
||||
|
||||
```text
|
||||
1. Receive content and spool it safely to storage
|
||||
2. Finalize the content-addressed blob write
|
||||
3. Begin metadata transaction
|
||||
4. Insert or update file metadata in PostgreSQL
|
||||
5. Commit
|
||||
```
|
||||
|
||||
If step 1 or 2 fails, no metadata is committed. If step 4 or 5 fails, metadata rolls back and the storage layer can clean up unreferenced content later.
|
||||
|
||||
## Delete Flow
|
||||
|
||||
```text
|
||||
1. Begin metadata transaction
|
||||
2. Remove or soft-delete the metadata row
|
||||
3. Commit
|
||||
4. Decrement blob references and remove physical content when the refcount reaches zero
|
||||
```
|
||||
|
||||
If the metadata transaction fails, the physical file is not considered deleted. If the refcount cleanup fails, the system may keep extra content on disk, but user-visible metadata remains correct.
|
||||
|
||||
## Why This Matters
|
||||
|
||||
- Crash resilience during uploads and deletes
|
||||
- Safe recovery after power loss or host restarts
|
||||
- Clean separation between metadata correctness and background storage cleanup
|
||||
- Predictable behavior for trash, deduplication, and shared storage backends
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [Internal Architecture](/architecture/)
|
||||
- [Database Transactions](/architecture/database-transactions)
|
||||
- [Storage Quotas](/architecture/storage-quotas)
|
||||
@@ -37,6 +37,17 @@ All cross-layer dependencies point **inward** via trait-based ports. The DI cont
|
||||
8. **ZIP service** (last, depends on file & folder services)
|
||||
9. **Assemble `AppState`**
|
||||
|
||||
## AppState Shape
|
||||
|
||||
The assembled `AppState` groups the application into a few stable buckets:
|
||||
|
||||
- `core` for cross-cutting runtime services such as path resolution, caching, chunked uploads, deduplication, compression, thumbnails, and ZIP handling
|
||||
- `repositories` for PostgreSQL-backed folder, file, trash, and i18n persistence
|
||||
- `applications` for the use-case layer exposed to handlers
|
||||
- optional auth, admin, trash, share, favorites, recent, storage usage, calendar, and contact services when those features are enabled
|
||||
|
||||
This lets handlers depend on stable interfaces while the concrete implementation details stay inside the DI container.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# Share Integration
|
||||
|
||||
OxiCloud supports public file and folder sharing through signed share links. A share can be public, password-protected, time-limited, or scoped by permissions.
|
||||
|
||||
## What a Share Contains
|
||||
|
||||
A share record tracks:
|
||||
|
||||
- The shared item ID and whether it is a file or folder
|
||||
- A public token used in the share URL
|
||||
- Optional password protection
|
||||
- Optional expiration timestamp
|
||||
- Permissions for read, write, and reshare
|
||||
- The creator and access count
|
||||
|
||||
## Public and Private Routes
|
||||
|
||||
### Authenticated management routes
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/shares/` | Create a new share |
|
||||
| `GET` | `/api/shares/` | List current user's shares |
|
||||
| `GET` | `/api/shares/{id}` | Fetch one share |
|
||||
| `PUT` | `/api/shares/{id}` | Update permissions, password, or expiration |
|
||||
| `DELETE` | `/api/shares/{id}` | Delete a share |
|
||||
|
||||
### Public access routes
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/s/{token}` | Access a shared item |
|
||||
| `POST` | `/api/s/{token}/verify` | Verify a password-protected share |
|
||||
|
||||
## Service Responsibilities
|
||||
|
||||
The share service is responsible for:
|
||||
|
||||
- Validating that the underlying file or folder exists
|
||||
- Generating unique share IDs and public tokens
|
||||
- Enforcing password checks and expiration rules
|
||||
- Mapping domain permissions into API DTOs
|
||||
- Recording access counts
|
||||
|
||||
Share metadata is persisted separately from the file content itself. The shared resource still uses the normal storage model for files and folders.
|
||||
|
||||
## Example Workflow
|
||||
|
||||
### Creating a share link
|
||||
|
||||
1. A user selects a file or folder in the UI
|
||||
2. The frontend submits a request to `/api/shares/`
|
||||
3. OxiCloud validates the target and requested permissions
|
||||
4. The backend generates a token and public URL
|
||||
5. The share metadata is saved and returned to the caller
|
||||
|
||||
### Opening a share link
|
||||
|
||||
1. A guest opens `/api/s/{token}`
|
||||
2. OxiCloud verifies the token and checks expiration
|
||||
3. If the share is password protected, the client verifies the password first
|
||||
4. Access is counted and the shared resource is returned according to the granted permissions
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Passwords are stored as hashes, never as plaintext
|
||||
- Expired shares are rejected before content access
|
||||
- Permissions are checked per action, not only when the share is created
|
||||
|
||||
## Related Pages
|
||||
|
||||
- [OIDC / SSO](/config/oidc)
|
||||
- [Admin Settings](/config/admin-settings)
|
||||
- [Internal Architecture](/architecture/)
|
||||
@@ -15,6 +15,10 @@ OXICLOUD_ENABLE_USER_STORAGE_QUOTAS=true
|
||||
3. If the upload would exceed the quota, it's rejected with a `413 Payload Too Large` error
|
||||
4. Admins can view and set quotas via the admin panel or API
|
||||
|
||||
## Usage Calculation
|
||||
|
||||
The storage usage service recalculates logical usage from the user's home folder tree and sums file sizes recursively. Directory entries are skipped and the final value is written back to `auth.users.storage_used`.
|
||||
|
||||
## API
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
@@ -26,6 +30,14 @@ OXICLOUD_ENABLE_USER_STORAGE_QUOTAS=true
|
||||
|
||||
The admin panel (`/admin.html`) shows each user's current usage vs. quota with a visual progress bar.
|
||||
|
||||
The dashboard also exposes aggregate quota stats such as:
|
||||
|
||||
- total quota bytes
|
||||
- total used bytes
|
||||
- overall storage usage percent
|
||||
- users above 80% usage
|
||||
- users over quota
|
||||
|
||||
## Deduplication Interaction
|
||||
|
||||
Storage usage is calculated based on **logical file size** (what the user uploaded), not physical blob size. This means:
|
||||
|
||||
Reference in New Issue
Block a user