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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TABLE documents
ADD COLUMN IF NOT EXISTS created_by_plugin TEXT NULL;

CREATE INDEX IF NOT EXISTS idx_documents_created_by_plugin
ON documents(created_by_plugin);
30 changes: 30 additions & 0 deletions api/migrations/202604200002_backfill_created_by_plugin.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- Backfill created_by_plugin for existing documents using plugin KV/records activity
WITH candidates AS (
SELECT scope_id AS doc_id, plugin, MIN(created_at) AS first_seen
FROM (
SELECT scope_id, plugin, created_at
FROM plugin_kv
WHERE scope = 'doc' AND scope_id IS NOT NULL AND plugin IS NOT NULL AND plugin <> ''
UNION ALL
SELECT scope_id, plugin, created_at
FROM plugin_records
WHERE scope = 'doc' AND plugin IS NOT NULL AND plugin <> ''
) s
GROUP BY scope_id, plugin
),
chosen AS (
SELECT doc_id, plugin
FROM (
SELECT doc_id,
plugin,
first_seen,
ROW_NUMBER() OVER (PARTITION BY doc_id ORDER BY first_seen) AS rn
FROM candidates
) t
WHERE rn = 1
)
UPDATE documents d
SET created_by_plugin = c.plugin
FROM chosen c
WHERE d.id = c.doc_id
AND d.created_by_plugin IS NULL;
2 changes: 1 addition & 1 deletion api/openapi/openapi.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion api/src/application/access/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ where
}
Actor::ShareToken(t) => {
// Resolve token target and then decide access when document matches token scope
if let Ok(Some((share_id, perm, expires_at, shared_id, shared_type))) =
if let Ok(Some((share_id, perm, expires_at, shared_id, shared_type, _workspace_id))) =
shares_repo.resolve_share_by_token(t).await
{
if access_repo
Expand Down
2 changes: 2 additions & 0 deletions api/src/application/ports/document_repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub trait DocumentRepository: Send + Sync {
title: &str,
parent_id: Option<Uuid>,
doc_type: &str,
created_by_plugin: Option<&str>,
) -> anyhow::Result<DomainDocument>;

async fn create_for_user_tx(
Expand All @@ -76,6 +77,7 @@ pub trait DocumentRepository: Send + Sync {
title: &str,
parent_id: Option<Uuid>,
doc_type: &str,
created_by_plugin: Option<&str>,
) -> anyhow::Result<DomainDocument>;

// parent_id: None => not provided; Some(None) => set NULL; Some(Some(uuid)) => set to value
Expand Down
1 change: 1 addition & 0 deletions api/src/application/ports/share_access_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub trait ShareAccessPort: Send + Sync {
Option<chrono::DateTime<chrono::Utc>>,
Uuid,
String,
Uuid,
)>,
>;

Expand Down
3 changes: 2 additions & 1 deletion api/src/application/ports/shares_repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ pub trait SharesRepository: Send + Sync {
Option<chrono::DateTime<chrono::Utc>>,
Uuid,
String,
Uuid,
)>,
>; // (share_id, permission, expires_at, shared_id, shared_type)
>; // (share_id, permission, expires_at, shared_id, shared_type, workspace_id)

async fn list_share_mounts(&self, workspace_id: Uuid) -> anyhow::Result<Vec<ShareMountRow>>;

Expand Down
11 changes: 10 additions & 1 deletion api/src/application/services/documents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ impl DocumentService {
title: &str,
parent_id: Option<Uuid>,
doc_type: &str,
created_by_plugin: Option<&str>,
) -> Result<DomainDocument, ServiceError> {
ensure_can_create(permissions, doc_type)?;
if let Some(parent_id) = parent_id {
Expand All @@ -138,7 +139,15 @@ impl DocumentService {
};
let mut tx = self.begin_transaction().await?;
let doc = match uc
.execute_tx(&mut tx, workspace_id, actor_id, title, parent_id, doc_type)
.execute_tx(
&mut tx,
workspace_id,
actor_id,
title,
parent_id,
doc_type,
created_by_plugin,
)
.await
{
Ok(doc) => doc,
Expand Down
36 changes: 17 additions & 19 deletions api/src/application/services/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use uuid::Uuid;

use crate::application::dto::diff::TextDiffResult;
use crate::application::dto::git::{
GitChangeItem, GitCommitInfo, GitConfigDto, GitRemoteCheckDto, GitStatusDto,
GitSyncRequestDto, GitSyncResponseDto, GitignoreUpdateDto, UpsertGitConfigInput,
GitChangeItem, GitCommitInfo, GitConfigDto, GitRemoteCheckDto, GitStatusDto, GitSyncRequestDto,
GitSyncResponseDto, GitignoreUpdateDto, UpsertGitConfigInput,
};
use crate::application::ports::document_repository::DocumentRepository;
use crate::application::ports::files_repository::FilesRepository;
Expand Down Expand Up @@ -132,23 +132,21 @@ impl GitService {
workspace: self.workspace.as_ref(),
repo: self.repo.as_ref(),
};
uc.execute(workspace_id, payload)
.await
.map_err(|err| {
let msg_lower = err.to_string().to_lowercase();
if msg_lower.contains("git_http_auth_redirect")
|| msg_lower.contains("too many redirects")
|| msg_lower.contains("http (34)")
{
ServiceError::BadRequest("git_auth_redirect")
} else if msg_lower.contains("git_http_not_found")
|| msg_lower.contains("status code: 404")
{
ServiceError::BadRequest("git_repo_not_found")
} else {
ServiceError::from(err)
}
})
uc.execute(workspace_id, payload).await.map_err(|err| {
let msg_lower = err.to_string().to_lowercase();
if msg_lower.contains("git_http_auth_redirect")
|| msg_lower.contains("too many redirects")
|| msg_lower.contains("http (34)")
{
ServiceError::BadRequest("git_auth_redirect")
} else if msg_lower.contains("git_http_not_found")
|| msg_lower.contains("status code: 404")
{
ServiceError::BadRequest("git_repo_not_found")
} else {
ServiceError::from(err)
}
})
}

pub async fn get_changes(
Expand Down
12 changes: 12 additions & 0 deletions api/src/application/services/git_rebuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,18 @@ mod tests {
})
}
}

async fn check_remote(
&self,
_workspace_id: Uuid,
_cfg: &crate::application::ports::git_repository::UserGitCfg,
) -> anyhow::Result<crate::application::dto::git::GitRemoteCheckDto> {
Ok(crate::application::dto::git::GitRemoteCheckDto {
ok: true,
message: "ok".into(),
reason: None,
})
}
}

struct RecordingJobQueue {
Expand Down
6 changes: 3 additions & 3 deletions api/src/application/services/markdown/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ pub fn render(
// Provide data-sourcepos for editor<->preview sync
c_opts.render.sourcepos = true;
// Treat soft line breaks as <br>; default on for "doc" flavor unless explicitly disabled
let hardbreaks = opts
.hardbreaks
.unwrap_or_else(|| matches!(opts.flavor.as_deref(), Some(f) if f.eq_ignore_ascii_case("doc")));
let hardbreaks = opts.hardbreaks.unwrap_or_else(
|| matches!(opts.flavor.as_deref(), Some(f) if f.eq_ignore_ascii_case("doc")),
);
c_opts.render.hardbreaks = hardbreaks;
// Allow HtmlBlock/HtmlInline to pass through; will be sanitized by ammonia afterwards
c_opts.render.unsafe_ = true;
Expand Down
17 changes: 16 additions & 1 deletion api/src/application/services/plugins/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ pub struct PluginExecutionService {
plugin_repo: Arc<dyn PluginRepository>,
document_repo: Arc<dyn DocumentRepository>,
runtime: Arc<dyn PluginRuntime>,
authorization: Arc<crate::application::services::authorization::AuthorizationService>,
}

impl PluginExecutionService {
pub fn new(
plugin_repo: Arc<dyn PluginRepository>,
document_repo: Arc<dyn DocumentRepository>,
runtime: Arc<dyn PluginRuntime>,
authorization: Arc<crate::application::services::authorization::AuthorizationService>,
) -> Self {
Self {
plugin_repo,
document_repo,
runtime,
authorization,
}
}

Expand All @@ -37,13 +40,25 @@ impl PluginExecutionService {
plugin: &str,
action: &str,
payload: Option<serde_json::Value>,
allowed_doc_id: Option<Uuid>,
actor: &crate::application::access::Actor,
) -> Result<Option<ExecResult>, ServiceError> {
let uc = ExecutePluginAction {
runtime: self.runtime.as_ref(),
plugin_repo: self.plugin_repo.as_ref(),
document_repo: self.document_repo.as_ref(),
authorization: self.authorization.as_ref(),
};
uc.execute(workspace_id, user_id, permissions, plugin, action, payload)
uc.execute(
workspace_id,
user_id,
permissions,
plugin,
action,
payload,
allowed_doc_id,
actor,
)
.await
.map_err(ServiceError::from)
}
Expand Down
30 changes: 28 additions & 2 deletions api/src/application/services/shares.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ impl ShareService {
uc.execute(token).await.map_err(ServiceError::from)
}

pub async fn resolve_share_context(
&self,
token: &str,
) -> Result<
Option<(
Uuid,
String,
Option<chrono::DateTime<chrono::Utc>>,
Uuid,
String,
Uuid,
)>,
ServiceError,
> {
self.repo
.resolve_share_by_token(token)
.await
.map_err(ServiceError::from)
}

pub async fn list_active(
&self,
workspace_id: Uuid,
Expand Down Expand Up @@ -168,8 +188,14 @@ impl ShareService {
.await
.map_err(ServiceError::from)?
.ok_or(ServiceError::NotFound)?;
let (_share_id, permission, expires_at, target_document_id, target_document_type) =
resolved;
let (
_share_id,
permission,
expires_at,
target_document_id,
target_document_type,
_workspace_id,
) = resolved;
if let Some(exp) = expires_at {
if exp < chrono::Utc::now() {
return Err(ServiceError::NotFound);
Expand Down
21 changes: 19 additions & 2 deletions api/src/application/use_cases/documents/create_document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,17 @@ impl<'a, R: DocumentRepository + ?Sized> CreateDocument<'a, R> {
title: &str,
parent_id: Option<Uuid>,
doc_type: &str,
created_by_plugin: Option<&str>,
) -> anyhow::Result<DomainDocument> {
self.repo
.create_for_user(workspace_id, created_by, title, parent_id, doc_type)
.create_for_user(
workspace_id,
created_by,
title,
parent_id,
doc_type,
created_by_plugin,
)
.await
}

Expand All @@ -30,9 +38,18 @@ impl<'a, R: DocumentRepository + ?Sized> CreateDocument<'a, R> {
title: &str,
parent_id: Option<Uuid>,
doc_type: &str,
created_by_plugin: Option<&str>,
) -> anyhow::Result<DomainDocument> {
self.repo
.create_for_user_tx(tx, workspace_id, created_by, title, parent_id, doc_type)
.create_for_user_tx(
tx,
workspace_id,
created_by,
title,
parent_id,
doc_type,
created_by_plugin,
)
.await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ where
doc_type: "folder".to_string(),
created_at: Utc::now(),
updated_at: Utc::now(),
created_by_plugin: None,
slug: sanitize_filename(workspace_name),
desired_path: String::new(),
path: None,
Expand Down
Loading