mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
chore: reserialize db on equal version, update bindings and docs
- Run de/ser roundtrip in pre_init even when db version matches, ensuring all #[serde(default)] fields are populated before any typed access - Add patchdb.md documentation for TypedDbWatch patterns - Update TS bindings for CheckPortParams, CheckPortRes, ifconfigUrl - Update CLAUDE.md docs with patchdb and component-level references
This commit is contained in:
@@ -26,7 +26,7 @@ make test-core # Run Rust tests
|
||||
## Operating Rules
|
||||
|
||||
- Always verify cross-layer changes using the order described in [ARCHITECTURE.md](ARCHITECTURE.md#cross-layer-verification)
|
||||
- Check component-level CLAUDE.md files for component-specific conventions
|
||||
- Check component-level CLAUDE.md files for component-specific conventions. ALWAYS read it before operating on that component.
|
||||
- Follow existing patterns before inventing new ones
|
||||
|
||||
## Supplementary Documentation
|
||||
|
||||
@@ -53,6 +53,8 @@ Patch-DB provides diff-based state synchronization. Changes to `db/model/public.
|
||||
- `.mutate(|v| ...)` — Deserialize, mutate, reserialize
|
||||
- For maps: `.keys()`, `.as_idx(&key)`, `.as_idx_mut(&key)`, `.insert()`, `.remove()`, `.contains_key()`
|
||||
|
||||
See [patchdb.md](patchdb.md) for `TypedDbWatch<T>` construction, API, and usage patterns.
|
||||
|
||||
## i18n
|
||||
|
||||
See [i18n-patterns.md](i18n-patterns.md) for internationalization key conventions and the `t!()` macro.
|
||||
@@ -64,6 +66,7 @@ See [core-rust-patterns.md](core-rust-patterns.md) for common utilities (Invoke
|
||||
## Related Documentation
|
||||
|
||||
- [rpc-toolkit.md](rpc-toolkit.md) — JSON-RPC handler patterns
|
||||
- [patchdb.md](patchdb.md) — Patch-DB watch patterns and TypedDbWatch
|
||||
- [i18n-patterns.md](i18n-patterns.md) — Internationalization conventions
|
||||
- [core-rust-patterns.md](core-rust-patterns.md) — Common Rust utilities
|
||||
- [s9pk-structure.md](s9pk-structure.md) — S9PK package format
|
||||
|
||||
@@ -23,3 +23,4 @@ cd sdk && make baseDist dist # Rebuild SDK after ts-bindings
|
||||
- When adding RPC endpoints, follow the patterns in [rpc-toolkit.md](rpc-toolkit.md)
|
||||
- When modifying `#[ts(export)]` types, regenerate bindings and rebuild the SDK (see [ARCHITECTURE.md](../ARCHITECTURE.md#build-pipeline))
|
||||
- When adding i18n keys, add all 5 locales in `core/locales/i18n.yaml` (see [i18n-patterns.md](i18n-patterns.md))
|
||||
- When using DB watches, follow the `TypedDbWatch<T>` patterns in [patchdb.md](patchdb.md)
|
||||
|
||||
105
core/patchdb.md
Normal file
105
core/patchdb.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Patch-DB Patterns
|
||||
|
||||
## Model<T> and HasModel
|
||||
|
||||
Types stored in the database derive `HasModel`, which generates typed accessor methods on `Model<T>`:
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct ServerInfo {
|
||||
pub version: Version,
|
||||
pub network: NetworkInfo,
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Generated accessors** (one per field):
|
||||
- `as_version()` — `&Model<Version>`
|
||||
- `as_version_mut()` — `&mut Model<Version>`
|
||||
- `into_version()` — `Model<Version>`
|
||||
|
||||
**`Model<T>` APIs:**
|
||||
- `.de()` — Deserialize to `T`
|
||||
- `.ser(&value)` — Serialize from `T`
|
||||
- `.mutate(|v| ...)` — Deserialize, mutate, reserialize
|
||||
- For maps: `.keys()`, `.as_idx(&key)`, `.insert()`, `.remove()`, `.contains_key()`
|
||||
|
||||
## Database Access
|
||||
|
||||
```rust
|
||||
// Read-only snapshot
|
||||
let snap = db.peek().await;
|
||||
let version = snap.as_public().as_server_info().as_version().de()?;
|
||||
|
||||
// Atomic mutation
|
||||
db.mutate(|db| {
|
||||
db.as_public_mut().as_server_info_mut().as_version_mut().ser(&new_version)?;
|
||||
Ok(())
|
||||
}).await;
|
||||
```
|
||||
|
||||
## TypedDbWatch<T>
|
||||
|
||||
Watch a JSON pointer path for changes and deserialize as a typed value. Requires `T: HasModel`.
|
||||
|
||||
### Construction
|
||||
|
||||
```rust
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
|
||||
let ptr: JsonPointer = "/public/serverInfo".parse().unwrap();
|
||||
let mut watch = db.watch(ptr).await.typed::<ServerInfo>();
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
- `watch.peek()?.de()?` — Get current value as `T`
|
||||
- `watch.changed().await?` — Wait until the watched path changes
|
||||
- `watch.peek()?.as_field().de()?` — Access nested fields via `HasModel` accessors
|
||||
|
||||
### Usage Patterns
|
||||
|
||||
**Wait for a condition, then proceed:**
|
||||
|
||||
```rust
|
||||
// Wait for DB version to match current OS version
|
||||
let current = Current::default().semver();
|
||||
let mut watch = db
|
||||
.watch("/public/serverInfo".parse().unwrap())
|
||||
.await
|
||||
.typed::<ServerInfo>();
|
||||
loop {
|
||||
let server_info = watch.peek()?.de()?;
|
||||
if server_info.version == current {
|
||||
break;
|
||||
}
|
||||
watch.changed().await?;
|
||||
}
|
||||
```
|
||||
|
||||
**React to changes in a loop:**
|
||||
|
||||
```rust
|
||||
// From net_controller.rs — react to host changes
|
||||
let mut watch = db
|
||||
.watch("/public/serverInfo/network/host".parse().unwrap())
|
||||
.await
|
||||
.typed::<Host>();
|
||||
loop {
|
||||
if let Err(e) = watch.changed().await {
|
||||
tracing::error!("DB watch disconnected: {e}");
|
||||
break;
|
||||
}
|
||||
let host = watch.peek()?.de()?;
|
||||
// ... process host ...
|
||||
}
|
||||
```
|
||||
|
||||
### Real Examples
|
||||
|
||||
- `net_controller.rs:469` — Watch `Hosts` for package network changes
|
||||
- `net_controller.rs:493` — Watch `Host` for main UI network changes
|
||||
- `service_actor.rs:37` — Watch `StatusInfo` for service state transitions
|
||||
- `gateway.rs:1212` — Wait for DB migrations to complete before syncing
|
||||
@@ -90,7 +90,13 @@ impl Current {
|
||||
.await
|
||||
.result?;
|
||||
}
|
||||
Ordering::Equal => (),
|
||||
Ordering::Equal => {
|
||||
db.apply_function(|db| {
|
||||
Ok::<_, Error>((to_value(&from_value::<Database>(db.clone())?)?, ()))
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
4
sdk/base/lib/osBindings/CheckPortParams.ts
Normal file
4
sdk/base/lib/osBindings/CheckPortParams.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { GatewayId } from './GatewayId'
|
||||
|
||||
export type CheckPortParams = { port: number; gateway: GatewayId }
|
||||
3
sdk/base/lib/osBindings/CheckPortRes.ts
Normal file
3
sdk/base/lib/osBindings/CheckPortRes.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type CheckPortRes = { ip: string; port: number; reachable: boolean }
|
||||
@@ -25,6 +25,7 @@ export type ServerInfo = {
|
||||
zram: boolean
|
||||
governor: Governor | null
|
||||
smtp: SmtpValue | null
|
||||
ifconfigUrl: string
|
||||
ram: number
|
||||
devices: Array<LshwDevice>
|
||||
kiosk: boolean | null
|
||||
|
||||
@@ -56,6 +56,8 @@ export { Category } from './Category'
|
||||
export { Celsius } from './Celsius'
|
||||
export { CheckDependenciesParam } from './CheckDependenciesParam'
|
||||
export { CheckDependenciesResult } from './CheckDependenciesResult'
|
||||
export { CheckPortParams } from './CheckPortParams'
|
||||
export { CheckPortRes } from './CheckPortRes'
|
||||
export { CifsAddParams } from './CifsAddParams'
|
||||
export { CifsBackupTarget } from './CifsBackupTarget'
|
||||
export { CifsRemoveParams } from './CifsRemoveParams'
|
||||
|
||||
@@ -205,6 +205,7 @@ export const mockPatchData: DataModel = {
|
||||
caFingerprint: '63:2B:11:99:44:40:17:DF:37:FC:C3:DF:0F:3D:15',
|
||||
ntpSynced: false,
|
||||
smtp: null,
|
||||
ifconfigUrl: 'https://ifconfig.co',
|
||||
platform: 'x86_64-nonfree',
|
||||
zram: true,
|
||||
governor: 'performance',
|
||||
|
||||
Reference in New Issue
Block a user