c7d3ec57c3
Commitd2217531(google-labs-jules SSRF fix) catastrophically deleted 11,500+ lines across 100+ files while fixing an unrelated IMAP issue. Restored from d2217531^ (pre-bad-commit state): Deleted files (fully restored): - app/api/{automation,classification_rules,comments,sharing}.py - app/middleware/upload_rate_limit.py - app/tasks/{automation_tasks,classify_document}.py - app/utils/{automation_hooks,classification_rules}.py - docs/AppleAppStoreCompliance.md - frontend/input.css, package.json, package-lock.json, tailwind.config.js - frontend/static/js/{annotations,claim,comments,sharing}.js - frontend/templates/{admin_connections,file_annotations,file_summary}.html - tests/{test_api_files_comprehensive,test_auth_extended,test_sharing, test_comments,test_connections,test_imap_profiles,test_api_sessions, test_automation,test_classification_rules,test_api_advanced_filters, test_api_classification_rules,test_upload_rate_limit,test_api_dropbox, test_classify_document,test_comments_ui,test_upload_to_icloud, test_api_onedrive_comprehensive,test_frontend_build,test_sentry, test_diagnostic,test_database,test_views_dropbox,test_local_auth}.py Truncated files (content restored): - app/{auth,config,main,models,celery_worker,database}.py - app/api/{__init__,api_tokens,diagnostic,dropbox,files,google_drive, integrations,local_auth,mobile,onedrive,pipelines,qr_auth, settings,url_upload}.py - app/middleware/upload_rate_limit.py - app/tasks/upload_to_nextcloud.py - app/utils/{allowed_types,settings_service,settings_sync,user_scope,webhook}.py - app/views/{base,dropbox,files,google_drive,onedrive,settings}.py - docs/{API,AuthenticationSetup,ConfigurationGuide,DatabaseConfiguration, DeploymentGuide,DropboxSetup,GoogleDriveSetup,KubernetesDeployment, MobileApp,OneDriveSetup,ProductionReadiness,SentrySetup, SocialLoginSetup,UserGuide}.md - frontend/static/{js/upload.js,styles.css} - frontend/templates/{api_tokens,base,devices,dropbox,dropbox_callback, file_view,files,google_drive,onedrive,onedrive_callback, signup}.html - frontend/translations/en.json - migrations/env.py - tests/{conftest,test_api_integrations,test_api_mobile,test_api_settings, test_api_tokens,test_audit_logs,test_duplicates,test_imap_tasks, test_setup_wizard,test_views_files_comprehensive}.py Security fixes kept from post-d2217531 commits: - app/utils/network.py: DNS SSRF fail-secure fix (06b0fced) - app/utils/file_operations.py: path traversal fix (1018ea17) - tests/test_imap_tasks.py: re-applied 4 is_private_ip mock patches Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> Agent-Logs-Url: https://github.com/christianlouis/DocuElevate/sessions/51133dd8-9bec-41ab-aa10-3de753634187
227 lines
11 KiB
HTML
227 lines
11 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}{{ file.document_title or file.original_filename or 'Document' }} - DocuElevate{% endblock %}
|
|
|
|
{% block head_extra %}
|
|
<script src="/static/js/common.js"></script>
|
|
<style>
|
|
.summary-container { max-width: 900px; margin: 0 auto; }
|
|
|
|
/* ── header ── */
|
|
.summary-header {
|
|
display: flex; align-items: flex-start; justify-content: space-between;
|
|
gap: 1rem; margin-bottom: 1.5rem; flex-wrap: wrap;
|
|
}
|
|
.summary-title { font-size: 1.75rem; font-weight: 700; color: #1f2937; line-height: 1.25; }
|
|
.summary-subtitle { font-size: 0.9rem; color: #6b7280; margin-top: 0.25rem; font-family: monospace; }
|
|
.dark .summary-title { color: #f3f4f6; }
|
|
.dark .summary-subtitle { color: #9ca3af; }
|
|
|
|
/* ── status pill ── */
|
|
.status-pill {
|
|
display: inline-flex; align-items: center; gap: 0.4rem;
|
|
padding: 0.35rem 0.85rem; border-radius: 9999px;
|
|
font-size: 0.8rem; font-weight: 600; white-space: nowrap;
|
|
}
|
|
.pill-completed { background: #d1fae5; color: #065f46; }
|
|
.pill-failed { background: #fee2e2; color: #991b1b; }
|
|
.pill-processing { background: #dbeafe; color: #1e3a8a; }
|
|
.pill-pending { background: #fef3c7; color: #92400e; }
|
|
.pill-duplicate { background: #e5e7eb; color: #374151; }
|
|
|
|
/* ── cards ── */
|
|
.summary-card {
|
|
background: white; border-radius: 0.5rem;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,.08);
|
|
padding: 1.5rem; margin-bottom: 1.25rem;
|
|
}
|
|
.dark .summary-card { background: #1f2937; box-shadow: 0 1px 3px rgba(0,0,0,.3); }
|
|
.summary-card-title {
|
|
font-size: 1rem; font-weight: 700; color: #374151;
|
|
text-transform: uppercase; letter-spacing: 0.05em;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.dark .summary-card-title { color: #d1d5db; }
|
|
|
|
/* ── info rows ── */
|
|
.info-row { display: flex; justify-content: space-between; padding: 0.4rem 0; border-bottom: 1px solid #f3f4f6; font-size: 0.875rem; }
|
|
.info-row:last-child { border-bottom: none; }
|
|
.info-key { color: #6b7280; }
|
|
.info-val { color: #1f2937; font-family: monospace; text-align: right; word-break: break-all; max-width: 60%; }
|
|
.dark .info-row { border-bottom-color: #374151; }
|
|
.dark .info-key { color: #9ca3af; }
|
|
.dark .info-val { color: #e5e7eb; }
|
|
|
|
/* ── nav cards ── */
|
|
.nav-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.25rem; }
|
|
@media (max-width: 768px) { .nav-grid { grid-template-columns: 1fr; } }
|
|
.nav-card {
|
|
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
background: white; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,.08);
|
|
padding: 1.5rem; text-decoration: none; color: #374151;
|
|
transition: box-shadow 0.15s, transform 0.15s; min-height: 120px;
|
|
}
|
|
.nav-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,.15); transform: translateY(-2px); }
|
|
.dark .nav-card { background: #1f2937; color: #e5e7eb; }
|
|
.dark .nav-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,.4); }
|
|
.nav-card i { font-size: 1.5rem; margin-bottom: 0.75rem; }
|
|
.nav-card-title { font-weight: 700; font-size: 0.95rem; margin-bottom: 0.25rem; }
|
|
.nav-card-desc { font-size: 0.8rem; color: #6b7280; text-align: center; }
|
|
.dark .nav-card-desc { color: #9ca3af; }
|
|
|
|
/* ── action buttons ── */
|
|
.action-btn {
|
|
display: inline-flex; align-items: center; gap: 0.4rem;
|
|
padding: 0.5rem 1.1rem; border-radius: 0.375rem;
|
|
font-size: 0.875rem; font-weight: 600; text-decoration: none;
|
|
cursor: pointer; border: none; transition: filter 0.1s;
|
|
}
|
|
.action-btn:hover { filter: brightness(0.92); }
|
|
.btn-primary { background: #3b82f6; color: white; }
|
|
.btn-secondary { background: #f3f4f6; color: #374151; }
|
|
|
|
/* ── error ── */
|
|
.error-box { background: #fee2e2; border: 1px solid #f87171; color: #b91c1c; padding: 1rem; border-radius: 0.375rem; margin-bottom: 1rem; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="summary-container">
|
|
|
|
{% if error %}
|
|
<div class="error-box"><strong>Error:</strong> {{ error }}</div>
|
|
{% elif file %}
|
|
|
|
<!-- ── Back + header ── -->
|
|
<a href="/files" style="display:inline-flex;align-items:center;gap:0.4rem;color:#3b82f6;font-weight:600;text-decoration:none;margin-bottom:1.25rem;" aria-label="Back to Files">
|
|
<i class="fas fa-arrow-left" aria-hidden="true"></i> Back to Files
|
|
</a>
|
|
|
|
<div class="summary-header">
|
|
<div>
|
|
<div class="summary-title">
|
|
{% if gpt_metadata and gpt_metadata.filename %}
|
|
{{ gpt_metadata.filename | replace('.pdf','') | replace('_',' ') }}
|
|
{% elif file.document_title %}
|
|
{{ file.document_title }}
|
|
{% else %}
|
|
{{ file.original_filename or '(untitled)' }}
|
|
{% endif %}
|
|
</div>
|
|
<div class="summary-subtitle">{{ file.original_filename }}</div>
|
|
</div>
|
|
|
|
<div style="display:flex;align-items:center;gap:0.75rem;flex-wrap:wrap;">
|
|
<!-- Status pill -->
|
|
{% if file.is_duplicate %}
|
|
<span class="status-pill pill-duplicate"><i class="fas fa-copy" aria-hidden="true"></i> Duplicate</span>
|
|
{% elif step_summary %}
|
|
{% set main_completed = step_summary.main.success + step_summary.main.skipped %}
|
|
{% if step_summary.main.failure > 0 or step_summary.uploads.failure > 0 %}
|
|
<span class="status-pill pill-failed"><i class="fas fa-times-circle" aria-hidden="true"></i> Failed</span>
|
|
{% elif step_summary.main["in_progress"] > 0 or step_summary.uploads["in_progress"] > 0 %}
|
|
<span class="status-pill pill-processing"><i class="fas fa-circle-notch fa-spin" aria-hidden="true"></i> Processing</span>
|
|
{% elif step_summary.total_main_steps > 0 and main_completed == step_summary.total_main_steps and step_summary.main.failure == 0 %}
|
|
<span class="status-pill pill-completed"><i class="fas fa-check-circle" aria-hidden="true"></i> Completed</span>
|
|
{% else %}
|
|
<span class="status-pill pill-pending"><i class="fas fa-pause-circle" aria-hidden="true"></i> Pending</span>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Navigation cards ── -->
|
|
<nav class="nav-grid" aria-label="File sections">
|
|
<a href="/files/{{ file.id }}/detail" class="nav-card">
|
|
<i class="fas fa-file-alt" aria-hidden="true" style="color:#3b82f6;"></i>
|
|
<div class="nav-card-title">Document Detail</div>
|
|
<div class="nav-card-desc">Metadata, preview & extracted text</div>
|
|
</a>
|
|
<a href="/files/{{ file.id }}/process" class="nav-card">
|
|
<i class="fas fa-cogs" aria-hidden="true" style="color:#6366f1;"></i>
|
|
<div class="nav-card-title">Processing</div>
|
|
<div class="nav-card-desc">Pipeline status & processing history</div>
|
|
</a>
|
|
<a href="/files/{{ file.id }}/annotations" class="nav-card">
|
|
<i class="fas fa-comments" aria-hidden="true" style="color:#10b981;"></i>
|
|
<div class="nav-card-title">Comments & Annotations</div>
|
|
<div class="nav-card-desc">Discussion & document annotations</div>
|
|
</a>
|
|
</nav>
|
|
|
|
<!-- ── File info card ── -->
|
|
<div class="summary-card">
|
|
<div class="summary-card-title"><i class="fas fa-info-circle" aria-hidden="true" style="color:#6366f1;margin-right:0.4rem;"></i>File Information</div>
|
|
<div class="info-row"><span class="info-key">File ID</span><span class="info-val">{{ file.id }}</span></div>
|
|
<div class="info-row"><span class="info-key">Filename</span><span class="info-val">{{ file.original_filename }}</span></div>
|
|
<div class="info-row"><span class="info-key">Size</span><span class="info-val">{{ (file.file_size / 1024) | round(1) }} KB</span></div>
|
|
<div class="info-row"><span class="info-key">MIME Type</span><span class="info-val">{{ file.mime_type or 'unknown' }}</span></div>
|
|
<div class="info-row"><span class="info-key">Created</span><span class="info-val">{{ file.created_at.strftime('%Y-%m-%d %H:%M') if file.created_at else 'N/A' }}</span></div>
|
|
{% if file.detected_language %}
|
|
<div class="info-row"><span class="info-key">Language</span><span class="info-val">{{ file.detected_language }}</span></div>
|
|
{% endif %}
|
|
{% if pipeline_info %}
|
|
<div class="info-row"><span class="info-key">Pipeline</span><span class="info-val">{{ pipeline_info.name }}</span></div>
|
|
{% endif %}
|
|
{% if file.document_title %}
|
|
<div class="info-row"><span class="info-key">Document Title</span><span class="info-val">{{ file.document_title }}</span></div>
|
|
{% endif %}
|
|
{% if multi_user_enabled %}
|
|
<div class="info-row">
|
|
<span class="info-key">{{ _("file.owner_label") }}</span>
|
|
<span class="info-val">{{ owner_display or _("file.owner_unowned") }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- ── Quick actions ── -->
|
|
<div class="summary-card">
|
|
<div class="summary-card-title"><i class="fas fa-bolt" aria-hidden="true" style="color:#f59e0b;margin-right:0.4rem;"></i>Quick Actions</div>
|
|
<div style="display:flex;gap:0.75rem;flex-wrap:wrap;">
|
|
{% if processed_file_exists %}
|
|
<a href="/api/files/{{ file.id }}/download?version=processed" class="action-btn btn-primary">
|
|
<i class="fas fa-download" aria-hidden="true"></i> Download Processed
|
|
</a>
|
|
{% endif %}
|
|
{% if original_file_exists %}
|
|
<a href="/api/files/{{ file.id }}/download?version=original" class="action-btn btn-secondary">
|
|
<i class="fas fa-download" aria-hidden="true"></i> Download Original
|
|
</a>
|
|
{% endif %}
|
|
<a href="/files/{{ file.id }}/detail" class="action-btn btn-secondary">
|
|
<i class="fas fa-eye" aria-hidden="true"></i> View Detail
|
|
</a>
|
|
{% if multi_user_enabled and file.owner_id is none %}
|
|
<button
|
|
class="action-btn btn-primary"
|
|
id="claim-btn"
|
|
aria-label="{{ _('file.claim_ownership') }}"
|
|
style="background:#10b981;"
|
|
>
|
|
<i class="fas fa-user-check" aria-hidden="true"></i> {{ _("file.claim_ownership") }}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
<p id="claim-msg" style="margin-top:0.5rem;font-size:0.875rem;display:none;" role="alert"></p>
|
|
</div>
|
|
|
|
{% else %}
|
|
<!-- file is None without error -->
|
|
<div class="error-box">Document not found.</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
|
|
{% if multi_user_enabled and file and file.owner_id is none %}
|
|
<script src="{{ url_for('static', path='js/claim.js') }}" defer></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
initClaimOwnership({{ file.id | tojson }}, {
|
|
confirm: {{ _("file.claim_ownership_confirm") | tojson }},
|
|
success: {{ _("file.claim_ownership_success") | tojson }},
|
|
failed: {{ _("file.claim_ownership_failed") | tojson }}
|
|
});
|
|
});
|
|
</script>
|
|
{% endif %}
|
|
{% endblock %}
|