Compare commits

2 Commits

Author SHA1 Message Date
hiep200311 adc1934e9c Refactor static serving: use Blueprint static_folder, inline header
- Disable Flask default /static/ folder (static_folder=None in app.py)
- Add headerRoute blueprint to serve views/header/ at /assets/header/
- Configure transcription and scraper blueprints with static_folder
  pointing to their respective views/ subfolders
- Inline header HTML directly in headerLoader.js (remove fetch call)
- Update all HTML templates to use new /assets/... URLs
2026-06-24 16:22:31 +07:00
hiep200311 9f7824825c Move CSS/JS assets from static/ into views/ feature folders
Co-locate each feature's CSS and JS alongside its HTML template.
Remove the standalone static/ directory entirely.
2026-06-24 16:22:14 +07:00
15 changed files with 119 additions and 130 deletions
+4 -1
View File
@@ -3,10 +3,12 @@ from flask import Flask
from config import Config
from routes.transcriptionRoute import transcription_bp
from routes.scraperZLibraryRoute import scraperZLibraryRoute
from routes.headerRoute import header_bp
def create_app():
"""Application factory function"""
app = Flask(__name__, template_folder='views')
# static_folder=None → tắt hoàn toàn thư mục /static mặc định của Flask
app = Flask(__name__, template_folder='views', static_folder=None)
# Load configuration
app.config.from_object(Config)
@@ -18,6 +20,7 @@ def create_app():
Config.init_app()
# Register blueprints
app.register_blueprint(header_bp)
app.register_blueprint(transcription_bp)
app.register_blueprint(scraperZLibraryRoute, url_prefix='/scraperZLibrary')
+11
View File
@@ -0,0 +1,11 @@
# routes/headerRoute.py
from flask import Blueprint
# Blueprint để serve headerLoader.js từ views/header/
# → URL: /assets/header/headerLoader.js
header_bp = Blueprint(
'header',
__name__,
static_folder='../views/header',
static_url_path='/assets/header',
)
+30 -23
View File
@@ -1,23 +1,30 @@
# routes/scraperZLibraryRoute.py
from flask import Blueprint
from controllers.scraperZLibraryController import ScraperController
# Create blueprint
scraperZLibraryRoute = Blueprint('scraperZLibrary', __name__)
# Define routes
scraperZLibraryRoute.route('/', methods=['GET'])(ScraperController.index)
scraperZLibraryRoute.route('/api/search', methods=['POST'])(ScraperController.search_books)
scraperZLibraryRoute.route('/api/download/txt', methods=['POST'])(ScraperController.download_txt)
scraperZLibraryRoute.route('/api/download/json', methods=['POST'])(ScraperController.download_json)
scraperZLibraryRoute.route('/api/download/books', methods=['POST'])(ScraperController.download_books_zip) # New route
scraperZLibraryRoute.add_url_rule(
'/api/download/stream',
view_func=ScraperController.download_books_stream,
methods=['POST'],
)
scraperZLibraryRoute.add_url_rule(
'/api/download/file/<token>',
view_func=ScraperController.fetch_stored_file,
methods=['GET'],
)
# routes/scraperZLibraryRoute.py
from flask import Blueprint
from controllers.scraperZLibraryController import ScraperController
# Blueprint với static_folder trỏ vào views/scraperZLibraryPages
# → Flask serve CSS/JS qua URL /scraperZLibrary/assets/scraper/...
# Lưu ý: static_url_path chỉ cần phần sau /scraperZLibrary/ (url_prefix đã được thêm khi register)
scraperZLibraryRoute = Blueprint(
'scraperZLibrary',
__name__,
static_folder='../views/scraperZLibraryPages',
static_url_path='/assets/scraper',
)
# Define routes
scraperZLibraryRoute.route('/', methods=['GET'])(ScraperController.index)
scraperZLibraryRoute.route('/api/search', methods=['POST'])(ScraperController.search_books)
scraperZLibraryRoute.route('/api/download/txt', methods=['POST'])(ScraperController.download_txt)
scraperZLibraryRoute.route('/api/download/json', methods=['POST'])(ScraperController.download_json)
scraperZLibraryRoute.route('/api/download/books', methods=['POST'])(ScraperController.download_books_zip) # New route
scraperZLibraryRoute.add_url_rule(
'/api/download/stream',
view_func=ScraperController.download_books_stream,
methods=['POST'],
)
scraperZLibraryRoute.add_url_rule(
'/api/download/file/<token>',
view_func=ScraperController.fetch_stored_file,
methods=['GET'],
)
+20 -14
View File
@@ -1,14 +1,20 @@
# routes/transcriptionRoute.py
from flask import Blueprint
from controllers.transcriptionController import TranscriptionController
# Create blueprint
transcription_bp = Blueprint('transcription', __name__)
# Define routes
transcription_bp.route('/', methods=['GET'])(TranscriptionController.index)
transcription_bp.route('/upload-chunk', methods=['POST'])(TranscriptionController.upload_chunk)
transcription_bp.route('/status/<job_id>', methods=['GET'])(TranscriptionController.status)
transcription_bp.route('/transcript/<job_id>', methods=['GET'])(TranscriptionController.download_transcript)
transcription_bp.route('/view/<job_id>', methods=['GET'])(TranscriptionController.view_transcript)
transcription_bp.route('/jobs', methods=['GET'])(TranscriptionController.list_jobs)
# routes/transcriptionRoute.py
from flask import Blueprint
from controllers.transcriptionController import TranscriptionController
# Blueprint với static_folder trỏ vào views/transcriptionPages
# → Flask serve CSS/JS qua URL /assets/transcription/...
transcription_bp = Blueprint(
'transcription',
__name__,
static_folder='../views/transcriptionPages',
static_url_path='/assets/transcription',
)
# Define routes
transcription_bp.route('/', methods=['GET'])(TranscriptionController.index)
transcription_bp.route('/upload-chunk', methods=['POST'])(TranscriptionController.upload_chunk)
transcription_bp.route('/status/<job_id>', methods=['GET'])(TranscriptionController.status)
transcription_bp.route('/transcript/<job_id>', methods=['GET'])(TranscriptionController.download_transcript)
transcription_bp.route('/view/<job_id>', methods=['GET'])(TranscriptionController.view_transcript)
transcription_bp.route('/jobs', methods=['GET'])(TranscriptionController.list_jobs)
-28
View File
@@ -1,28 +0,0 @@
<!-- static/header/header.html -->
<header class="app-header">
<nav class="header-nav">
<div class="nav-container">
<!-- Left: Brand -->
<div class="nav-left">
<a href="/" class="nav-brand">KYLONG.</a>
</div>
<!-- Center: Navigation -->
<div class="nav-center">
<ul class="nav-links">
<li>
<a href="/" class="nav-link" data-nav="transcription">Phiên Âm</a>
</li>
<li>
<a href="/scraperZLibrary" class="nav-link" data-nav="scraper">Z-Library</a>
</li>
</ul>
</div>
<!-- Right: Utility -->
<div class="nav-right">
<span class="status-badge"><span class="dot"></span> Online</span>
</div>
</div>
</nav>
</header>
@@ -1,16 +1,45 @@
// static/header/headerLoader.js
// views/header/headerLoader.js
(function () {
"use strict";
// Configuration
const CONFIG = {
headerPath: "/static/header/header.html",
insertPosition: "afterbegin",
containerSelector: "body",
activeClass: "active",
version: "1.0",
version: "1.1",
};
// Header HTML inlined — không cần fetch file nữa
const headerHTML = `
<header class="app-header">
<nav class="header-nav">
<div class="nav-container">
<!-- Left: Brand -->
<div class="nav-left">
<a href="/" class="nav-brand">KYLONG.</a>
</div>
<!-- Center: Navigation -->
<div class="nav-center">
<ul class="nav-links">
<li>
<a href="/" class="nav-link" data-nav="transcription">Phiên Âm</a>
</li>
<li>
<a href="/scraperZLibrary" class="nav-link" data-nav="scraper">Z-Library</a>
</li>
</ul>
</div>
<!-- Right: Utility -->
<div class="nav-right">
<span class="status-badge"><span class="dot"></span> Online</span>
</div>
</div>
</nav>
</header>`;
const styles = `
<style id="header-component-styles">
@import url('https://db.onlinewebfonts.com/c/5ac3fe7c6abd2f62067f266d89671492?family=HelveticaNowDisplay-Medium');
@@ -156,37 +185,21 @@
</style>
`;
// Load header function
async function loadHeader() {
try {
// Fetch header HTML
const response = await fetch(`${CONFIG.headerPath}?v=${CONFIG.version}`);
if (!response.ok) {
throw new Error(`Failed to load header: ${response.status}`);
}
const headerHTML = await response.text();
// Inject styles if not already present
if (!document.getElementById("header-component-styles")) {
document.head.insertAdjacentHTML("beforeend", styles);
}
// Insert header
const container = document.querySelector(CONFIG.containerSelector);
container.insertAdjacentHTML(CONFIG.insertPosition, headerHTML);
// Highlight active link
highlightActiveLink();
console.log("✅ Header component loaded v" + CONFIG.version);
} catch (error) {
console.error("❌ Error loading header:", error);
createFallbackHeader();
// Inject header directly (no fetch needed)
function loadHeader() {
// Inject styles if not already present
if (!document.getElementById("header-component-styles")) {
document.head.insertAdjacentHTML("beforeend", styles);
}
// Insert header HTML inline
const container = document.querySelector(CONFIG.containerSelector);
container.insertAdjacentHTML(CONFIG.insertPosition, headerHTML);
// Highlight active link
highlightActiveLink();
console.log("✅ Header component loaded v" + CONFIG.version);
}
// Highlight current page link
@@ -212,29 +225,6 @@
});
}
// Fallback header in case of error
function createFallbackHeader() {
const fallback = document.createElement("div");
fallback.style.cssText = `
padding: 15px 20px;
background: #fff;
border-bottom: 1px solid #e0e0e0;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
`;
fallback.innerHTML = `
<div style="max-width: 1200px; margin: 0 auto; display: flex; gap: 30px;">
<a href="/" style="color: #111; text-decoration: none;">📝 Phiên Âm</a>
<a href="/scraperZLibrary" style="color: #111; text-decoration: none;">📚 Z-Library Scraper</a>
</div>
`;
document.body.insertAdjacentElement("afterbegin", fallback);
document.body.style.paddingTop = "50px";
}
// Initialize when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", loadHeader);
@@ -7,7 +7,7 @@
<title>Tìm Kiếm Sách Z-Library</title>
<link rel="stylesheet" href="https://db.onlinewebfonts.com/c/5ac3fe7c6abd2f62067f266d89671492?family=HelveticaNowDisplay-Medium" />
<link rel="stylesheet" href="https://db.onlinewebfonts.com/c/1aa3377e489837a26d019bba501e779d?family=HelveticaNowDisplayW01-Rg" />
<link rel="stylesheet" href="/static/scraperZLibraryPages/scraperZLibraryMain.css" />
<link rel="stylesheet" href="/scraperZLibrary/assets/scraper/scraperZLibraryMain.css" />
</head>
<body>
<div class="container">
@@ -73,7 +73,7 @@
</div>
</div>
<script src="/static/scraperZLibraryPages/scraperZLibraryMain.js"></script>
<script src="/static/header/headerLoader.js?v=1.0"></script>
<script src="/scraperZLibrary/assets/scraper/scraperZLibraryMain.js"></script>
<script src="/assets/header/headerLoader.js?v=1.1"></script>
</body>
</html>
@@ -7,7 +7,7 @@
<title>Phiên Âm</title>
<link rel="stylesheet" href="https://db.onlinewebfonts.com/c/5ac3fe7c6abd2f62067f266d89671492?family=HelveticaNowDisplay-Medium" />
<link rel="stylesheet" href="https://db.onlinewebfonts.com/c/1aa3377e489837a26d019bba501e779d?family=HelveticaNowDisplayW01-Rg" />
<link rel="stylesheet" href="/static/transcriptionPages/transcriptionMain.css" />
<link rel="stylesheet" href="/assets/transcription/transcriptionMain.css" />
</head>
<body>
<div class="container">
@@ -105,7 +105,7 @@
</div>
</div>
<script src="/static/transcriptionPages/transcriptionMain.js"></script>
<script src="/static/header/headerLoader.js?v=1.0"></script>
<script src="/assets/transcription/transcriptionMain.js"></script>
<script src="/assets/header/headerLoader.js?v=1.1"></script>
</body>
</html>
@@ -7,7 +7,7 @@
<title>Transcript - {{ filename }}</title>
<link rel="stylesheet" href="https://db.onlinewebfonts.com/c/5ac3fe7c6abd2f62067f266d89671492?family=HelveticaNowDisplay-Medium" />
<link rel="stylesheet" href="https://db.onlinewebfonts.com/c/1aa3377e489837a26d019bba501e779d?family=HelveticaNowDisplayW01-Rg" />
<link rel="stylesheet" href="/static/transcriptionPages/transcriptionReview.css" />
<link rel="stylesheet" href="/assets/transcription/transcriptionReview.css" />
</head>
<body>
<div class="container">
@@ -24,6 +24,6 @@
<div class="transcript">{{ content }}</div>
</div>
<script src="/static/transcriptionPages/transcriptionReview.js"></script>
<script src="/assets/transcription/transcriptionReview.js"></script>
</body>
</html>