Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ jobs:
- sync-anilist
- release-mangaupdates
- release-nyaa
- release-tsundoku
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
Expand Down Expand Up @@ -586,6 +587,7 @@ jobs:
- sync-anilist
- release-mangaupdates
- release-nyaa
- release-tsundoku
steps:
- uses: actions/checkout@v4
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ jobs:
- sync-anilist
- release-mangaupdates
- release-nyaa
- release-tsundoku
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
Expand Down
3 changes: 3 additions & 0 deletions crates/codex-db/src/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ pub mod users;
// OIDC authentication
pub mod oidc_connections;

// System-scoped plugin KV store (per-plugin, no user context)
pub mod plugin_data;

// User plugin system (per-user plugin instances and data storage)
pub mod user_plugin_data;
pub mod user_plugins;
Expand Down
81 changes: 81 additions & 0 deletions crates/codex-db/src/entities/plugin_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Plugin Data entity for system-scoped (per-plugin) key-value storage.
//!
//! Mirrors [`super::user_plugin_data`] but is keyed by `plugin_id` rather than
//! `user_plugin_id`. System plugins (e.g. release sources) run with no user
//! context, so they can't use the per-user store; this is their durable KV
//! bucket — used, for example, to persist a release feed cursor.
//!
//! ## Storage Isolation
//!
//! Each entry is scoped to a specific `plugin_id`. Plugins can only address
//! their own data by key; the host resolves the plugin scope from the
//! connection context.
//!
//! ## TTL Support
//!
//! Entries can optionally have an `expires_at` timestamp for cached data. A
//! background cleanup task removes expired entries periodically.

use chrono::{DateTime, Utc};
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[sea_orm(table_name = "plugin_data")]
pub struct Model {
/// Unique identifier for this data entry
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,

/// Reference to the system plugin (provides plugin scoping)
pub plugin_id: Uuid,

/// Storage key (e.g., "feed_cursor")
pub key: String,

/// Plugin-managed JSON data
pub data: serde_json::Value,

/// Optional TTL — entry is considered expired after this timestamp
pub expires_at: Option<DateTime<Utc>>,

/// When this entry was first created
pub created_at: DateTime<Utc>,

/// When this entry was last updated
pub updated_at: DateTime<Utc>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::plugins::Entity",
from = "Column::PluginId",
to = "super::plugins::Column::Id",
on_delete = "Cascade"
)]
Plugin,
}

impl Related<super::plugins::Entity> for Entity {
fn to() -> RelationDef {
Relation::Plugin.def()
}
}

impl ActiveModelBehavior for ActiveModel {}

// =============================================================================
// Helper Methods
// =============================================================================

impl Model {
/// Check if this entry has expired
pub fn is_expired(&self) -> bool {
match self.expires_at {
Some(expires_at) => Utc::now() >= expires_at,
None => false, // No expiry means never expires
}
}
}
3 changes: 3 additions & 0 deletions crates/codex-db/src/entities/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ pub use super::series_duplicates::Entity as SeriesDuplicates;
pub use super::series_external_ids::Entity as SeriesExternalIds;
pub use super::series_metadata::Entity as SeriesMetadata;

// System-scoped plugin KV store
#[allow(unused_imports)]
pub use super::plugin_data::Entity as PluginData;
// User plugin system
#[allow(unused_imports)]
pub use super::user_plugin_data::Entity as UserPluginData;
Expand Down
7 changes: 7 additions & 0 deletions crates/codex-db/src/repositories/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ pub mod access_group;
// OIDC authentication
pub mod oidc_connection;

// System-scoped plugin KV store (per-plugin, no user context)
pub mod plugin_data;

// User plugin system
pub mod user_plugin_data;
pub mod user_plugins;
Expand Down Expand Up @@ -119,6 +122,10 @@ pub use access_group::AccessGroupRepository;
// OIDC authentication
pub use oidc_connection::OidcConnectionRepository;

// System-scoped plugin KV store
#[allow(unused_imports)]
pub use plugin_data::PluginDataRepository;

// User plugin system
#[allow(unused_imports)]
pub use user_plugin_data::UserPluginDataRepository;
Expand Down
Loading
Loading