chore: checkpoint turso and ai runtime work

This commit is contained in:
Agent Board
2026-07-03 23:20:16 +08:00
parent a75b3d11f9
commit 36d027a4a1
67 changed files with 4224 additions and 914 deletions
@@ -658,7 +658,7 @@ pub(crate) fn ensure_local_workspace_access_with_state(
}
let actor_id = context.auth.actor_id.trim();
let canonical_root_uri = file_uri_for_path(&canonical_root);
let sqlite_access = state
let control_plane_access = state
.control_plane()
.resolve_access(actor_id, &canonical_root_uri)
.unwrap_or_else(|error| {
@@ -675,7 +675,7 @@ pub(crate) fn ensure_local_workspace_access_with_state(
grant_ids: Vec::new(),
}
});
if local_access_permission_allows(&sqlite_access.permission, mode) {
if local_access_permission_allows(&control_plane_access.permission, mode) {
return Ok(canonical_root);
}
ensure_local_workspace_access_for_actor_with_mode(
@@ -785,22 +785,8 @@ pub(crate) fn control_plane_status_display() -> String {
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| "sqlite".to_string());
.unwrap_or_else(|| "libsql-local".to_string());
match backend.as_str() {
"sqlite" => {
let db_path = std::env::var("MNOTE_CONTROL_PLANE_DB_PATH")
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| {
default_local_workspace_base_dir()
.join("control-plane")
.join("control-plane.db")
.display()
.to_string()
});
format!("backend=sqlite; db={db_path}")
}
"libsql-local" | "turso-local" | "turso" => {
let db_path = std::env::var("MNOTE_TURSO_LOCAL_PATH")
.ok()
@@ -850,6 +836,7 @@ pub(crate) fn control_plane_status_display() -> String {
});
format!("backend=turso-synced; local={db_path}; remote=env:MNOTE_TURSO_DATABASE_URL")
}
"sqlite" => "backend=sqlite; unsupported-runtime; use control-plane-admin only".to_string(),
other => format!("backend={other}; unsupported"),
}
}
@@ -1211,7 +1198,7 @@ fn append_control_plane_outbox_event(state: &AppState, event_type: &str, payload
let _ = state.stream_delta_tx.send(delta);
}
fn list_sqlite_share_links_for_context(
fn list_control_plane_share_links_for_context(
state: &AppState,
context: &RequestContext,
query: ShareLinkListQuery,
@@ -1227,15 +1214,15 @@ fn list_sqlite_share_links_for_context(
let links = state
.control_plane()
.list_share_links(workspace_id)
.map_err(|error| WebError::internal(format!("SQLite 分享链接读取失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane 分享链接读取失败: {error}")))?;
Ok(json!({
"ok": true,
"controlPlane": "sqlite",
"controlPlane": "control-plane",
"links": links.iter().map(share_link_payload).collect::<Vec<_>>(),
}))
}
fn create_sqlite_share_link_for_context(
fn create_control_plane_share_link_for_context(
state: &AppState,
context: &RequestContext,
request: LocalShareLinkRequest,
@@ -1263,7 +1250,7 @@ fn create_sqlite_share_link_for_context(
created_by: context.auth.actor_id.trim().to_string(),
expires_at: request.expires_at,
})
.map_err(|error| WebError::internal(format!("SQLite 分享链接写入失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane 分享链接写入失败: {error}")))?;
let _ = state.control_plane().append_audit(AppendAuditInput {
actor_user_id: Some(context.auth.actor_id.trim().to_string()),
action: "control.share.created".to_string(),
@@ -1286,13 +1273,13 @@ fn create_sqlite_share_link_for_context(
);
Ok(json!({
"ok": true,
"controlPlane": "sqlite",
"controlPlane": "control-plane",
"token": created.token,
"link": share_link_payload(&created.link),
}))
}
fn revoke_sqlite_share_link_for_context(
fn revoke_control_plane_share_link_for_context(
state: &AppState,
context: &RequestContext,
link_id: &str,
@@ -1312,7 +1299,7 @@ fn revoke_sqlite_share_link_for_context(
control_plane::ControlPlaneError::NotFound(message) => {
WebError::new(StatusCode::NOT_FOUND, "share_link_not_found", message)
}
other => WebError::internal(format!("SQLite 分享链接撤销失败: {other}")),
other => WebError::internal(format!("control-plane 分享链接撤销失败: {other}")),
})?;
let _ = state.control_plane().append_audit(AppendAuditInput {
actor_user_id: Some(context.auth.actor_id.trim().to_string()),
@@ -1330,7 +1317,7 @@ fn revoke_sqlite_share_link_for_context(
);
Ok(json!({
"ok": true,
"controlPlane": "sqlite",
"controlPlane": "control-plane",
"revokedLinkId": link_id,
}))
}
@@ -1879,7 +1866,7 @@ fn is_default_workspace_auto_grant(grant: &DirectoryGrantRecord) -> bool {
&& grant.root_uri.ends_with("/workspaces/my-space")
}
fn sqlite_access_policy_payload(
fn control_plane_access_policy_payload(
state: &AppState,
context: &RequestContext,
) -> Result<Value, WebError> {
@@ -1887,14 +1874,14 @@ fn sqlite_access_policy_payload(
let grants = state
.control_plane()
.list_directory_grants()
.map_err(|error| WebError::internal(format!("SQLite 控制面授权列表读取失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane授权列表读取失败: {error}")))?;
let grant_values = grants
.iter()
.map(control_plane_grant_payload)
.collect::<Vec<_>>();
Ok(json!({
"ok": true,
"controlPlane": "sqlite",
"controlPlane": "control-plane",
"policyPath": local_access_policy_path().display().to_string(),
"policy": {
"admins": policy.admins,
@@ -1907,7 +1894,7 @@ fn sqlite_access_policy_payload(
}))
}
fn sqlite_user_access_policy_payload(
fn control_plane_user_access_policy_payload(
state: &AppState,
context: &RequestContext,
) -> Result<Value, WebError> {
@@ -1922,7 +1909,7 @@ fn sqlite_user_access_policy_payload(
let grants = state
.control_plane()
.list_directory_grants()
.map_err(|error| WebError::internal(format!("SQLite 控制面授权列表读取失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane授权列表读取失败: {error}")))?;
let grant_values = grants
.iter()
.filter(|grant| {
@@ -1933,7 +1920,7 @@ fn sqlite_user_access_policy_payload(
.collect::<Vec<_>>();
Ok(json!({
"ok": true,
"controlPlane": "sqlite",
"controlPlane": "control-plane",
"policy": {
"grants": grant_values,
},
@@ -1956,7 +1943,7 @@ fn validate_local_access_root_for_context(
}))
}
fn add_sqlite_user_access_grant_for_context(
fn add_control_plane_user_access_grant_for_context(
state: &AppState,
context: &RequestContext,
mut request: LocalAccessGrantRequest,
@@ -1973,19 +1960,19 @@ fn add_sqlite_user_access_grant_for_context(
ensure_path_inside_owned_local_workspace(actor_id, &canonical)?;
request.root_uri = file_uri_for_path(&canonical);
request.root_path = canonical.display().to_string();
add_sqlite_local_access_grant_for_context_inner(state, context, request, "user")
add_control_plane_local_access_grant_for_context_inner(state, context, request, "user")
}
fn add_sqlite_local_access_grant_for_context(
fn add_control_plane_local_access_grant_for_context(
state: &AppState,
context: &RequestContext,
request: LocalAccessGrantRequest,
) -> Result<Value, WebError> {
let _policy = require_local_access_policy_admin(context)?;
add_sqlite_local_access_grant_for_context_inner(state, context, request, "admin")
add_control_plane_local_access_grant_for_context_inner(state, context, request, "admin")
}
fn add_sqlite_local_access_grant_for_context_inner(
fn add_control_plane_local_access_grant_for_context_inner(
state: &AppState,
context: &RequestContext,
request: LocalAccessGrantRequest,
@@ -2010,7 +1997,7 @@ fn add_sqlite_local_access_grant_for_context_inner(
root_uri: Some(root_uri.clone()),
include_revoked: false,
})
.map_err(|error| WebError::internal(format!("SQLite 控制面授权查重失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane授权查重失败: {error}")))?;
if duplicates.iter().any(|grant| {
grant.permission.trim() == permission
&& grant.recursive == request.recursive
@@ -2035,7 +2022,7 @@ fn add_sqlite_local_access_grant_for_context_inner(
source: source.to_string(),
created_by: Some(context.auth.actor_id.trim().to_string()),
})
.map_err(|error| WebError::internal(format!("SQLite 控制面授权写入失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane授权写入失败: {error}")))?;
let _ = state.control_plane().append_audit(AppendAuditInput {
actor_user_id: Some(context.auth.actor_id.trim().to_string()),
action: "control.grant.created".to_string(),
@@ -2058,14 +2045,14 @@ fn add_sqlite_local_access_grant_for_context_inner(
let grants = state
.control_plane()
.list_directory_grants()
.map_err(|error| WebError::internal(format!("SQLite 控制面授权列表读取失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane授权列表读取失败: {error}")))?;
let grant_values = grants
.iter()
.map(control_plane_grant_payload)
.collect::<Vec<_>>();
Ok(json!({
"ok": true,
"controlPlane": "sqlite",
"controlPlane": "control-plane",
"policyPath": local_access_policy_path().display().to_string(),
"grant": control_plane_grant_payload(&grant),
"policy": {
@@ -2074,7 +2061,7 @@ fn add_sqlite_local_access_grant_for_context_inner(
}))
}
fn delete_sqlite_user_access_grant_for_context(
fn delete_control_plane_user_access_grant_for_context(
state: &AppState,
context: &RequestContext,
grant_id: &str,
@@ -2102,7 +2089,7 @@ fn delete_sqlite_user_access_grant_for_context(
root_uri: None,
include_revoked: false,
})
.map_err(|error| WebError::internal(format!("SQLite 控制面授权查找失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane授权查找失败: {error}")))?;
let grant = grants.first().ok_or_else(|| {
WebError::new(
StatusCode::NOT_FOUND,
@@ -2134,7 +2121,7 @@ fn delete_sqlite_user_access_grant_for_context(
"local_access_policy_grant_not_found",
message,
),
other => WebError::internal(format!("SQLite 控制面授权撤销失败: {other}")),
other => WebError::internal(format!("control-plane授权撤销失败: {other}")),
})?;
let _ = state.control_plane().append_audit(AppendAuditInput {
actor_user_id: Some(actor_id.to_string()),
@@ -2152,14 +2139,14 @@ fn delete_sqlite_user_access_grant_for_context(
);
Ok(json!({
"ok": true,
"controlPlane": "sqlite",
"controlPlane": "control-plane",
"deletedGrantId": grant_id,
"revokedUserId": revoked_user_id,
"policy": {
"grants": state
.control_plane()
.list_directory_grants()
.map_err(|error| WebError::internal(format!("SQLite 控制面授权列表读取失败: {error}")))?
.map_err(|error| WebError::internal(format!("control-plane授权列表读取失败: {error}")))?
.iter()
.filter(|grant| grant.created_by.as_deref().map(str::trim) == Some(actor_id))
.map(control_plane_grant_payload)
@@ -2168,7 +2155,7 @@ fn delete_sqlite_user_access_grant_for_context(
}))
}
fn delete_sqlite_local_access_grant_for_context(
fn delete_control_plane_local_access_grant_for_context(
state: &AppState,
context: &RequestContext,
grant_id: &str,
@@ -2189,7 +2176,7 @@ fn delete_sqlite_local_access_grant_for_context(
root_uri: None,
include_revoked: false,
})
.map_err(|error| WebError::internal(format!("SQLite 控制面授权查找失败: {error}")))?;
.map_err(|error| WebError::internal(format!("control-plane授权查找失败: {error}")))?;
if grants
.first()
.map(is_default_workspace_auto_grant)
@@ -2211,7 +2198,7 @@ fn delete_sqlite_local_access_grant_for_context(
"local_access_policy_grant_not_found",
message,
),
other => WebError::internal(format!("SQLite 控制面授权撤销失败: {other}")),
other => WebError::internal(format!("control-plane授权撤销失败: {other}")),
})?;
let _ = state.control_plane().append_audit(AppendAuditInput {
actor_user_id: Some(context.auth.actor_id.trim().to_string()),
@@ -2229,7 +2216,7 @@ fn delete_sqlite_local_access_grant_for_context(
);
Ok(json!({
"ok": true,
"controlPlane": "sqlite",
"controlPlane": "control-plane",
"policyPath": local_access_policy_path().display().to_string(),
"deletedGrantId": grant_id,
"revokedUserId": revoked_user_id,
@@ -2237,7 +2224,7 @@ fn delete_sqlite_local_access_grant_for_context(
"grants": state
.control_plane()
.list_directory_grants()
.map_err(|error| WebError::internal(format!("SQLite 控制面授权列表读取失败: {error}")))?
.map_err(|error| WebError::internal(format!("control-plane授权列表读取失败: {error}")))?
.iter()
.map(control_plane_grant_payload)
.collect::<Vec<_>>(),
@@ -2420,7 +2407,7 @@ pub async fn get_local_access_policy(
State(state): State<AppState>,
Extension(context): Extension<RequestContext>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = sqlite_access_policy_payload(&state, &context)
let payload = control_plane_access_policy_payload(&state, &context)
.map_err(|error| error.with_context(&context))?;
Ok((StatusCode::OK, Json(payload)))
}
@@ -2429,7 +2416,7 @@ pub async fn get_user_access_policy(
State(state): State<AppState>,
Extension(context): Extension<RequestContext>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = sqlite_user_access_policy_payload(&state, &context)
let payload = control_plane_user_access_policy_payload(&state, &context)
.map_err(|error| error.with_context(&context))?;
Ok((StatusCode::OK, Json(payload)))
}
@@ -2448,7 +2435,7 @@ pub async fn create_local_access_grant(
Extension(context): Extension<RequestContext>,
Json(request): Json<LocalAccessGrantRequest>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = add_sqlite_local_access_grant_for_context(&state, &context, request)
let payload = add_control_plane_local_access_grant_for_context(&state, &context, request)
.map_err(|error| error.with_context(&context))?;
if let Some(user_id) = payload
.pointer("/grant/userId")
@@ -2466,7 +2453,7 @@ pub async fn create_user_access_grant(
Extension(context): Extension<RequestContext>,
Json(request): Json<LocalAccessGrantRequest>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = add_sqlite_user_access_grant_for_context(&state, &context, request)
let payload = add_control_plane_user_access_grant_for_context(&state, &context, request)
.map_err(|error| error.with_context(&context))?;
if let Some(user_id) = payload
.pointer("/grant/userId")
@@ -2484,7 +2471,7 @@ pub async fn delete_local_access_grant(
Extension(context): Extension<RequestContext>,
AxumPath(grant_id): AxumPath<String>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = delete_sqlite_local_access_grant_for_context(&state, &context, &grant_id)
let payload = delete_control_plane_local_access_grant_for_context(&state, &context, &grant_id)
.map_err(|error| error.with_context(&context))?;
if let Some(user_id) = payload
.get("revokedUserId")
@@ -2502,7 +2489,7 @@ pub async fn delete_user_access_grant(
Extension(context): Extension<RequestContext>,
AxumPath(grant_id): AxumPath<String>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = delete_sqlite_user_access_grant_for_context(&state, &context, &grant_id)
let payload = delete_control_plane_user_access_grant_for_context(&state, &context, &grant_id)
.map_err(|error| error.with_context(&context))?;
if let Some(user_id) = payload
.get("revokedUserId")
@@ -2536,7 +2523,7 @@ pub async fn get_share_links(
Extension(context): Extension<RequestContext>,
Query(query): Query<ShareLinkListQuery>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = list_sqlite_share_links_for_context(&state, &context, query)
let payload = list_control_plane_share_links_for_context(&state, &context, query)
.map_err(|error| error.with_context(&context))?;
Ok((StatusCode::OK, Json(payload)))
}
@@ -2546,7 +2533,7 @@ pub async fn create_share_link(
Extension(context): Extension<RequestContext>,
Json(request): Json<LocalShareLinkRequest>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = create_sqlite_share_link_for_context(&state, &context, request)
let payload = create_control_plane_share_link_for_context(&state, &context, request)
.map_err(|error| error.with_context(&context))?;
Ok((StatusCode::OK, Json(payload)))
}
@@ -2556,7 +2543,7 @@ pub async fn delete_share_link(
Extension(context): Extension<RequestContext>,
AxumPath(link_id): AxumPath<String>,
) -> Result<(StatusCode, Json<Value>), WebError> {
let payload = revoke_sqlite_share_link_for_context(&state, &context, &link_id)
let payload = revoke_control_plane_share_link_for_context(&state, &context, &link_id)
.map_err(|error| error.with_context(&context))?;
Ok((StatusCode::OK, Json(payload)))
}
@@ -9578,7 +9565,7 @@ fn markdown_page_subtree(document_id: &str, title: &str, content: &Value) -> Val
#[cfg(test)]
mod tests {
use super::{
add_sqlite_local_access_grant_for_context,
add_control_plane_local_access_grant_for_context,
create_default_local_workspace_for_actor_at_base, create_local_access_grant,
create_share_grant, create_share_link, create_user_access_grant, create_user_share_grant,
delete_share_link, delete_user_access_grant, editor_blocks_to_markdown_for_file,
@@ -11054,7 +11041,7 @@ fn main() {}
.expect("created grant id")
.to_string();
assert_eq!(created["grant"]["permission"], "read");
assert_eq!(created["controlPlane"], "sqlite");
assert_eq!(created["controlPlane"], "control-plane");
let created_events = state
.control_plane()
.drain_outbox(10)
@@ -11080,7 +11067,7 @@ fn main() {}
.expect_err("created read grant cannot write");
assert_eq!(write_error.status(), StatusCode::FORBIDDEN);
let duplicate_error = add_sqlite_local_access_grant_for_context(
let duplicate_error = add_control_plane_local_access_grant_for_context(
&state,
&request_context("admin_1", "user"),
LocalAccessGrantRequest {
@@ -11262,7 +11249,7 @@ fn main() {}
}
#[tokio::test]
async fn user_access_policy_grant_uses_sqlite_directory_grants_for_owned_folder() {
async fn user_access_policy_grant_uses_control_plane_directory_grants_for_owned_folder() {
let _guard = env_lock().lock().expect("env lock");
let owned_root = temp_root("mnote-user-access-policy-owned-root");
let outside_root = temp_root("mnote-user-access-policy-outside-root");
@@ -11304,7 +11291,7 @@ fn main() {}
.as_str()
.expect("grant id")
.to_string();
assert_eq!(created["controlPlane"], "sqlite");
assert_eq!(created["controlPlane"], "control-plane");
assert_eq!(created["grant"]["userId"], "user_target");
assert_eq!(created["grant"]["createdBy"], "user_owner");
assert_eq!(created["grant"]["permission"], "write");
@@ -11324,7 +11311,7 @@ fn main() {}
)
.await
.expect("owner can list self-created directory grants");
assert_eq!(listed["controlPlane"], "sqlite");
assert_eq!(listed["controlPlane"], "control-plane");
assert_eq!(listed["grants"][0]["id"], grant_id);
assert_eq!(listed["grants"][0]["permission"], "write");
@@ -11334,7 +11321,7 @@ fn main() {}
)
.await
.expect("target can list incoming directory grants");
assert_eq!(target_listed["controlPlane"], "sqlite");
assert_eq!(target_listed["controlPlane"], "control-plane");
assert_eq!(target_listed["grants"][0]["id"], grant_id);
assert_eq!(target_listed["grants"][0]["userId"], "user_target");
@@ -11452,7 +11439,7 @@ fn main() {}
}
#[tokio::test]
async fn share_link_api_creates_lists_and_revokes_sqlite_record() {
async fn share_link_api_creates_lists_and_revokes_control_plane_record() {
let _guard = env_lock().lock().expect("env lock");
let state = test_state();
std::env::set_var("MNOTE_ADMIN_USER_IDS", "admin_1");
@@ -11488,7 +11475,7 @@ fn main() {}
.await
.expect("admin can create share link");
let link_id = created["link"]["id"].as_str().expect("link id").to_string();
assert_eq!(created["controlPlane"], "sqlite");
assert_eq!(created["controlPlane"], "control-plane");
assert_eq!(created["token"], "visible-token");
assert!(created["link"].get("tokenHash").is_none());
assert_eq!(created["link"]["permission"], "read");
@@ -11507,7 +11494,7 @@ fn main() {}
let stored = state
.control_plane()
.list_share_links(&workspace.id)
.expect("list sqlite share links");
.expect("list control-plane share links");
assert_eq!(stored.len(), 1);
assert_eq!(stored[0].id, link_id);
assert_ne!(stored[0].token_hash, "visible-token");
@@ -12691,30 +12678,30 @@ fn main() {}
}
#[tokio::test]
async fn local_file_open_allows_sqlite_directory_read_grant_without_json_policy() {
async fn local_file_open_allows_control_plane_directory_read_grant_without_json_policy() {
let _guard = env_lock().lock().expect("env lock");
let root = temp_root("mnote-local-file-open-sqlite-read-grant-root");
let policy_root = temp_root("mnote-local-file-open-sqlite-read-grant-config");
let root = temp_root("mnote-local-file-open-control-plane-read-grant-root");
let policy_root = temp_root("mnote-local-file-open-control-plane-read-grant-config");
let policy_file = policy_root.join("missing-access-policy.json");
let state = test_state();
let root_uri = format!("file://{}", root.display());
std::fs::write(root.join("README.txt"), "hello sqlite").expect("write file");
std::fs::write(root.join("README.txt"), "hello control plane").expect("write file");
std::env::set_var("MNOTE_LOCAL_ACCESS_POLICY_FILE", &policy_file);
state
.control_plane()
.upsert_user(UpsertUserInput {
id: Some("sqlite_reader".into()),
email: Some("sqlite-reader@example.com".into()),
username: "sqlite_reader".into(),
display_name: "sqlite_reader".into(),
id: Some("control_plane_reader".into()),
email: Some("control-plane-reader@example.com".into()),
username: "control_plane_reader".into(),
display_name: "control_plane_reader".into(),
role: None,
password_hash: None,
})
.expect("upsert sqlite reader");
.expect("upsert control-plane reader");
state
.control_plane()
.grant_directory_access(DirectoryGrantInput {
user_id: "sqlite_reader".into(),
user_id: "control_plane_reader".into(),
workspace_id: None,
root_uri: root_uri.clone(),
root_path: root
@@ -12728,9 +12715,9 @@ fn main() {}
source: "test".into(),
created_by: None,
})
.expect("grant sqlite read");
.expect("grant control-plane read");
let context = request_context("sqlite_reader", "user");
let context = request_context("control_plane_reader", "user");
let (_, _, bytes) = open_local_file(
State(state),
Extension(context),
@@ -12741,8 +12728,8 @@ fn main() {}
}),
)
.await
.expect("sqlite read grant can open local file");
assert_eq!(bytes, b"hello sqlite");
.expect("control-plane read grant can open local file");
assert_eq!(bytes, b"hello control plane");
assert!(!policy_file.exists(), "SQLite grant 不应写旧 JSON policy");
std::env::remove_var("MNOTE_LOCAL_ACCESS_POLICY_FILE");
@@ -12751,32 +12738,32 @@ fn main() {}
}
#[tokio::test]
async fn local_file_open_allows_legacy_sqlite_directory_grant_with_path_root_uri() {
async fn local_file_open_allows_legacy_control_plane_directory_grant_with_path_root_uri() {
let _guard = env_lock().lock().expect("env lock");
let root = temp_root("mnote-local-file-open-legacy-sqlite-grant-root");
let policy_root = temp_root("mnote-local-file-open-legacy-sqlite-grant-config");
let root = temp_root("mnote-local-file-open-legacy-control-plane-grant-root");
let policy_root = temp_root("mnote-local-file-open-legacy-control-plane-grant-config");
let policy_file = policy_root.join("missing-access-policy.json");
let state = test_state();
let canonical_root = root.canonicalize().expect("canonical root");
let canonical_root_path = canonical_root.display().to_string();
let root_uri = format!("file://{canonical_root_path}");
std::fs::write(root.join("README.txt"), "hello legacy sqlite").expect("write file");
std::fs::write(root.join("README.txt"), "hello legacy control plane").expect("write file");
std::env::set_var("MNOTE_LOCAL_ACCESS_POLICY_FILE", &policy_file);
state
.control_plane()
.upsert_user(UpsertUserInput {
id: Some("legacy_sqlite_reader".into()),
email: Some("legacy-sqlite-reader@example.com".into()),
username: "legacy_sqlite_reader".into(),
display_name: "legacy_sqlite_reader".into(),
id: Some("legacy_control_plane_reader".into()),
email: Some("legacy-control-plane-reader@example.com".into()),
username: "legacy_control_plane_reader".into(),
display_name: "legacy_control_plane_reader".into(),
role: None,
password_hash: None,
})
.expect("upsert sqlite reader");
.expect("upsert control-plane reader");
state
.control_plane()
.grant_directory_access(DirectoryGrantInput {
user_id: "legacy_sqlite_reader".into(),
user_id: "legacy_control_plane_reader".into(),
workspace_id: None,
root_uri: canonical_root_path.clone(),
root_path: canonical_root_path,
@@ -12786,9 +12773,9 @@ fn main() {}
source: "legacy-test".into(),
created_by: None,
})
.expect("grant legacy sqlite read");
.expect("grant legacy control-plane read");
let context = request_context("legacy_sqlite_reader", "user");
let context = request_context("legacy_control_plane_reader", "user");
let (_, _, bytes) = open_local_file(
State(state),
Extension(context),
@@ -12799,8 +12786,8 @@ fn main() {}
}),
)
.await
.expect("legacy sqlite read grant can open local file");
assert_eq!(bytes, b"hello legacy sqlite");
.expect("legacy control-plane read grant can open local file");
assert_eq!(bytes, b"hello legacy control plane");
assert!(!policy_file.exists(), "SQLite grant 不应写旧 JSON policy");
std::env::remove_var("MNOTE_LOCAL_ACCESS_POLICY_FILE");
@@ -12809,10 +12796,10 @@ fn main() {}
}
#[tokio::test]
async fn local_resource_write_allows_sqlite_directory_write_grant_without_json_policy() {
async fn local_resource_write_allows_control_plane_directory_write_grant_without_json_policy() {
let _guard = env_lock().lock().expect("env lock");
let root = temp_root("mnote-local-resource-write-sqlite-grant-root");
let policy_root = temp_root("mnote-local-resource-write-sqlite-grant-config");
let root = temp_root("mnote-local-resource-write-control-plane-grant-root");
let policy_root = temp_root("mnote-local-resource-write-control-plane-grant-config");
let policy_file = policy_root.join("missing-access-policy.json");
let state = test_state();
let root_uri = format!("file://{}", root.display());
@@ -12822,18 +12809,18 @@ fn main() {}
state
.control_plane()
.upsert_user(UpsertUserInput {
id: Some("sqlite_writer".into()),
email: Some("sqlite-writer@example.com".into()),
username: "sqlite_writer".into(),
display_name: "sqlite_writer".into(),
id: Some("control_plane_writer".into()),
email: Some("control-plane-writer@example.com".into()),
username: "control_plane_writer".into(),
display_name: "control_plane_writer".into(),
role: None,
password_hash: None,
})
.expect("upsert sqlite writer");
.expect("upsert control-plane writer");
state
.control_plane()
.grant_directory_access(DirectoryGrantInput {
user_id: "sqlite_writer".into(),
user_id: "control_plane_writer".into(),
workspace_id: None,
root_uri: root_uri.clone(),
root_path: root
@@ -12847,9 +12834,9 @@ fn main() {}
source: "test".into(),
created_by: None,
})
.expect("grant sqlite write");
.expect("grant control-plane write");
let context = request_context("sqlite_writer", "user");
let context = request_context("control_plane_writer", "user");
let (_, _, payload) = write_local_resource(
State(state),
Extension(context),
@@ -12868,7 +12855,7 @@ fn main() {}
}),
)
.await
.expect("sqlite write grant can write local resource");
.expect("control-plane write grant can write local resource");
assert_eq!(payload["ok"], true);
let written = std::fs::read_to_string(root.join("README.md")).expect("read file");
assert!(written.contains("new content"));