Merge pull request #177 from abneeeees/improvement/header-and-footer

feat(header and footer): Enhance header and footer
This commit is contained in:
Zeecka
2026-01-18 14:12:43 +01:00
committed by GitHub
11 changed files with 451 additions and 426 deletions
+1
View File
@@ -2,6 +2,7 @@
WEB_APP_PORT=5000
# Application configuration
PROJECT_VERSION=3.1.2
MAX_CONTENT_LENGTH=3145728
MAX_PENDING_TIME=600
MAX_STORE_TIME=259200
+1
View File
@@ -99,6 +99,7 @@ Thanks to contributors:
- [Zeecka](https://www.zeecka.fr/) - **(author)**
- [aradhyacp](https://github.com/aradhyacp)
- [Philip Zimmermann](https://github.com/Philip-Zimmermann)
- [abneeeees](https://github.com/abneeeees)
Thanks to the open-source community:
[binwalk](https://github.com/ReFirmLabs/binwalk), [exiftool](https://exiftool.org/), [GraphicsMagick identify](http://www.graphicsmagick.org/identify.html), [foremost](https://foremost.sourceforge.net/), [openstego](https://www.openstego.com/), [outguess](https://www.rbcafe.com/software/outguess/), [pngcheck](https://www.libpng.org/pub/png/apps/pngcheck.html), [steghide](https://steghide.sourceforge.net/), [strings](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/strings.html), [zsteg](https://github.com/zed-0xff/zsteg), ...
+6
View File
@@ -36,11 +36,17 @@ def create_app() -> Flask:
app = Flask(__name__)
app.json.sort_keys = False # type: ignore
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DB_URI")
app.config["PROJECT_VERSION"] = os.getenv("PROJECT_VERSION")
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["MAX_CONTENT_LENGTH"] = int(os.environ.get("MAX_CONTENT_LENGTH", 1024 * 1024))
app.config["REDIS_QUEUE"] = Queue(connection=Redis(host="redis", port=6379))
db.init_app(app)
@app.context_processor
def inject_datetime() -> dict[str, Any]:
"""Inject datetime into all templates."""
return {"datetime": datetime}
@app.errorhandler(413)
def too_large(_: Any) -> tuple[Response, int]:
"""Error Handler for Max File Size."""
File diff suppressed because it is too large Load Diff
+13 -14
View File
@@ -105,11 +105,12 @@ document.addEventListener('click', function (event) {
// Check if the clicked element is a copy icon
if (event.target.classList.contains('copy-icon')) {
const icon = event.target;
const textarea = icon.closest('.textarea-container').querySelector('textarea');
const codeContainer = icon.closest('.code-container');
const codeBlock = codeContainer ? codeContainer.querySelector('code') : null;
if (textarea) {
textarea.select();
navigator.clipboard.writeText(textarea.value).then(() => {
if (codeBlock) {
const textToCopy = codeBlock.textContent;
navigator.clipboard.writeText(textToCopy).then(() => {
icon.classList.remove('fa-copy');
icon.classList.add('fa-check');
setTimeout(() => {
@@ -487,16 +488,13 @@ function parseResult(result) {
analyzer.innerHTML += `<div class="alert alert-success" role="alert">${output}</div>`;
} else if (Array.isArray(result[tool]["output"])) {
if (result[tool]["output"].length > 0) {
var texarea_content = `<div class="textarea-container">`;
texarea_content += `<textarea class="form-control w-100 mb-2" rows="8" readonly>`;
for (const line of result[tool]["output"]) {
texarea_content += escapeHtml(`${line}\n`);
}
texarea_content = texarea_content.trim();
texarea_content += `</textarea>`;
texarea_content += `<i class="fas fa-copy copy-icon"></i>`;
texarea_content += `</div>`;
analyzer.innerHTML += texarea_content;
var code_content = `<div class="code-container position-relative mb-2">`;
code_content += `<pre class="mb-0"><code>`;
code_content += result[tool]["output"].map(line => escapeHtml(line)).join('\n').trim();
code_content += `</code></pre>`;
code_content += `<i class="fas fa-copy copy-icon"></i>`;
code_content += `</div>`;
analyzer.innerHTML += code_content;
}
} else if (typeof result[tool]["output"] === "object") {
var table_content = `<div class="table-container">`;
@@ -739,3 +737,4 @@ if (browseBtn) {
xhr.send(formData);
});
}
+33 -16
View File
@@ -2,30 +2,48 @@
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Aperi'Solve</title>
<link rel="icon" type="image/svg+xml" href="/static/img/logo.svg" />
<link href="https://fonts.googleapis.com/css2?family=Fira+Code&display=swap" rel="stylesheet" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="description" content="Aperi'Solve - Online steganography analysis platform for images">
<title>Aperi'Solve - Steganography Analysis</title>
<link rel="icon" type="image/svg+xml" href="/static/img/logo.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="/static/css/aperisolve.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/aperisolve.css" rel="stylesheet">
</head>
<body class="text-light">
<body class="d-flex flex-column min-vh-100 text-light">
<div id="three-bg"></div>
<div class="container mt-5">
<a class="title_link" href="/">
<h1 class="mb-4"><img id="water-logo" src="/static/img/logo.svg" /> Aperi'Solve</h1>
</a>
{% block body_content %} {% endblock %}
</div>
<!-- Main content area -->
<main class="flex-grow-1">
<div class="container mt-5">
<header class="site-header mb-4">
<a class="title-link d-inline-flex align-items-center text-decoration-none" href="/" aria-label="Aperi'Solve Home">
<img src="/static/img/logo.svg" alt="Aperi'Solve logo" class="logo-img me-3" width="80" height="80">
<div class="d-flex flex-column">
<span class="title-text">Aperi'Solve</span>
<span class="title-version">v.{{ config["PROJECT_VERSION"] }}</span>
</div>
</a>
</header>
{% block body_content %} {% endblock %}
</div>
</main>
<!-- Footer -->
{% include "footer.html" %}
<!-- Modal Structure -->
<div id="image-modal-container">
<img id="modal-image" src="" />
</div>
<!-- Scripts -->
<script src="/static/js/aperisolve.js"></script>
<!-- Background -->
<script type="importmap">
{
"imports": {
@@ -36,7 +54,6 @@
</script>
<script type="module" src="/static/js/background.js"></script>
{% block footer_content %} {% endblock %}
{% include "footer.html" %}
</body>
</html>
+4 -5
View File
@@ -1,8 +1,7 @@
{% extends 'base.html' %}
{% block body_content %}
<div id="httperror">
<h1>{{ statuscode }}</h1>
<br>
<h2>{{ message }}</h2>
</div>
<section id="httperror" class="text-center py-5">
<h1 class="display-1 mb-4">{{ statuscode }}</h1>
<h2 class="h3 text-light-emphasis">{{ message }}</h2>
</section>
{% endblock %}
+20 -17
View File
@@ -1,23 +1,26 @@
<footer class="d-flex justify-content-center gap-4 my-4">
<div>
<a href="https://buymeacoffee.com/aperisolve" title="Coffee" class="text-light text-decoration-none">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" id="coffee" />
<footer class="mt-auto py-3 px-3">
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<!-- Copyright -->
<div class="footer-text">
Aperi'Solve (v.{{ config["PROJECT_VERSION"] }}) © {{ datetime.today().year }}
</div>
<!-- Links -->
<div class="d-flex align-items-center gap-3 flex-wrap">
<a href="https://buymeacoffee.com/aperisolve" title="Buy Me A Coffee" class="footer-link">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" class="footer-icon-img" />
</a>
</div>
<div>
<a href="/faq" title="FAQ" class="text-light text-decoration-none">
<i class="fa-regular fa-circle-question"></i>
<a href="/faq" title="FAQ" class="footer-link">
<i class="fa-regular fa-circle-question fa-lg"></i>
</a>
</div>
<div>
<a href="https://github.com/Zeecka/AperiSolve" target="_blank" title="GitHub"
class="text-light text-decoration-none">
<i class="fa-brands fa-github"></i>
<a href="https://github.com/Zeecka/AperiSolve" target="_blank" rel="noopener" title="GitHub" class="footer-link">
<i class="fa-brands fa-github fa-lg"></i>
</a>
</div>
<div>
<a href="https://x.com/Zeecka_" target="_blank" title="Twitter" class="text-light text-decoration-none">
<i class="fa-brands fa-x-twitter"></i>
<a href="https://x.com/Zeecka_" target="_blank" rel="noopener" title="Twitter" class="footer-link">
<i class="fa-brands fa-x-twitter fa-lg"></i>
</a>
</div>
</div>
</div>
</footer>
+45 -25
View File
@@ -1,39 +1,59 @@
{% extends 'base.html' %}
{% block body_content %}
<form id="upload-form" class="card p-4" method="POST" enctype="multipart/form-data">
<form id="upload-form" class="card p-4 mb-4" method="POST" enctype="multipart/form-data">
<!-- Description Section -->
<section class="mb-4">
<h2 class="h4 mb-3">What is this?</h2>
<p class="text-light-emphasis mb-0">
Aperi'Solve is an online platform which performs layer analysis on images.
The platform also uses zsteg, steghide, outguess, exiftool, binwalk, foremost and strings
for deeper steganography analysis. Supports: PNG, JPG, GIF, BMP, JPEG, JFIF, JPE, TIFF...
</p>
</section>
<!-- Drop Zone -->
<div class="mb-3">
<div>
<h2>What is this ?</h2>
<p>Aperi'Solve is an online platform which performs layer analysis on image. The platform also uses zsteg,
steghide, outguess, exiftool, binwalk, foremost and strings for deeper steganography analysis. The platform
supports the following images format: .png, .jpg, .gif, .bmp, .jpeg, .jfif, .jpe, .tiff...</p>
</div>
<div id="drop-zone" class="border border-secondary rounded p-4 text-center mb-3">
<p id="drag-msg">📂 Drag and drop an image here or click to select</p>
<input type="file" id="image" name="image" accept="image/*" hidden required />
<button type="button" class="btn btn-secondary mt-2" id="browse-btn">
Choose an image
<label for="image" class="form-label visually-hidden">Image file</label>
<div id="drop-zone" class="drop-zone border border-2 border-dashed rounded-3 p-4 text-center">
<p id="drag-msg" class="mb-3">📂 Drag and drop an image here or click to select</p>
<input type="file" id="image" name="image" accept="image/*" class="visually-hidden" required>
<button type="button" class="btn btn-secondary" id="browse-btn" aria-label="Choose image file">
<i class="fa fa-folder-open me-2"></i>Choose an image
</button>
</div>
</div>
<!-- Password Field -->
<div class="mb-3">
<label for="password" class="form-label">Password (optional)</label>
<input class="form-control" type="text" name="password" id="password" />
<label for="password" class="form-label">Password <small class="text-muted">(optional)</small></label>
<input class="form-control" type="text" name="password" id="password" placeholder="Enter password if required">
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="deep" />
<label class="form-check-label" for="deep">Deep analysis</label>
<!-- Deep Analysis Toggle -->
<div class="form-check form-switch mb-4">
<input class="form-check-input" type="checkbox" role="switch" id="deep">
<label class="form-check-label" for="deep">
Deep analysis <small class="text-muted">(slower, more thorough)</small>
</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<div class="progress mt-3 d-none" id="progress-container">
<div class="progress-bar" id="progress-bar" role="progress-bar" style="width: 0%">
<!-- Submit Button -->
<button type="submit" class="btn btn-primary btn-lg w-100">
<i class="fa fa-upload me-2"></i>Analyze Image
</button>
<!-- Progress Bar -->
<div class="progress mt-3 d-none" id="progress-container" role="progressbar" aria-label="Upload progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" id="progress-bar" style="width: 0%">
0%
</div>
</div>
</form>
<div id="result" class="mt-5">
<div id="result-infos" class="mb-3"></div>
<div id="result-analyzers" class="mb-3"></div>
<div id="result-removal" class="mb-3"></div>
</div>
<!-- Results Section -->
<section id="result" class="results-section pb-4">
<div id="result-infos" class="mb-4"></div>
<div id="result-analyzers" class="mb-4"></div>
<div id="result-removal" class="mb-4"></div>
</section>
{% endblock %}
+5 -5
View File
@@ -1,10 +1,10 @@
{% extends 'base.html' %}
{% block body_content %}
<div id="result" class="mt-5">
<div id="result-infos" class="mb-3"></div>
<div id="result-analyzers" class="mb-3"></div>
<div id="result-removal" class="mb-3"></div>
</div>
<section id="result" class="results-section">
<div id="result-infos" class="mb-4"></div>
<div id="result-analyzers" class="mb-4"></div>
<div id="result-removal" class="mb-4"></div>
</section>
{% endblock %}
{% block footer_content %}
<script>
+12 -7
View File
@@ -1,10 +1,15 @@
{% extends 'base.html' %}
{% block body_content %}
<div id="result" class="mt-5">
{% for img in images %}<!--
--><div class="browse-images">
<a href="{{ img.link }}" target="_blank"><img src="{{ img.file }}"/></a>
</div><!--
-->{% endfor %}
</div>
<section id="result" class="results-section">
<h2 class="h3 mb-4">Browse Submissions</h2>
<div class="row g-3">
{% for img in images %}
<div class="col-6 col-md-4 col-lg-3">
<a href="{{ img.link }}" target="_blank" class="d-block browse-image-link">
<img src="{{ img.file }}" alt="Submission thumbnail" class="img-fluid rounded" loading="lazy">
</a>
</div>
{% endfor %}
</div>
</section>
{% endblock %}