Initial Commit

This commit is contained in:
mrkmntal 2025-10-25 15:10:38 -04:00
commit 1d85bf6663
7 changed files with 377 additions and 0 deletions

129
README.md Normal file
View file

@ -0,0 +1,129 @@
# 🧠 MentalNet Funnies
**A lightweight PHP + SQLite web app for hosting MentalNet Original comic strips.**
Built for simplicity, security, and self-hosting — no external dependencies, just pure PHP, HTML, and CSS.
---
## 🚀 Overview
**MentalNet Funnies** is a micro-comic CMS that serves as the home for *Debian Osaka*, *Tux*, *Miku*, and other MentalNet.xyz comics.
Its designed for local hosting on Apache + PHP setups and includes a tiny password-protected admin panel for uploads.
### ✨ Features
* 🖼 Upload, title, and describe new comics via a web form
* 💾 SQLite database — no external DB needed
* 🔐 Password-protected admin area
* 📱 Mobile-friendly front page (`index.php`)
* 🪶 Compact iframe version (`funnies-frame.php`) for embedding
* ⚙️ One-click database initializer (`init_db.php`)
---
## 📁 Directory Structure
```
/var/www/html/funnies/
├── index.php # Main public comic viewer
├── funnies-frame.php # Small-width iframe-friendly viewer
├── uploads/ # Comic image storage
├── mentalnet_funnies.db # SQLite database file
├── admin/
│ ├── auth.php # Login handler
│ ├── config.php # Config & DB setup
│ ├── upload.php # Upload interface
│ ├── init_db.php # Manual DB table initializer
└── README.md # This file
```
---
## ⚙️ Installation
1. **Create directory & set permissions**
```bash
sudo mkdir -p /var/www/html/funnies/uploads
sudo chown -R www-data:www-data /var/www/html/funnies
sudo chmod -R 755 /var/www/html/funnies
```
2. **Set up the admin password**
Generate a bcrypt hash:
```bash
php -r 'echo password_hash("YourStrongPassword", PASSWORD_DEFAULT), PHP_EOL;'
```
Then place that hash inside `admin/config.php` under:
```php
$ADMIN_PASS_HASH = '$2y$10$replace_with_real_hash';
```
3. **Initialize the database**
Visit:
`https://yourdomain.com/funnies/admin/init_db.php`
Or run the init script via command-line:
`php init_db.php`
It will create the `comics` table or confirm if it already exists.
4. **Upload a comic**
Visit:
`https://yourdomain.com/funnies/admin/auth.php`
Log in and use the form to upload images and descriptions.
5. **View your comics**
* Main gallery: `https://yourdomain.com/funnies/`
* Iframe feed: `https://yourdomain.com/funnies/funnies-frame.php`
---
## 🪄 Embedding the Funnies Frame
The iframe viewer (`funnies-frame.php`) is optimized for small layouts (≈ 325 px).
Example embed:
```html
<iframe src="https://mentalnet.xyz/funnies/funnies-frame.php"
width="325"
height="500"
frameborder="0"
scrolling="yes"
style="border-radius:8px;overflow:hidden;">
</iframe>
```
> 💡 You can adjust image width inside `funnies-frame.php` (default: 250 px for compact display).
---
## 🛡 Security Notes
* Only the admin section (`/admin/`) requires authentication.
* Use strong passwords and HTTPS.
* The upload form restricts to images only.
* SQLite DB and uploads folder should be owned by `www-data` with 755 perms.
---
## 🧩 Customization Ideas
* Add a “View All Comics →” link inside the iframe version.
* Include navigation buttons (Next / Previous comic).
* Implement tags or short titles for categorization.
* Add a JSON API endpoint for future integration with other MentalNet services.
---
## 🧰 License
MIT License — free to modify and self-host.
Created by **MarkMental** as part of the **mentalnet.xyz** web ecosystem.
> “In the MentalNet, Tux is root — and Miku too.” 🐧🎤

33
admin/auth.php Normal file
View file

@ -0,0 +1,33 @@
<?php
session_start();
require_once __DIR__ . '/config.php';
if (isset($_POST['password'])) {
if (password_verify($_POST['password'], $ADMIN_PASS_HASH)) {
$_SESSION['auth'] = true;
header("Location: upload.php");
exit;
} else {
$error = "Invalid password.";
}
}
if (!isset($_SESSION['auth'])):
?>
<!DOCTYPE html>
<html>
<head><title>Login - MentalNet Funnies Admin</title></head>
<body style="background:#111;color:#eee;font-family:monospace;text-align:center">
<h1>🔒 MentalNet Funnies Admin</h1>
<form method="post">
<input type="password" name="password" placeholder="Password" autofocus>
<button type="submit">Login</button>
</form>
<?php if (!empty($error)) echo "<p style='color:red;'>$error</p>"; ?>
</body>
</html>
<?php
exit;
endif;
?>

22
admin/config.php Normal file
View file

@ -0,0 +1,22 @@
<?php
// --- basic config ---
$db_file = __DIR__ . '/../mentalnet_funnies.db';
// password hash (generate with: php -r "echo password_hash('yourpassword', PASSWORD_DEFAULT);")
$ADMIN_PASS_HASH = 'pwhashgoeshere';
// create db if not exists
if (!file_exists($db_file)) {
$db = new SQLite3($db_file);
$db->exec("CREATE TABLE comics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
filename TEXT NOT NULL,
description TEXT,
date_added DATETIME DEFAULT CURRENT_TIMESTAMP
)");
} else {
$db = new SQLite3($db_file);
}
?>

20
admin/init_db.php Normal file
View file

@ -0,0 +1,20 @@
<?php
$db = new SQLite3(__DIR__ . '/../mentalnet_funnies.db');
// Check if the table already exists
$result = $db->querySingle("SELECT name FROM sqlite_master WHERE type='table' AND name='comics'");
if ($result) {
echo " Table 'comics' already exists.";
} else {
$db->exec("CREATE TABLE comics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
filename TEXT NOT NULL,
description TEXT,
date_added DATETIME DEFAULT CURRENT_TIMESTAMP
)");
echo "✅ Table 'comics' created.";
}
?>

40
admin/upload.php Normal file
View file

@ -0,0 +1,40 @@
<?php
require_once __DIR__ . '/auth.php'; // ensures login
require_once __DIR__ . '/config.php';
$msg = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['comic'])) {
$title = trim($_POST['title']);
$desc = trim($_POST['description']);
$file = $_FILES['comic'];
if ($file['error'] === UPLOAD_ERR_OK && $title) {
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$fname = uniqid('comic_') . '.' . $ext;
move_uploaded_file($file['tmp_name'], __DIR__ . '/../uploads/' . $fname);
$stmt = $db->prepare("INSERT INTO comics (title, filename, description) VALUES (:t,:f,:d)");
$stmt->bindValue(':t',$title,SQLITE3_TEXT);
$stmt->bindValue(':f',$fname,SQLITE3_TEXT);
$stmt->bindValue(':d',$desc,SQLITE3_TEXT);
$stmt->execute();
$msg = "✅ Uploaded '$title'";
} else $msg = "Upload failed.";
}
?>
<!DOCTYPE html>
<html>
<head><title>Upload Comic - MentalNet Funnies</title></head>
<body style="background:#111;color:#eee;font-family:monospace;text-align:center">
<h1>🧠 Upload Comic</h1>
<p><?=htmlspecialchars($msg)?></p>
<form method="post" enctype="multipart/form-data">
<p><input type="text" name="title" placeholder="Title" required></p>
<p><textarea name="description" placeholder="Description"></textarea></p>
<p><input type="file" name="comic" accept="image/*" required></p>
<p><button type="submit">Upload</button></p>
</form>
<p><a href="../index.php" style="color:#0ff;"> View Site</a></p>
</body>
</html>

72
funnies-frame.php Normal file
View file

@ -0,0 +1,72 @@
<?php
$db = new SQLite3(__DIR__ . '/mentalnet_funnies.db');
$res = $db->query('SELECT * FROM comics ORDER BY date_added DESC LIMIT 3'); // show latest 3 for compactness
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MentalNet Funnies Frame</title>
<style>
html, body {
margin: 0;
padding: 0;
background: #111;
color: #eee;
font-family: monospace;
overflow-x: hidden;
}
h1 {
display: none; /* hide header for iframe context */
}
.comic {
margin-bottom: 1rem;
padding: 0.25rem 0.5rem;
}
h2 {
font-size: 0.9rem;
margin: 0.4rem 0;
color: #0ff;
text-align: center;
word-wrap: break-word;
}
img {
width: 250px;
border-radius: 4px;
display: block;
margin: 0 auto;
box-shadow: 0 0 6px #000;
}
p {
font-size: 0.8rem;
margin: 0.25rem auto;
text-align: center;
color: #bbb;
width: 95%;
line-height: 1.3;
}
small {
color: #666;
font-size: 0.7rem;
}
@media (max-width: 360px) {
img { width: 98%; }
h2 { font-size: 0.85rem; }
}
</style>
</head>
<body>
<?php while($row=$res->fetchArray()): ?>
<div class="comic">
<h2><?= htmlspecialchars($row['title']) ?></h2>
<img src="uploads/<?= htmlspecialchars($row['filename']) ?>" alt="<?= htmlspecialchars($row['title']) ?>">
<?php if($row['description']): ?>
<p><?= nl2br(htmlspecialchars($row['description'])) ?></p>
<?php endif; ?>
<p><small><?= htmlspecialchars(date('M j, Y', strtotime($row['date_added']))) ?></small></p>
</div>
<?php endwhile; ?>
</body>
</html>

61
index.php Normal file
View file

@ -0,0 +1,61 @@
<?php
$db = new SQLite3(__DIR__ . '/mentalnet_funnies.db');
$res = $db->query('SELECT * FROM comics ORDER BY date_added DESC');
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MentalNet Funnies</title>
<style>
body {
background:#111;
color:#eee;
font-family: monospace;
text-align: center;
margin:0;
padding:0;
}
h1 {
margin: 1rem 0;
font-size: 1.6rem;
}
h2 {
font-size: 1.1rem;
margin: 0.5rem 0;
}
img {
width: 95%;
max-width: 640px;
border-radius: 8px;
margin: 0.75rem auto;
box-shadow: 0 0 10px #222;
}
p {
margin: 0.5rem auto;
width: 90%;
line-height: 1.4;
}
small {
color: #888;
}
@media (max-width: 600px) {
h1 { font-size: 1.3rem; }
img { width: 98%; border-radius: 6px; }
}
</style>
</head>
<body>
<h1>🧠 MentalNet Funnies</h1>
<?php while($row=$res->fetchArray()): ?>
<div>
<h2><?= htmlspecialchars($row['title']) ?></h2>
<img src="uploads/<?= htmlspecialchars($row['filename']) ?>" alt="<?= htmlspecialchars($row['title']) ?>">
<p><?= nl2br(htmlspecialchars($row['description'])) ?></p>
<p><small><?= htmlspecialchars($row['date_added']) ?></small></p>
</div>
<?php endwhile; ?>
</body>
</html>