added more pages and changed project name to DocuNova
This commit is contained in:
+9
-1
@@ -19,7 +19,7 @@ router.mount("/static", StaticFiles(directory=frontend_folder), name="static")
|
|||||||
@router.get("/upload", response_class=FileResponse)
|
@router.get("/upload", response_class=FileResponse)
|
||||||
@require_login
|
@require_login
|
||||||
async def serve_upload(request: Request):
|
async def serve_upload(request: Request):
|
||||||
return os.path.join(frontend_folder, "index.html")
|
return os.path.join(frontend_folder, "upload.html")
|
||||||
|
|
||||||
# 3) Serve favicon.ico from the frontend folder
|
# 3) Serve favicon.ico from the frontend folder
|
||||||
@router.get("/favicon.ico", response_class=FileResponse)
|
@router.get("/favicon.ico", response_class=FileResponse)
|
||||||
@@ -29,3 +29,11 @@ def favicon():
|
|||||||
""" @router.exception_handler(404)
|
""" @router.exception_handler(404)
|
||||||
async def custom_404_handler(request: Request, exc):
|
async def custom_404_handler(request: Request, exc):
|
||||||
return FileResponse("frontend/404.html", status_code=status.HTTP_404_NOT_FOUND) """
|
return FileResponse("frontend/404.html", status_code=status.HTTP_404_NOT_FOUND) """
|
||||||
|
|
||||||
|
@router.get("/", response_class=FileResponse)
|
||||||
|
async def serve_upload(request: Request):
|
||||||
|
return os.path.join(frontend_folder, "index.html")
|
||||||
|
|
||||||
|
@router.get("/about", response_class=FileResponse)
|
||||||
|
async def serve_upload(request: Request):
|
||||||
|
return os.path.join(frontend_folder, "about.html")
|
||||||
+2
-4
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from fastapi import FastAPI, HTTPException, UploadFile, File, status, Request
|
from fastapi import FastAPI, HTTPException, UploadFile, File, status, Request, FileResponse
|
||||||
from starlette.middleware.sessions import SessionMiddleware
|
from starlette.middleware.sessions import SessionMiddleware
|
||||||
from starlette.config import Config
|
from starlette.config import Config
|
||||||
from starlette.middleware.trustedhost import TrustedHostMiddleware
|
from starlette.middleware.trustedhost import TrustedHostMiddleware
|
||||||
@@ -52,9 +52,7 @@ app.add_middleware(TrustedHostMiddleware, allowed_hosts=[
|
|||||||
def on_startup():
|
def on_startup():
|
||||||
init_db() # Create tables if they don't exist
|
init_db() # Create tables if they don't exist
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def root():
|
|
||||||
return {"message": "Document Processing API"}
|
|
||||||
|
|
||||||
@app.post("/process/")
|
@app.post("/process/")
|
||||||
def process(file_path: str):
|
def process(file_path: str):
|
||||||
|
|||||||
+67
-12
@@ -2,20 +2,75 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>404 - Page not found</title>
|
<title>404 - Oops, DocuNova Lost the Page</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- Tailwind CSS via CDN -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-50 h-screen flex flex-col items-center justify-center">
|
<body class="bg-gradient-to-br from-blue-50 to-white min-h-screen flex flex-col">
|
||||||
<h1 class="text-4xl font-bold mb-4">404 - Page not found</h1>
|
<!-- Navigation Bar -->
|
||||||
<p class="text-gray-700 mb-8">
|
<nav class="bg-white shadow">
|
||||||
Sorry, we couldn’t find the page you’re looking for.
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between h-16 items-center">
|
||||||
</p>
|
<!-- Brand -->
|
||||||
<a
|
<div class="flex-shrink-0">
|
||||||
href="/"
|
<a href="/" class="text-xl font-bold text-blue-500 hover:text-blue-700">
|
||||||
class="text-blue-600 underline text-lg hover:text-blue-800"
|
DocuNova
|
||||||
>
|
</a>
|
||||||
← Back to home
|
</div>
|
||||||
</a>
|
<!-- Menu Items -->
|
||||||
|
<div class="flex space-x-6 items-center">
|
||||||
|
<a href="/" class="text-gray-700 hover:text-gray-900">Home</a>
|
||||||
|
<a href="/upload" class="text-gray-700 hover:text-gray-900">Upload</a>
|
||||||
|
<a href="/about" class="text-gray-700 hover:text-gray-900">About</a>
|
||||||
|
<a href="/files" class="text-gray-700 hover:text-gray-900">Files</a>
|
||||||
|
<!-- Dynamic Auth Section -->
|
||||||
|
<div id="authSection" class="text-gray-700 hover:text-gray-900"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content Area -->
|
||||||
|
<main class="flex-grow flex flex-col items-center justify-center text-center px-4">
|
||||||
|
<div class="max-w-xl">
|
||||||
|
<h1 class="text-7xl font-extrabold text-blue-600 mb-6">404</h1>
|
||||||
|
<h2 class="text-3xl font-bold text-gray-800 mb-4">Oops, we couldn’t find that page!</h2>
|
||||||
|
<p class="text-gray-600 mb-8">
|
||||||
|
It seems DocuNova has misplaced the document you were looking for. Whether it got lost in the cloud or hidden between the files, don’t worry – we’ve got your back.
|
||||||
|
</p>
|
||||||
|
<a href="/" class="inline-block bg-blue-500 hover:bg-blue-600 text-white font-semibold py-3 px-6 rounded-lg transition-colors">
|
||||||
|
← Return Home
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="bg-white shadow">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 py-4 text-center text-gray-600">
|
||||||
|
© 2025 DocuNova. All rights reserved.
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- JavaScript for Dynamic Auth Section -->
|
||||||
|
<script>
|
||||||
|
(async function checkAuth() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch("/api/whoami");
|
||||||
|
if (resp.ok) {
|
||||||
|
const data = await resp.json();
|
||||||
|
document.getElementById("authSection").innerHTML = `
|
||||||
|
<img src="${data.picture}" alt="User Avatar" class="inline-block h-8 w-8 rounded-full mr-2">
|
||||||
|
Logged in as <strong>${data.email}</strong>
|
||||||
|
<a href="/logout" class="ml-4 text-blue-600 hover:text-blue-800">Logout</a>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
document.getElementById("authSection").innerHTML =
|
||||||
|
`<a href="/login" class="text-blue-600">Login</a>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById("authSection").innerHTML =
|
||||||
|
`<a href="/login" class="text-blue-600">Login</a>`;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>About DocuNova</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- Tailwind CSS via CDN -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50 min-h-screen flex flex-col">
|
||||||
|
<!-- Navigation Bar -->
|
||||||
|
<nav class="bg-white shadow">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between h-16 items-center">
|
||||||
|
<!-- Brand -->
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<a href="/" class="text-xl font-bold text-blue-500 hover:text-blue-700">
|
||||||
|
DocuNova
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<!-- Menu items -->
|
||||||
|
<div class="flex space-x-4 items-center">
|
||||||
|
<a href="/" class="text-gray-700 hover:text-gray-900">Home</a>
|
||||||
|
<a href="/upload" class="text-gray-700 hover:text-gray-900">Upload</a>
|
||||||
|
<a href="/about" class="text-gray-700 hover:text-gray-900">About</a>
|
||||||
|
<a href="/files" class="text-gray-700 hover:text-gray-900">Files</a>
|
||||||
|
<!-- Dynamic auth section -->
|
||||||
|
<div id="authSection" class="text-gray-700 hover:text-gray-900"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content Area -->
|
||||||
|
<main class="flex-grow container mx-auto px-4 py-8">
|
||||||
|
<h1 class="text-4xl font-bold mb-4">About DocuNova</h1>
|
||||||
|
<p class="text-gray-700 mb-6 leading-relaxed">
|
||||||
|
Welcome to <strong>DocuNova</strong> – your modern, intelligent solution for document processing! We’ve built DocuNova to completely transform the way you handle your documents – from upload to extraction, from processing to storage. With cutting-edge technologies and a user-first design, DocuNova makes managing your documents as easy as a click.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Our Story Section -->
|
||||||
|
<section class="bg-white shadow rounded p-6 mb-8">
|
||||||
|
<h2 class="text-2xl font-semibold mb-2">Our Story</h2>
|
||||||
|
<p class="text-gray-600 mb-4">
|
||||||
|
DocuNova was created with one goal in mind: to simplify and streamline document management for everyone, whether you’re a small startup or a large enterprise. Tired of clunky, outdated systems, we set out to design a platform that is intuitive, flexible, and packed with powerful features.
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-600">
|
||||||
|
We harness the power of OpenAI for metadata extraction and text refinement, integrate seamlessly with Dropbox, Nextcloud, and Paperless NGX for storage and indexing, leverage Azure Document Intelligence for OCR, and even use Gotenberg for file-to-PDF conversions. And while we’ve implemented AWS S3 for now, we’re always evolving!
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Key Features Section -->
|
||||||
|
<section class="bg-white shadow rounded p-6 mb-8">
|
||||||
|
<h2 class="text-2xl font-semibold mb-2">Key Features</h2>
|
||||||
|
<ul class="list-disc list-inside text-gray-600">
|
||||||
|
<li>Simple and secure file uploads with drag & drop support</li>
|
||||||
|
<li>Automated metadata extraction, indexing, and version control</li>
|
||||||
|
<li>Integration with popular cloud services and storage platforms</li>
|
||||||
|
<li>OCR and intelligent document processing powered by AI</li>
|
||||||
|
<li>IMAP integration for automated document fetching</li>
|
||||||
|
<li>Highly configurable via environment variables for custom workflows</li>
|
||||||
|
<li>Docker-ready for easy deployment and scalability</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Meet the Creator Section -->
|
||||||
|
<section class="bg-white shadow rounded p-6 mb-8">
|
||||||
|
<h2 class="text-2xl font-semibold mb-2">Meet the Creator</h2>
|
||||||
|
<p class="text-gray-600">
|
||||||
|
DocuNova is passionately developed by <a href="https://christianlouis.de" target="_blank" class="text-blue-600 hover:underline">Christian Krakau-Louis</a>, a visionary committed to solving real-world challenges with innovative technology. His dedication and expertise ensure that every feature in DocuNova is crafted with you in mind.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- GitHub Link Section -->
|
||||||
|
<section class="bg-white shadow rounded p-6">
|
||||||
|
<h2 class="text-2xl font-semibold mb-2">Get Involved</h2>
|
||||||
|
<p class="text-gray-600 mb-4">
|
||||||
|
Want to dive into the code, contribute ideas, or simply check out the magic behind DocuNova? Visit our GitHub repository to see the project in action!
|
||||||
|
</p>
|
||||||
|
<a href="https://github.com/christianlouis/document-processor" target="_blank" class="flex items-center space-x-2 text-blue-600 hover:text-blue-800">
|
||||||
|
<img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" alt="GitHub Logo" class="h-6 w-6" />
|
||||||
|
<span>View DocuNova on GitHub</span>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="bg-white shadow">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 py-4 text-center text-gray-600">
|
||||||
|
© 2025 DocuNova. All rights reserved.
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- JavaScript for dynamic auth section -->
|
||||||
|
<script>
|
||||||
|
(async function checkAuth() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch("/api/whoami");
|
||||||
|
if (resp.ok) {
|
||||||
|
const data = await resp.json();
|
||||||
|
document.getElementById("authSection").innerHTML = `
|
||||||
|
<img src="${data.picture}" alt="User Avatar" class="inline-block h-8 w-8 rounded-full mr-2">
|
||||||
|
Logged in as <strong>${data.email}</strong>
|
||||||
|
<a href="/logout" class="ml-4 text-blue-600 hover:text-blue-800">Logout</a>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
document.getElementById("authSection").innerHTML =
|
||||||
|
`<a href="/login" class="text-blue-600">Login</a>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById("authSection").innerHTML =
|
||||||
|
`<a href="/login" class="text-blue-600">Login</a>`;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+44
-98
@@ -2,141 +2,87 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Document Processor - Upload</title>
|
<title>DocuNova - Intelligent Document Processing</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<!-- Tailwind via CDN or your own build -->
|
<!-- Tailwind CSS via CDN -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-50 h-screen flex flex-col">
|
<body class="bg-gray-50 min-h-screen flex flex-col">
|
||||||
|
|
||||||
<!-- Navigation Bar -->
|
<!-- Navigation Bar -->
|
||||||
<nav class="bg-white shadow">
|
<nav class="bg-white shadow">
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between h-16 items-center">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between h-16 items-center">
|
||||||
<!-- Brand -->
|
<!-- Brand -->
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<a href="/" class="text-xl font-bold text-blue-500 hover:text-blue-700">
|
<a href="/" class="text-xl font-bold text-blue-500 hover:text-blue-700">
|
||||||
Document Processor
|
DocuNova
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<!-- Menu items on the right -->
|
<!-- Menu items -->
|
||||||
<div class="flex space-x-4 items-center">
|
<div class="flex space-x-4 items-center">
|
||||||
<!-- Example link to /upload (current page), or other pages -->
|
<a href="/" class="text-gray-700 hover:text-gray-900">Home</a>
|
||||||
<a href="/upload" class="text-gray-700 hover:text-gray-900">
|
<a href="/upload" class="text-gray-700 hover:text-gray-900">Upload</a>
|
||||||
Upload
|
<a href="/about" class="text-gray-700 hover:text-gray-900">About</a>
|
||||||
</a>
|
<a href="/files" class="text-gray-700 hover:text-gray-900">Files</a>
|
||||||
<!-- Placeholder for future pages -->
|
<!-- Dynamic auth section -->
|
||||||
<a href="/some-other" class="text-gray-700 hover:text-gray-900">
|
|
||||||
Some Page
|
|
||||||
</a>
|
|
||||||
<!-- Dynamic login/logout section -->
|
|
||||||
<div id="authSection" class="text-gray-700 hover:text-gray-900"></div>
|
<div id="authSection" class="text-gray-700 hover:text-gray-900"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Main content area -->
|
<!-- Main Content Area -->
|
||||||
<main class="flex-grow flex flex-col items-center justify-center p-8">
|
<main class="flex-grow container mx-auto px-4 py-8">
|
||||||
<h1 class="text-3xl font-bold mb-8">Upload a File</h1>
|
<h1 class="text-4xl font-bold mb-4">Welcome to DocuNova</h1>
|
||||||
|
<p class="text-gray-700 mb-8">
|
||||||
<div
|
Your intelligent solution for processing, managing, and organizing documents effortlessly.
|
||||||
id="dropZone"
|
</p>
|
||||||
class="border-4 border-dashed border-gray-300 rounded-lg p-8 bg-white text-center w-full max-w-lg"
|
<!-- Sample Sections -->
|
||||||
ondrop="handleDrop(event)"
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
ondragover="handleDragOver(event)"
|
<div class="bg-white shadow rounded p-6">
|
||||||
>
|
<h2 class="text-2xl font-semibold mb-2">Upload Documents</h2>
|
||||||
<p class="text-gray-500 mb-4">
|
<p class="text-gray-600">
|
||||||
Drag & drop a file here, or click to select a file.
|
Quickly upload and process your files with our user-friendly interface.
|
||||||
</p>
|
</p>
|
||||||
<input
|
<a href="/upload" class="mt-4 inline-block text-blue-600 hover:text-blue-800">
|
||||||
id="fileInput"
|
Get Started →
|
||||||
type="file"
|
</a>
|
||||||
class="hidden"
|
</div>
|
||||||
onchange="handleFileSelect(event)"
|
<div class="bg-white shadow rounded p-6">
|
||||||
/>
|
<h2 class="text-2xl font-semibold mb-2">Manage Your Files</h2>
|
||||||
|
<p class="text-gray-600">
|
||||||
|
View, organize, and collaborate on your processed files in one central hub.
|
||||||
|
</p>
|
||||||
|
<a href="/files" class="mt-4 inline-block text-blue-600 hover:text-blue-800">
|
||||||
|
View Files →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="statusMessage" class="mt-4 text-gray-700"></div>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Footer (optional) -->
|
<!-- Footer -->
|
||||||
<footer class="bg-white shadow mt-auto">
|
<footer class="bg-white shadow">
|
||||||
<div class="max-w-7xl mx-auto px-4 py-4 text-center text-gray-600">
|
<div class="max-w-7xl mx-auto px-4 py-4 text-center text-gray-600">
|
||||||
© 2025 Document Processor - All rights reserved.
|
© 2025 DocuNova. All rights reserved.
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- JavaScript -->
|
<!-- JavaScript for dynamic auth section -->
|
||||||
<script>
|
<script>
|
||||||
// 1) Drag-and-drop / file input logic
|
|
||||||
const dropZone = document.getElementById("dropZone");
|
|
||||||
const fileInput = document.getElementById("fileInput");
|
|
||||||
const statusMessage = document.getElementById("statusMessage");
|
|
||||||
|
|
||||||
dropZone.addEventListener("click", () => fileInput.click());
|
|
||||||
|
|
||||||
function handleDragOver(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.dataTransfer.dropEffect = "copy";
|
|
||||||
dropZone.classList.add("bg-gray-100");
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDrop(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
dropZone.classList.remove("bg-gray-100");
|
|
||||||
if (e.dataTransfer.files.length) {
|
|
||||||
uploadFile(e.dataTransfer.files[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFileSelect(e) {
|
|
||||||
if (e.target.files.length) {
|
|
||||||
uploadFile(e.target.files[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function uploadFile(file) {
|
|
||||||
statusMessage.textContent = `Uploading ${file.name}...`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append("file", file);
|
|
||||||
|
|
||||||
const response = await fetch("/ui-upload", {
|
|
||||||
method: "POST",
|
|
||||||
body: formData,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Upload failed with status ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
statusMessage.textContent = `File ${file.name} uploaded. Task ID: ${result.task_id}`;
|
|
||||||
} catch (err) {
|
|
||||||
statusMessage.textContent = `Error: ${err}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Example dynamic auth check: if you have an endpoint like /api/whoami
|
|
||||||
// that returns JSON with { email: "..."} if logged in, or 401 if not.
|
|
||||||
(async function checkAuth() {
|
(async function checkAuth() {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch("/api/whoami");
|
const resp = await fetch("/api/whoami");
|
||||||
if (resp.ok) {
|
if (resp.ok) {
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
// data.picture is the Gravatar URL
|
// data.picture is expected to be a Gravatar URL
|
||||||
document.getElementById("authSection").innerHTML = `
|
document.getElementById("authSection").innerHTML = `
|
||||||
<img src="${data.picture}" alt="User Avatar" class="inline-block h-8 w-8 rounded-full mr-2">
|
<img src="${data.picture}" alt="User Avatar" class="inline-block h-8 w-8 rounded-full mr-2">
|
||||||
Logged in as <strong>${data.email}</strong>
|
Logged in as <strong>${data.email}</strong>
|
||||||
<a href="/logout" class="ml-4 text-blue-600 hover:text-blue-800">Logout</a>
|
<a href="/logout" class="ml-4 text-blue-600 hover:text-blue-800">Logout</a>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
// If 401 or other non-OK status => show Login link
|
|
||||||
document.getElementById("authSection").innerHTML =
|
document.getElementById("authSection").innerHTML =
|
||||||
`<a href="/login" class="text-blue-600">Login</a>`;
|
`<a href="/login" class="text-blue-600">Login</a>`;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If fetch fails, also show login
|
|
||||||
document.getElementById("authSection").innerHTML =
|
document.getElementById("authSection").innerHTML =
|
||||||
`<a href="/login" class="text-blue-600">Login</a>`;
|
`<a href="/login" class="text-blue-600">Login</a>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>DocuNova - Upload</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!-- Tailwind CSS via CDN -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50 min-h-screen flex flex-col">
|
||||||
|
<!-- Navigation Bar -->
|
||||||
|
<nav class="bg-white shadow">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between h-16 items-center">
|
||||||
|
<!-- Brand -->
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<a href="/" class="text-xl font-bold text-blue-500 hover:text-blue-700">
|
||||||
|
DocuNova
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<!-- Menu Items -->
|
||||||
|
<div class="flex space-x-4 items-center">
|
||||||
|
<a href="/" class="text-gray-700 hover:text-gray-900">Home</a>
|
||||||
|
<a href="/upload" class="text-gray-700 hover:text-gray-900">Upload</a>
|
||||||
|
<a href="/about" class="text-gray-700 hover:text-gray-900">About</a>
|
||||||
|
<a href="/files" class="text-gray-700 hover:text-gray-900">Files</a>
|
||||||
|
<!-- Dynamic Auth Section -->
|
||||||
|
<div id="authSection" class="text-gray-700 hover:text-gray-900"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content Area -->
|
||||||
|
<main class="flex-grow flex flex-col items-center justify-center p-8">
|
||||||
|
<h1 class="text-3xl font-bold mb-8">Upload a File</h1>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="dropZone"
|
||||||
|
class="border-4 border-dashed border-gray-300 rounded-lg p-8 bg-white text-center w-full max-w-lg"
|
||||||
|
ondrop="handleDrop(event)"
|
||||||
|
ondragover="handleDragOver(event)"
|
||||||
|
>
|
||||||
|
<p class="text-gray-500 mb-4">
|
||||||
|
Drag & drop a file here, or click to select a file.
|
||||||
|
</p>
|
||||||
|
<input
|
||||||
|
id="fileInput"
|
||||||
|
type="file"
|
||||||
|
class="hidden"
|
||||||
|
onchange="handleFileSelect(event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="statusMessage" class="mt-4 text-gray-700"></div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="bg-white shadow mt-auto">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 py-4 text-center text-gray-600">
|
||||||
|
© 2025 DocuNova. All rights reserved.
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- JavaScript -->
|
||||||
|
<script>
|
||||||
|
// Drag-and-drop / file input logic
|
||||||
|
const dropZone = document.getElementById("dropZone");
|
||||||
|
const fileInput = document.getElementById("fileInput");
|
||||||
|
const statusMessage = document.getElementById("statusMessage");
|
||||||
|
|
||||||
|
dropZone.addEventListener("click", () => fileInput.click());
|
||||||
|
|
||||||
|
function handleDragOver(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.dataTransfer.dropEffect = "copy";
|
||||||
|
dropZone.classList.add("bg-gray-100");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDrop(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
dropZone.classList.remove("bg-gray-100");
|
||||||
|
if (e.dataTransfer.files.length) {
|
||||||
|
uploadFile(e.dataTransfer.files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileSelect(e) {
|
||||||
|
if (e.target.files.length) {
|
||||||
|
uploadFile(e.target.files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadFile(file) {
|
||||||
|
statusMessage.textContent = `Uploading ${file.name}...`;
|
||||||
|
try {
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
const response = await fetch("/ui-upload", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Upload failed with status ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
statusMessage.textContent = `File ${file.name} uploaded. Task ID: ${result.task_id}`;
|
||||||
|
} catch (err) {
|
||||||
|
statusMessage.textContent = `Error: ${err}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic auth check using /api/whoami
|
||||||
|
(async function checkAuth() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch("/api/whoami");
|
||||||
|
if (resp.ok) {
|
||||||
|
const data = await resp.json();
|
||||||
|
document.getElementById("authSection").innerHTML = `
|
||||||
|
<img src="${data.picture}" alt="User Avatar" class="inline-block h-8 w-8 rounded-full mr-2">
|
||||||
|
Logged in as <strong>${data.email}</strong>
|
||||||
|
<a href="/logout" class="ml-4 text-blue-600 hover:text-blue-800">Logout</a>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
document.getElementById("authSection").innerHTML =
|
||||||
|
`<a href="/login" class="text-blue-600">Login</a>`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById("authSection").innerHTML =
|
||||||
|
`<a href="/login" class="text-blue-600">Login</a>`;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user