chore(i18n): finish translating code comments and scripts to English
CI / Format and clippy (push) Successful in 1m57s
CI / Dependency audit (push) Successful in 13s
CI / Check desktop app (Linux) (push) Successful in 3m54s
CI / Tests (Linux) (push) Failing after 7m25s
Release / Create release (push) Successful in 6s
Release / CLI (linux-aarch64) (push) Successful in 1m48s
Release / CLI (linux-x86_64) (push) Successful in 2m39s
Release / Desktop app (linux-aarch64) (push) Successful in 10m9s
Release / Desktop app (linux-x86_64) (push) Successful in 12m20s
Release / Desktop app (windows) (push) Successful in 7m32s
Release / Build latest.json (push) Successful in 5s

Completes the English-only standardization for code, comments, scripts
and CI templates (UI/CLI-facing strings stay bilingual pt-BR/en,
untouched). Covers the remaining crates/, apps/gui/ui/ and
apps/gui/src-tauri/ comments, .gitea/ workflow and template comments,
.githooks/ hooks, and the scripts/ source comments for the files
already renamed in a prior commit (check.sh, generate-logo.py,
install-hooks.sh).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
This commit is contained in:
Lucas RayanandClaude Sonnet 5 committed 2026-10-30 20:43:02 -03:00
1 parent da212e6926
commit 6328220c60
129 files changed
+1961 -1834

No files matched your search

+56 -56
View File
@@ -1,5 +1,5 @@
//! Camada de serviço usada pela interface gráfica (Tauri e modo `guara ui`):
//! jobs em segundo plano (varredura, recuperação, imagem) e despachante RPC.
//! Background service layer backing the GUI (Tauri and `guara ui` mode):
//! manages scan/recovery/imaging jobs, sessions, and JSON-RPC dispatch.
use crate::device;
use crate::error::{Error, Result};
@@ -49,7 +49,7 @@ struct State {
report: Option<Value>,
started: Instant,
finished_in: Option<f64>,
/// Leitura de datas EXIF/dimensões em andamento: (feitos, total).
/// Background EXIF/dimension enrichment progress: `(done, total)`.
enrich: Option<(u64, u64)>,
recoveries: Vec<recover::RecoverReport>,
started_unix: Option<i64>,
@@ -64,11 +64,11 @@ pub struct Job {
cancel: AtomicBool,
stop_requested: AtomicBool,
src: Mutex<Option<Arc<dyn Source>>>,
/// Caminho atualmente usado para ler a origem (pode mudar se o disco voltar com outro nome).
/// Active device/image path used to read the source (may update if reattached under a new node).
src_path: Mutex<String>,
/// Impressão digital do disco (reconhecer o mesmo disco depois de reconectar).
/// Source disk content fingerprint (used to recognize the drive after reconnection).
fingerprint: Mutex<Option<String>>,
/// Arquivo de sessão salvo automaticamente.
/// Path to the autosaved session file.
session_file: Mutex<Option<PathBuf>>,
st: Mutex<State>,
}
@@ -213,7 +213,7 @@ impl Engine {
job
}
// ---------------------------------------------------------------- varredura
// ---------------------------------------------------------------- scan
pub fn start_scan(&self, path: &str, mut opts: ScanOptions) -> Result<String> {
let raw = open_path(Path::new(path)).map_err(|e| open_error(path, e))?;
@@ -282,7 +282,7 @@ impl Engine {
}
s.checkpoint.as_ref().is_some_and(|c| c.io_lost)
};
// Sem origem, ou depois de uma desconexão: reabre o disco (mesmo com outro nome).
// Source not yet open or previously disconnected: try to reopen (even under a new device name).
if j.source().is_none() || io_lost {
self.reconnect(&j).map_err(|e| {
Error::msg(format!(
@@ -359,14 +359,14 @@ impl Engine {
Ok(json!({ "total": total, "items": page }))
}
/// Contagens para a navegação lateral (tipos, datas, pastas, câmeras).
/// Sidebar navigation facet counts (categories, dates, folders, cameras).
pub fn facets(&self, id: &str) -> Result<Value> {
let j = self.job(id)?;
let s = j.st.lock().unwrap();
Ok(crate::explorer::facets(&s.found))
}
/// Esquema de um banco SQLite achado, com o número de linhas de cada tabela.
/// Schema of a recovered SQLite database, including row counts per table.
pub fn db_info(&self, id: &str, file: u64) -> Result<Value> {
let (f, src) = self.open_file(id, file)?;
let fs = recover::FoundSource {
@@ -383,7 +383,7 @@ impl Engine {
Ok(v)
}
/// Uma página de linhas de uma tabela (ou os registros apagados prováveis dela).
/// A page of live table rows (or carvable deleted records from free blocks).
pub fn db_rows(
&self,
id: &str,
@@ -415,13 +415,13 @@ impl Engine {
Ok(serde_json::to_value(&page)?)
}
/// Metadados detalhados (EXIF, PDF, Office, vídeo…) de um arquivo.
/// Detailed format metadata (EXIF, PDF, Office, video, etc.) for a recovered file.
pub fn metadata(&self, id: &str, file: u64) -> Result<crate::meta::Meta> {
let (f, src) = self.open_file(id, file)?;
Ok(crate::meta::extract(src.as_ref(), &f))
}
/// Miniatura JPEG (cache em memória; decodificações limitadas em paralelo).
/// Generates or fetches an in-memory cached JPEG thumbnail (with bounded concurrency).
pub fn thumb(&self, job: &str, id: u64, size: u32) -> std::result::Result<Arc<Vec<u8>>, u16> {
let size = size.clamp(48, 640);
let key = (job.to_string(), id, size);
@@ -445,7 +445,7 @@ impl Engine {
}
}
/// Achado + origem para leitura posicional (streaming de pré-visualização).
/// Returns `(Found, Source)` for positional streaming preview.
pub fn open_file(&self, id: &str, file: u64) -> Result<(Found, Arc<dyn Source>)> {
let j = self.job(id)?;
let s = j.st.lock().unwrap();
@@ -456,7 +456,7 @@ impl Engine {
Ok((s.found[i].clone(), j.source().ok_or_else(no_source)?))
}
// ---------------------------------------------------------------- recuperação
// ---------------------------------------------------------------- recovery
pub fn start_recover(
&self,
@@ -575,13 +575,13 @@ impl Engine {
Ok(job.id.clone())
}
// ---------------------------------------------------------------- imagem
// ---------------------------------------------------------------- disk imaging
pub fn start_image(&self, path: &str, dest: &str) -> Result<String> {
self.start_image_as(path, dest, &image::ImageFormat::Raw)
}
/// Cria a imagem em bruto ou em outro formato forense (E01 ou AFF4).
/// Creates a raw, E01, or AFF4 forensic disk image.
pub fn start_image_as(
&self,
path: &str,
@@ -644,9 +644,9 @@ impl Engine {
Ok(job.id.clone())
}
// ---------------------------------------------------------------- exportação e relatório
// ---------------------------------------------------------------- export and report
/// CSV dos achados que atendem ao filtro (ou todos). Devolve quantas linhas gravou.
/// Exports matching findings (or all findings) to CSV. Returns the number of rows written.
pub fn export_csv(&self, id: &str, path: &str, q: Option<&Query>) -> Result<usize> {
let j = self.job(id)?;
let s = j.st.lock().unwrap();
@@ -662,7 +662,7 @@ impl Engine {
Ok(items.len())
}
/// Relatório HTML de recuperação (identificação do caso, origem, achados, hashes).
/// Generates an HTML or PDF forensic recovery report (case info, source, findings, hashes).
pub fn export_report(
&self,
id: &str,
@@ -714,7 +714,7 @@ impl Engine {
Ok(())
}
// ---------------------------------------------------------------- busca de texto e hash da origem
// ---------------------------------------------------------------- text search and source hashing
pub fn start_search(&self, path: &str, opts: crate::search::SearchOpts) -> Result<String> {
if opts.terms.iter().all(|t| t.trim().is_empty()) {
@@ -776,7 +776,7 @@ impl Engine {
}))
}
/// Calcula MD5/SHA-1/SHA-256 da origem inteira (verificação de integridade).
/// Computes MD5/SHA-1/SHA-256 across the entire source (integrity verification job).
pub fn start_hash(&self, path: &str) -> Result<String> {
let src = open_path(Path::new(path)).map_err(|e| open_error(path, e))?;
let job = self.register(
@@ -833,7 +833,7 @@ impl Engine {
Ok(job.id.clone())
}
// ---------------------------------------------------------------- sessões
// ---------------------------------------------------------------- sessions
pub fn save_session(&self, id: &str, path: &str) -> Result<()> {
let j = self.job(id)?;
@@ -860,7 +860,7 @@ impl Engine {
"O arquivo {path} não é uma sessão do Guará válida (ou foi criado por uma versão incompatível): {e}"
))
})?;
// Clicar de novo numa sessão já aberta reaproveita o job em vez de criar outro igual.
// Re-opening an already loaded session reuses its existing job ID.
let want = Path::new(path);
if let Some(j) = self
.jobs
@@ -899,8 +899,8 @@ impl Engine {
Status::Paused
};
}
// A sessão sempre abre. Tenta religar o disco (inclusive se ele voltou com outro nome);
// sem a origem ainda dá para navegar, exportar e gerar relatório.
// Always load the session metadata. Attempt to reconnect the source disk (even under a new path);
// if the source is offline, browsing, filtering, CSV export, and reporting still work.
match self.reconnect(&job) {
Ok(Some(nome)) => job.st.lock().unwrap().warnings.push(format!(
"O disco foi reconhecido de novo como {nome} (o nome mudou desde a varredura)."
@@ -911,7 +911,7 @@ impl Engine {
data.target
)),
}
// continua salvando automaticamente no mesmo arquivo se a varredura for retomada
// keep autosaving to the same file if the scan is resumed
*job.session_file.lock().unwrap() = Some(PathBuf::from(path));
crate::audit::record(
"session.load",
@@ -923,9 +923,9 @@ impl Engine {
Ok(job.id.clone())
}
/// Reabre a origem da sessão: tenta o caminho atual e o original e, se o disco voltou com
/// outro nome, procura entre os dispositivos conectados o que tem o mesmo conteúdo.
/// `Ok(Some(caminho))` quando achou o disco num caminho diferente do esperado.
/// Reopens the session's source: tries the current and original paths first, then scans attached
/// devices for a matching fingerprint if the drive reconnected under a new device node.
/// Returns `Ok(Some(new_path))` when found at a different device path.
fn reconnect(&self, j: &Arc<Job>) -> Result<Option<String>> {
let fp = j.fingerprint.lock().unwrap().clone();
let size =
@@ -970,8 +970,8 @@ impl Engine {
))
}
/// Aponta uma sessão carregada para a origem (disco ou imagem) atual.
/// Recusa uma origem que não seja o mesmo disco, a menos que `force`.
/// Rebinds a loaded session to a user-selected source device or disk image.
/// Rejects a source whose fingerprint/size mismatches the session unless `force` is set.
pub fn relink(&self, id: &str, path: &str, force: bool) -> Result<()> {
let j = self.job(id)?;
let src = open_path(Path::new(path)).map_err(|e| open_error(path, e))?;
@@ -1010,7 +1010,7 @@ impl Engine {
Ok(())
}
/// Discos conectados que podem ser a origem desta sessão (mesmo tamanho primeiro).
/// Lists attached disks that could match this session's source (same-size candidates first).
pub fn candidates(&self, id: &str) -> Result<Value> {
let j = self.job(id)?;
let fp = j.fingerprint.lock().unwrap().clone();
@@ -1042,9 +1042,9 @@ impl Engine {
Ok(json!(out))
}
// ---------------------------------------------------------------- sessões automáticas
// ---------------------------------------------------------------- autosaved sessions
/// Sessões salvas automaticamente (mais recentes primeiro).
/// Lists autosaved sessions (most recent first).
pub fn sessions_list(&self) -> Value {
let dir = sessions_dir();
let mut items: Vec<Value> = vec![];
@@ -1071,7 +1071,7 @@ impl Engine {
json!(items)
}
/// Apaga uma sessão automática (e a miniatura de metadados).
/// Deletes an autosaved session file (and its `.meta.json` sidecar).
pub fn session_delete(&self, path: &str) -> Result<()> {
let p = Path::new(path);
if p.parent() != Some(sessions_dir().as_path()) {
@@ -1162,7 +1162,7 @@ impl Engine {
"health" => json!(crate::health::check(&s("path")?)),
"open_url" => {
let u = s("url")?;
// só mapas do OpenStreetMap: evita abrir endereços arbitrários a partir de metadados
// restrict to OpenStreetMap URLs: never open arbitrary URLs derived from untrusted file metadata
if !u.starts_with("https://www.openstreetmap.org/") {
return Err(Error::msg("endereço não permitido"));
}
@@ -1332,7 +1332,7 @@ struct SessionFile {
use crate::explorer::cat_key;
/// 503 quando a origem da sessão está desconectada; 404 nos demais casos.
/// Returns HTTP 503 when the session source is disconnected, or 404 otherwise.
fn status_of(e: Error) -> u16 {
if e.to_string().contains("não está disponível") {
503
@@ -1356,7 +1356,7 @@ fn open_error(path: &str, e: std::io::Error) -> Error {
}
fn spawn_scan(job: Arc<Job>, cp: Option<Checkpoint>) {
// Salva a sessão sozinho enquanto a varredura roda (e ao terminar/pausar).
// Periodically autosaves the session while scanning (and upon completion or pause).
let saver = job.clone();
std::thread::spawn(move || autosave_loop(saver));
std::thread::spawn(move || {
@@ -1420,8 +1420,8 @@ fn spawn_scan(job: Arc<Job>, cp: Option<Checkpoint>) {
});
}
/// Lê EXIF/dimensões das imagens achadas (em segundo plano) para permitir
/// navegar por data, câmera e resolução.
/// Background worker that extracts EXIF dates, camera models, GPS, and image dimensions
/// so findings can be browsed by date, camera, and resolution.
fn spawn_enrich(job: Arc<Job>) {
std::thread::spawn(move || {
let Some(src) = job.source() else { return };
@@ -1487,8 +1487,8 @@ pub fn base64(data: &[u8]) -> String {
out
}
/// Tipo MIME para servir a pré-visualização. Formatos que poderiam executar
/// script (HTML/SVG/XML) saem como texto puro.
/// Returns a safe MIME type for preview streaming. Formats capable of executing
/// scripts (HTML/SVG/XML) are served as `text/plain`.
pub fn mime_for(ext: &str) -> &'static str {
match ext.to_ascii_lowercase().as_str() {
"jpg" | "jpeg" => "image/jpeg",
@@ -1513,13 +1513,13 @@ pub fn mime_for(ext: &str) -> &'static str {
}
}
/// Trecho de um arquivo achado, pronto para responder a uma requisição HTTP
/// (com suporte a `Range`) tanto no modo `guara ui` quanto no app Tauri.
/// Byte slice of a recovered file, ready to answer an HTTP `Range` request
/// in both `guara ui` mode and the Tauri custom protocol handler.
pub struct FileSlice {
found: Found,
src: Arc<dyn Source>,
pub start: u64,
/// Inclusivo.
/// Inclusive end byte offset.
pub end: u64,
pub total: u64,
pub partial: bool,
@@ -1549,7 +1549,7 @@ impl FileSlice {
("Accept-Ranges", "bytes".into()),
("Cache-Control", "no-store".into()),
("X-Content-Type-Options", "nosniff".into()),
// Conteúdo recuperado é não confiável: nunca executa script nem carrega nada.
// Recovered content is untrusted: forbid script execution and external resource loading via CSP.
(
"Content-Security-Policy",
"sandbox; default-src 'none'".into(),
@@ -1569,7 +1569,7 @@ impl FileSlice {
}
h
}
/// Lê o trecho inteiro em memória (limitado a `cap` bytes).
/// Reads the requested byte range into memory (capped at `cap` bytes).
pub fn read_all(&self, cap: u64) -> std::io::Result<Vec<u8>> {
let n = self.len().min(cap) as usize;
let mut out = vec![0u8; n];
@@ -1621,7 +1621,7 @@ impl std::io::Read for FileSliceReader {
}
impl Engine {
/// Resolve `/file/<job>/<id>` + cabeçalho Range. `Err(status)` em caso de falha.
/// Resolves `/file/<job>/<id>` with optional `Range` header. Returns `Err(http_status)` on failure.
pub fn file_slice(
&self,
job: &str,
@@ -1663,9 +1663,9 @@ impl Engine {
}
}
// ---------------------------------------------------------------- sessões automáticas
// ---------------------------------------------------------------- autosaved sessions
/// Reaplica ao disco reaberto a montagem (volumes desbloqueados, RAID) da sessão.
/// Re-applies the session's virtual disk assembly (unlocked volumes, RAID, LVM) to a reopened source.
fn finish_source(j: &Job, raw: Arc<dyn Source>) -> Arc<dyn Source> {
match crate::assemble::assemble_source(raw.clone(), &j.opts.assembly) {
Ok(a) => a.source,
@@ -1700,7 +1700,7 @@ fn new_session_path(target: &str) -> PathBuf {
fn snapshot(j: &Job) -> SessionFile {
let s = j.st.lock().unwrap();
// Senhas e chaves nunca vão para o arquivo de sessão (só as chaves de volume, se o usuário pediu).
// Passwords and raw keyfiles are stripped before saving (only derived volume keys are kept if opted in).
let mut options = j.opts.clone();
options.assembly.secrets.clear();
if !options.assembly.remember_keys {
@@ -1722,14 +1722,14 @@ fn snapshot(j: &Job) -> SessionFile {
}
}
/// Grava a sessão de forma atômica (arquivo temporário + renomear), com o dono certo.
/// Atomically writes the session file (temp file + rename) owned by the invoking user.
fn write_session(j: &Job, path: &Path) -> Result<()> {
let data = snapshot(j);
let tmp = path.with_extension("json.tmp");
crate::owner::write_owned(&tmp, serde_json::to_vec(&data)?)?;
std::fs::rename(&tmp, path)?;
crate::owner::give_to_user(path);
// metadados pequenos para listar as sessões recentes sem ler o arquivo inteiro
// lightweight `.meta.json` sidecar so recent sessions can be listed without parsing full snapshots
let status = if data.done {
"concluída"
} else if data.checkpoint.is_some() {
@@ -1749,7 +1749,7 @@ fn write_session(j: &Job, path: &Path) -> Result<()> {
Ok(())
}
/// Salva de tempos em tempos (o intervalo cresce se a sessão for grande) e no fim.
/// Autosaves periodically (backing off as snapshot write time grows) and once on completion.
fn autosave_loop(job: Arc<Job>) {
let mut last = Instant::now();
let mut interval = std::time::Duration::from_secs(45);
@@ -1766,7 +1766,7 @@ fn autosave_loop(job: Arc<Job>) {
if write_session(&job, &path).is_err() {
return;
}
// nunca gasta mais de ~10% do tempo salvando
// spend at most ~10% of wall-clock time writing snapshots
interval = (t0.elapsed() * 10).max(std::time::Duration::from_secs(45));
last = Instant::now();
if !running {