Resources
The ResourceRepository manages modality resources (whiteboards, worksheets, lesson plans, assessments) in PostgreSQL.
Models
Section titled “Models”ResourceRow
Section titled “ResourceRow”Full resource with snapshot bytes (used for single-resource fetches):
pub struct ResourceRow { pub id: String, pub name: String, pub description: Option<String>, pub modality: String, pub snapshot: Option<Vec<u8>>, pub folder_id: Option<String>, pub cover_image_url: Option<String>, pub is_public: bool, pub is_replicable: bool, pub forked_from: Option<String>, pub created_by: String, pub created_at: DateTime<Utc>, pub updated_at: DateTime<Utc>, pub last_viewed_at: Option<DateTime<Utc>>,}ResourceSummaryRow
Section titled “ResourceSummaryRow”Without snapshot bytes (used for listings):
pub struct ResourceSummaryRow { // Same fields as ResourceRow minus `snapshot`}CreateResource
Section titled “CreateResource”pub struct CreateResource { pub name: String, pub description: Option<String>, pub modality: String, pub snapshot: Option<Vec<u8>>, pub folder_id: Option<String>,}Repository Methods
Section titled “Repository Methods”| Method | Purpose |
|---|---|
get(id) | Fetch single resource with snapshot |
list(ids) | Fetch multiple resources |
create(input) | Insert new resource |
create_with_id(id, input) | Insert with specific ID |
push_update(id, snapshot) | Merge snapshot bytes (Loro document merge) |
fork(id, user_id) | Fork resource (copy with forked_from reference) |
forks_of(id) | List all forks of a resource |
duplicate_many(ids, user_id) | Batch duplicate with new IDs |
delete(id) | Delete single resource |
delete_many(ids) | Batch delete |
update_last_viewed(id) | Touch last-viewed timestamp |
list_connection(params) | Cursor-based paginated listing |
Snapshot Merging
Section titled “Snapshot Merging”push_update doesn’t replace the snapshot — it merges the incoming bytes with the existing Loro document:
pub async fn push_update(&self, id: &str, snapshot: &[u8]) -> Result<()> { let existing = self.get(id).await?; let doc = LoroDoc::new(); if let Some(existing_bytes) = existing.snapshot { doc.import(&existing_bytes)?; } doc.import(snapshot)?; let merged = doc.export_snapshot(); // UPDATE resources SET snapshot = $1 WHERE id = $2}This ensures concurrent edits from multiple clients are merged via Loro CRDT, not overwritten.
Keyset Pagination
Section titled “Keyset Pagination”list_connection uses keyset pagination for stable cursor-based results:
- Decode cursor →
(sort_field_value, id) - Apply filter
WHERE (sort_field, id) > (cursor_value, cursor_id) - Fetch N+1 rows to determine
has_next_page - Return N rows + page info
Pagination Types
Section titled “Pagination Types”pub struct ResourceConnectionParams { pub first: Option<i32>, pub after: Option<String>, pub last: Option<i32>, pub before: Option<String>, pub sort: ResourceSort, pub direction: PaginationDirection, pub filter: Option<ResourceFilterInput>,}
pub enum ResourceSort { CreatedAt, UpdatedAt, Name, LastViewed, Modality,}
pub struct ResourceConnectionResult { pub rows: Vec<ResourceSummaryRow>, pub has_next_page: bool, pub has_previous_page: bool, pub total_count: i64,}Migrations
Section titled “Migrations”| Migration | Purpose |
|---|---|
001 | Create resources table |
003 | Add folder_id to resources |
004 | Add cover_image_url, last_viewed_at |
006 | Add is_public, is_replicable, forked_from |