first push

This commit is contained in:
jason
2026-04-22 15:47:27 -05:00
parent 923ef2ec0e
commit 1552a0ea65
86 changed files with 10066 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
<% if (typeof defaultPassword !== 'undefined' && defaultPassword) { %>
<div class="bg-amber-500/10 border-b border-amber-500/20 px-8 py-2.5 flex items-center gap-3">
<svg class="w-4 h-4 text-amber-400 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>
<p class="text-xs text-amber-400">
You're using the default password. <a href="/admin/settings" class="underline font-medium hover:text-amber-300">Change it now</a> before exposing this app to a network.
</p>
</div>
<% } %>
+59
View File
@@ -0,0 +1,59 @@
<aside class="fixed inset-y-0 left-0 w-56 bg-surface-900 border-r border-gray-800 flex flex-col z-20">
<!-- Brand -->
<div class="flex items-center gap-3 px-5 py-5 border-b border-gray-800">
<% if (brand.brand_logo_path) { %>
<img src="/brand/logo" alt="<%= brand.brand_name %>" class="h-7 w-auto object-contain" />
<% } else { %>
<div class="h-7 w-7 rounded-md btn-accent flex items-center justify-center shrink-0">
<svg class="w-4 h-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
</svg>
</div>
<% } %>
<div class="overflow-hidden">
<p class="text-sm font-semibold text-white truncate"><%= brand.brand_name %></p>
<p class="text-xs text-gray-500 truncate">Admin</p>
</div>
</div>
<!-- Nav links -->
<nav class="flex-1 py-4 px-3 space-y-1 overflow-y-auto">
<%
const navItems = [
{ href: '/admin', label: 'Models', icon: 'cube' },
{ href: '/admin/upload', label: 'Upload', icon: 'upload' },
{ href: '/admin/categories', label: 'Categories', icon: 'folder' },
{ href: '/admin/settings', label: 'Settings', icon: 'settings' },
]
const icons = {
cube: '<path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />',
upload: '<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />',
folder: '<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" />',
settings: '<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 010 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 010-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />',
}
%>
<% navItems.forEach(item => {
const active = currentPath && (currentPath === item.href || (item.href !== '/admin' && currentPath.startsWith(item.href)))
%>
<a href="<%= item.href %>"
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors <%= active ? 'btn-accent text-white font-medium' : 'text-gray-400 hover:text-white hover:bg-surface-800' %>">
<svg class="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.75">
<%- icons[item.icon] %>
</svg>
<%= item.label %>
</a>
<% }) %>
</nav>
<!-- Logout -->
<div class="p-3 border-t border-gray-800">
<form method="POST" action="/admin/logout">
<button type="submit" class="w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm text-gray-400 hover:text-white hover:bg-surface-800 transition-colors">
<svg class="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.75">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" />
</svg>
Sign out
</button>
</form>
</div>
</aside>
+29
View File
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= typeof title !== 'undefined' ? title + ' — ' : '' %><%= brand.brand_name %></title>
<% if (brand.brand_favicon_path) { %>
<link rel="icon" href="/brand/favicon" />
<% } %>
<!-- Inter font -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="/style.css" />
<!-- Dynamic brand accent color -->
<style>
:root { --accent: <%= brand.brand_accent %>; }
.btn-accent { background-color: var(--accent); }
.btn-accent:hover { filter: brightness(1.12); }
.border-accent { border-color: var(--accent); }
.text-accent { color: var(--accent); }
.ring-accent:focus { --tw-ring-color: var(--accent); }
</style>
</head>
<body class="bg-surface-950 text-gray-100 font-sans antialiased min-h-screen">
+31
View File
@@ -0,0 +1,31 @@
<a href="/view/<%= model.slug %>"
class="group bg-surface-900 border border-gray-800 hover:border-gray-600 rounded-2xl overflow-hidden transition-all duration-200 hover:shadow-2xl hover:shadow-black/30 hover:-translate-y-0.5 flex flex-col">
<!-- Thumbnail / placeholder -->
<div class="aspect-video bg-surface-800 flex items-center justify-center overflow-hidden">
<% if (model.thumbnail_path) { %>
<img src="/files/thumbnail/<%= model.id %>"
alt="<%= model.name %>"
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
<% } else { %>
<svg class="w-10 h-10 text-gray-700 group-hover:text-gray-600 transition-colors" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
</svg>
<% } %>
</div>
<!-- Info -->
<div class="p-4 flex-1 flex flex-col gap-1">
<p class="text-sm font-medium text-white group-hover:text-accent transition-colors leading-snug">
<%= model.name %>
</p>
<% if (model.description) { %>
<p class="text-xs text-gray-500 line-clamp-2 leading-relaxed"><%= model.description %></p>
<% } %>
<div class="flex items-center gap-2 mt-auto pt-2">
<span class="text-xs font-mono bg-surface-800 border border-gray-700 text-gray-500 px-1.5 py-0.5 rounded">
<%= model.file_type.toUpperCase() %>
</span>
</div>
</div>
</a>