Skip to content

feat: add saved static and smart camera collections - #501

Open
matteius wants to merge 1 commit into
mainfrom
feat/fleet-collections
Open

feat: add saved static and smart camera collections#501
matteius wants to merge 1 commit into
mainfrom
feat/fleet-collections

Conversation

@matteius

Copy link
Copy Markdown
Contributor

Summary

  • add UUID-backed saved camera collections with static and smart collection types
  • store static camera UUID membership atomically and validate smart collections against selector v1 before persistence
  • evaluate dynamic membership against current locations, normalized tags, configuration, and runtime health
  • add shared/private visibility, authorized effective counts, preview samples, and collection CRUD/member APIs

API

  • GET|POST /api/camera-collections
  • GET|PUT|DELETE /api/camera-collections/{collection_uuid}
  • GET|PUT /api/camera-collections/{collection_uuid}/members
  • POST /api/camera-collections/{collection_uuid}/preview

Authorization and safety

  • collection mutation is administrator-only in this initial phase
  • viewers see shared collections or collections they own
  • existing camera tag RBAC is applied before effective counts, member lists, and preview samples
  • smart selector definitions are redacted from shared-collection responses unless the caller is an administrator or owner
  • switching a collection from static to smart clears obsolete membership in the same transaction
  • static membership replacement validates every camera UUID before changing state

Validation

  • 10 focused collection database/API tests cover CRUD, case-insensitive conflicts, membership replacement and deduplication, foreign-key cleanup, type switching, invalid selectors, dynamic tag membership, preview, admin enforcement, private visibility, RBAC-filtered counts, and selector redaction
  • release regression: 11/11 suites passed across streams, locations, tags, selectors, fleet query, collections, auth, and system API
  • ASan/UBSan: 4/4 selector, fleet query, collection database, and collection API suites passed

Stack

@matteius
matteius force-pushed the feat/fleet-collections branch from 620cbe9 to bd7eff2 Compare August 22, 2026 16:06
@matteius
matteius force-pushed the feat/fleet-collections branch from bd7eff2 to c1bf468 Compare August 22, 2026 23:15
@matteius
matteius force-pushed the feat/fleet-collections branch from c1bf468 to 084ac25 Compare August 22, 2026 23:17
@matteius
matteius force-pushed the feat/fleet-collections branch from 084ac25 to 8599cae Compare August 22, 2026 23:24
@matteius
matteius force-pushed the feat/fleet-collections branch from 8599cae to b51e9d8 Compare August 22, 2026 23:36
@matteius
matteius force-pushed the feat/fleet-collections branch 2 times, most recently from 0066654 to 824dc54 Compare August 23, 2026 00:18
Base automatically changed from feat/fleet-selector-query to main August 23, 2026 00:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds persistent static and smart camera collections with CRUD, membership, previews, visibility controls, RBAC filtering, and selector evaluation.

Changes:

  • Adds collection schema, persistence, validation, and atomic membership updates.
  • Adds collection APIs, authorization, previews, and route registration.
  • Reuses fleet health enrichment and adds documentation and tests.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Summary Final review notes
tests/unit/test_db_camera_collections.c Database persistence tests
tests/unit/test_api_handlers_camera_collections.c API, visibility, RBAC, and preview tests
tests/unit/CMakeLists.txt Registers collection tests
src/web/libuv_api_handlers.c Registers collection routes
src/web/api_handlers_fleet.c Reuses fleet health enrichment
src/web/api_handlers_camera_collections.c Implements collection APIs and authorization Moderate (2 votes): filter or omit raw membership counts; validate UUID shape; handle private creation when authentication is disabled.
src/database/db_fleet_query.c Provides runtime health enrichment
src/database/db_camera_collections.c Implements persistence and membership transactions Critical (1 vote): roll back after a failed COMMIT.
include/web/api_handlers_camera_collections.h Declares collection handlers
include/database/db_fleet_query.h Declares health enrichment
include/database/db_embedded_migrations.h Embeds migration 0051
include/database/db_camera_collections.h Declares collection database APIs
docs/API.md Documents collection endpoints and behavior
db/migrations/0051_add_camera_collections.sql Adds collection schema and indexes
Suppressed comments (2)

src/web/api_handlers_camera_collections.c:315

  • When authentication is disabled, httpd_check_viewer_access returns the anonymous pseudo-user as USER_ROLE_VIEWER, even though httpd_check_admin_privileges grants that same request admin access. This condition therefore redacts selectors from unauthenticated smart-collection reads and from the POST/PUT response (the added smart-collection test expects the selector object). Include the auth-disabled mode in this privileged check.
    bool include_selector = user->role == USER_ROLE_ADMIN ||
        (collection->owner_user_id > 0 &&
         collection->owner_user_id == user->id);

src/web/api_handlers_camera_collections.c:157

  • This evaluates selectors against db_fleet_camera_load records, but that loader never populates fleet_camera_t.manufacturer or .model (its SELECT has no such fields and the struct is zero-initialized). Therefore valid v1 vendor and model predicates always see an empty string and smart collections using them silently match zero cameras. Populate those fields in the shared inventory path or explicitly reject/disable those predicates until inventory data exists.
        for (int i = 0; i < camera_count; i++) {
            if (fleet_selector_matches(selector, &cameras[i], NULL)) {
                matched[matched_count++] = &cameras[i];

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +73 to +76
static bool transaction_finish(sqlite3 *db, bool success) {
const char *sql = success ? "COMMIT;" : "ROLLBACK;";
return sqlite3_exec(db, sql, NULL, NULL, NULL) == SQLITE_OK && success;
}
} else {
cJSON_AddNullToObject(object, "owner_user_id");
}
cJSON_AddNumberToObject(object, "member_count", collection->member_count);
Comment on lines +21 to +23
static bool valid_uuid(const char *value) {
return value && strlen(value) == CAMERA_UUID_STRING_SIZE - 1;
}
Comment on lines +37 to +39
return user->role == USER_ROLE_ADMIN || collection->is_shared ||
(collection->owner_user_id > 0 &&
collection->owner_user_id == user->id);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants