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
13 changes: 13 additions & 0 deletions api/migrations/202701010001_create_git_pull_sessions.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CREATE TABLE IF NOT EXISTS git_pull_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id UUID NOT NULL,
status TEXT NOT NULL DEFAULT 'pending', -- pending | resolving | merged | stale
conflicts JSONB NOT NULL DEFAULT '[]'::jsonb,
resolutions JSONB NOT NULL DEFAULT '[]'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
base_commit BYTEA NULL,
remote_commit BYTEA NULL
);

CREATE INDEX IF NOT EXISTS idx_git_pull_sessions_workspace ON git_pull_sessions(workspace_id, updated_at DESC);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE git_pull_sessions
ADD COLUMN IF NOT EXISTS message TEXT NULL;
2 changes: 1 addition & 1 deletion api/openapi/openapi.json

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions api/src/application/dto/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,63 @@ pub struct GitSyncOutcome {
pub message: String,
}

#[derive(Debug, Clone)]
pub struct GitImportOutcome {
pub files_changed: u32,
pub commit_hash: Option<String>,
pub docs_created: u32,
pub attachments_created: u32,
pub message: String,
}

#[derive(Debug, Clone)]
pub struct GitignoreUpdateDto {
pub added: usize,
pub patterns: Vec<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct GitPullResolutionDto {
pub path: String,
/// one of: ours, theirs, custom_text
pub choice: String,
pub content: Option<String>,
}

#[derive(Debug, Clone)]
pub struct GitPullRequestDto {
pub resolutions: Vec<GitPullResolutionDto>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct GitPullConflictItemDto {
pub path: String,
pub is_binary: bool,
pub ours: Option<String>,
pub theirs: Option<String>,
pub base: Option<String>,
pub document_id: Option<uuid::Uuid>,
}

#[derive(Debug, Clone)]
pub struct GitPullResultDto {
pub success: bool,
pub message: String,
pub files_changed: u32,
pub commit_hash: Option<String>,
pub conflicts: Option<Vec<GitPullConflictItemDto>>,
pub base_commit: Option<Vec<u8>>,
pub remote_commit: Option<Vec<u8>>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct GitPullSessionDto {
pub id: uuid::Uuid,
pub workspace_id: uuid::Uuid,
pub status: String,
pub conflicts: Vec<GitPullConflictItemDto>,
pub resolutions: Vec<GitPullResolutionDto>,
pub message: Option<String>,
pub base_commit: Option<Vec<u8>>,
pub remote_commit: Option<Vec<u8>>,
}
1 change: 1 addition & 0 deletions api/src/application/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pub mod linkgraph;
pub mod ports;
pub mod services;
pub mod use_cases;
pub mod utils;
10 changes: 10 additions & 0 deletions api/src/application/ports/git_pull_session_repository.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use async_trait::async_trait;
use uuid::Uuid;

use crate::application::dto::git::GitPullSessionDto;

#[async_trait]
pub trait GitPullSessionRepository: Send + Sync {
async fn upsert(&self, session: GitPullSessionDto) -> anyhow::Result<()>;
async fn get(&self, workspace_id: Uuid, id: Uuid) -> anyhow::Result<Option<GitPullSessionDto>>;
}
29 changes: 27 additions & 2 deletions api/src/application/ports/git_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use uuid::Uuid;

use crate::application::dto::diff::TextDiffResult;
use crate::application::dto::git::{
GitChangeItem, GitCommitInfo, GitRemoteCheckDto, GitSyncOutcome, GitSyncRequestDto,
GitWorkspaceStatus,
GitChangeItem, GitCommitInfo, GitImportOutcome, GitPullRequestDto, GitPullResultDto,
GitRemoteCheckDto, GitSyncOutcome, GitSyncRequestDto, GitWorkspaceStatus,
};
use crate::application::ports::git_repository::UserGitCfg;

Expand Down Expand Up @@ -32,6 +32,31 @@ pub trait GitWorkspacePort: Send + Sync {
req: &GitSyncRequestDto,
cfg: Option<&UserGitCfg>,
) -> anyhow::Result<GitSyncOutcome>;
async fn import_repository(
&self,
workspace_id: Uuid,
actor_id: Uuid,
cfg: &UserGitCfg,
) -> anyhow::Result<GitImportOutcome>;
async fn pull(
&self,
workspace_id: Uuid,
actor_id: Uuid,
req: &GitPullRequestDto,
cfg: &UserGitCfg,
) -> anyhow::Result<GitPullResultDto>;
async fn head_commit(&self, workspace_id: Uuid) -> anyhow::Result<Option<Vec<u8>>>;
async fn remote_head(
&self,
workspace_id: Uuid,
cfg: &UserGitCfg,
) -> anyhow::Result<Option<Vec<u8>>>;
async fn has_pending_changes(&self, workspace_id: Uuid) -> anyhow::Result<bool>;
async fn drift_since_commit(
&self,
workspace_id: Uuid,
base_commit: &[u8],
) -> anyhow::Result<bool>;

async fn check_remote(
&self,
Expand Down
1 change: 1 addition & 0 deletions api/src/application/ports/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod document_exporter;
pub mod document_repository;
pub mod document_snapshot_archive_repository;
pub mod files_repository;
pub mod git_pull_session_repository;
pub mod git_rebuild_job_queue;
pub mod git_repository;
pub mod git_storage;
Expand Down
6 changes: 2 additions & 4 deletions api/src/application/services/api_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use argon2::{
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
};
use rand::{Rng, distributions::Alphanumeric, rngs::OsRng};
use sha2::{Digest, Sha256};
use uuid::Uuid;

use crate::application::dto::api_tokens::{ApiTokenDto, CreatedApiTokenDto};
Expand All @@ -14,6 +13,7 @@ use crate::application::services::errors::ServiceError;
use crate::application::use_cases::api_tokens::create_token::CreateApiToken;
use crate::application::use_cases::api_tokens::list_tokens::ListApiTokens;
use crate::application::use_cases::api_tokens::revoke_token::RevokeApiToken;
use crate::application::utils::hash::sha256_hex_str;
use crate::domain::workspaces::permissions::{PERM_API_TOKEN_MANAGE, PermissionSet};

pub struct ApiTokenService {
Expand Down Expand Up @@ -110,9 +110,7 @@ pub fn generate_api_token() -> anyhow::Result<GeneratedApiToken> {
}

pub fn compute_digest(token: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(token.as_bytes());
hex::encode(hasher.finalize())
sha256_hex_str(token)
}

pub fn verify_token(token: &str, token_hash: &str) -> anyhow::Result<bool> {
Expand Down
12 changes: 2 additions & 10 deletions api/src/application/services/documents.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::fmt::Write;
use std::path::Path;
use std::sync::Arc;

use sha2::{Digest, Sha256};
use sqlx::{Pool, Postgres, Transaction};
use tracing::{error, warn};
use uuid::Uuid;
Expand Down Expand Up @@ -50,6 +48,7 @@ use crate::application::use_cases::documents::snapshot_download::{
};
use crate::application::use_cases::documents::unarchive_document::UnarchiveDocument;
use crate::application::use_cases::documents::update_document::UpdateDocument;
use crate::application::utils::hash::sha256_hex;
use crate::domain::documents::document::{
BacklinkInfo as DomainBacklink, Document as DomainDocument, OutgoingLink as DomainOutgoingLink,
SearchHit,
Expand Down Expand Up @@ -1406,14 +1405,7 @@ fn duplicate_title(source_title: &str, override_title: Option<String>) -> String
}

fn hash_bytes(bytes: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(bytes);
let digest = hasher.finalize();
let mut out = String::with_capacity(64);
for byte in digest {
let _ = write!(&mut out, "{:02x}", byte);
}
out
sha256_hex(bytes)
}

fn path_depth(path: &str) -> usize {
Expand Down
Loading