mirror of
https://github.com/mivodev/mivo.git
synced 2026-01-26 05:25:42 +07:00
Initial Release v1.0.0: Full feature set with Docker automation, Nginx/Alpine stack
This commit is contained in:
145
app/Models/Logo.php
Normal file
145
app/Models/Logo.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use PDO;
|
||||
use Exception;
|
||||
|
||||
class Logo {
|
||||
protected $db;
|
||||
protected $table = 'logos';
|
||||
|
||||
public function __construct() {
|
||||
$this->db = \App\Core\Database::getInstance();
|
||||
$this->initTable();
|
||||
}
|
||||
|
||||
// Connect method removed as we use shared instance
|
||||
private function initTable() {
|
||||
$query = "CREATE TABLE IF NOT EXISTS {$this->table} (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
type TEXT,
|
||||
size INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)";
|
||||
$this->db->query($query);
|
||||
}
|
||||
|
||||
public function generateId($length = 6) {
|
||||
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$charactersLength = strlen($characters);
|
||||
$randomString = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$randomString .= $characters[rand(0, $charactersLength - 1)];
|
||||
}
|
||||
return $randomString;
|
||||
}
|
||||
|
||||
public function getAll() {
|
||||
$stmt = $this->db->query("SELECT * FROM {$this->table} ORDER BY created_at DESC");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getById($id) {
|
||||
$stmt = $this->db->query("SELECT * FROM {$this->table} WHERE id = :id", ['id' => $id]);
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function add($file) {
|
||||
// Security: Strict MIME Type Check
|
||||
$finfo = new \finfo(FILEINFO_MIME_TYPE);
|
||||
$mimeType = $finfo->file($file['tmp_name']);
|
||||
|
||||
$allowedMimes = [
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
'image/gif' => 'gif',
|
||||
'image/svg+xml' => 'svg',
|
||||
'image/webp' => 'webp'
|
||||
];
|
||||
|
||||
if (!array_key_exists($mimeType, $allowedMimes)) {
|
||||
throw new Exception("Invalid file type: " . $mimeType);
|
||||
}
|
||||
|
||||
// Use extension mapped from MIME type or sanitize original
|
||||
// Better to trust MIME mapping for extensions to avoid double extension attacks
|
||||
$extension = $allowedMimes[$mimeType];
|
||||
|
||||
// Generate Unique Short ID
|
||||
do {
|
||||
$id = $this->generateId();
|
||||
$exists = $this->getById($id);
|
||||
} while ($exists);
|
||||
|
||||
$uploadDir = ROOT . '/public/assets/img/logos/';
|
||||
if (!file_exists($uploadDir)) {
|
||||
mkdir($uploadDir, 0777, true);
|
||||
}
|
||||
|
||||
$filename = $id . '.' . $extension;
|
||||
$targetPath = $uploadDir . $filename;
|
||||
|
||||
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
|
||||
$this->db->query("INSERT INTO {$this->table} (id, name, path, type, size) VALUES (:id, :name, :path, :type, :size)", [
|
||||
'id' => $id,
|
||||
'name' => $file['name'],
|
||||
'path' => '/assets/img/logos/' . $filename,
|
||||
'type' => $extension,
|
||||
'size' => $file['size']
|
||||
]);
|
||||
return $id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function syncFiles() {
|
||||
// One-time sync: scan folder, if file not in DB, add it.
|
||||
$logoDir = ROOT . '/public/assets/img/logos/';
|
||||
if (!file_exists($logoDir)) return;
|
||||
|
||||
$files = glob($logoDir . '*.{jpg,jpeg,png,gif,svg}', GLOB_BRACE);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$filename = basename($file);
|
||||
$extension = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
|
||||
// Check if file is registered (maybe by path match)
|
||||
$webPath = '/assets/img/logos/' . $filename;
|
||||
$stmt = $this->db->query("SELECT COUNT(*) FROM {$this->table} WHERE path = :path", ['path' => $webPath]);
|
||||
|
||||
if ($stmt->fetchColumn() == 0) {
|
||||
// Not in DB, register it.
|
||||
// Ideally we'd rename it to a hashID, but since it's existing, let's generate an ID and map it.
|
||||
do {
|
||||
$id = $this->generateId();
|
||||
$exists = $this->getById($id);
|
||||
} while ($exists);
|
||||
|
||||
$this->db->query("INSERT INTO {$this->table} (id, name, path, type, size) VALUES (:id, :name, :path, :type, :size)", [
|
||||
'id' => $id,
|
||||
'name' => $filename,
|
||||
'path' => $webPath,
|
||||
'type' => $extension,
|
||||
'size' => filesize($file)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($id) {
|
||||
$logo = $this->getById($id);
|
||||
if ($logo) {
|
||||
$filePath = ROOT . '/public' . $logo['path'];
|
||||
if (file_exists($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
$this->db->query("DELETE FROM {$this->table} WHERE id = :id", ['id' => $id]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user