admin.html (14053B)
1 {{template "_header" .}} 2 <h1>Admin</h1> 3 4 {{if .Data.Error}} 5 <div class="admin-alert admin-alert--error"> 6 <strong>Error:</strong> {{.Data.Error}} 7 </div> 8 {{end}} 9 10 {{if .Data.Success}} 11 <div class="admin-alert admin-alert--success"> 12 <strong>Success:</strong> {{.Data.Success}} 13 </div> 14 {{end}} 15 16 <!-- Tab Navigation --> 17 <div class="admin-tab-nav"> 18 <button onclick="showAdminTab('settings')" id="admin-tab-settings" class="admin-tab-btn admin-tab-btn--active"> 19 Settings 20 </button> 21 <button onclick="showAdminTab('database')" id="admin-tab-database" class="admin-tab-btn"> 22 Database 23 </button> 24 <button onclick="showAdminTab('aliases')" id="admin-tab-aliases" class="admin-tab-btn"> 25 Aliases 26 </button> 27 <button onclick="showAdminTab('sedrules')" id="admin-tab-sedrules" class="admin-tab-btn"> 28 Sed Rules 29 </button> 30 <button onclick="showAdminTab('orphans')" id="admin-tab-orphans" class="admin-tab-btn"> 31 Orphans 32 </button> 33 <button onclick="showAdminTab('thumbnails')" id="admin-tab-thumbnails" class="admin-tab-btn"> 34 Thumbnails 35 </button> 36 </div> 37 38 <!-- Settings Tab --> 39 <div id="admin-content-settings"> 40 <div class="config-container"> 41 <div class="config-split"> 42 <h2>Settings</h2> 43 <form method="post" class="admin-form"> 44 <input type="hidden" name="active_tab" value="settings"> 45 46 <div class="admin-form-group"> 47 <label for="gallery_size" class="admin-form-label">Gallery Size:</label> 48 <input type="text" id="gallery_size" name="gallery_size" value="{{.Data.Config.GallerySize}}" required 49 placeholder="400px"> 50 <small class="admin-hint">Size of previews used in galleries</small> 51 </div> 52 53 <div class="admin-form-group"> 54 <label for="items_per_page" class="admin-form-label">Items per Page:</label> 55 <input type="text" id="items_per_page" name="items_per_page" value="{{.Data.Config.ItemsPerPage}}" required 56 placeholder="100"> 57 <small class="admin-hint">Items per page in galleries</small> 58 </div> 59 60 <button type="submit" class="admin-btn admin-btn--primary"> 61 Save Settings 62 </button> 63 </form> 64 </div> 65 66 <div class="config-split"> 67 <h3>Current Configuration:</h3> 68 <ul> 69 <li><strong>Database:</strong> {{.Data.Config.DatabasePath}}</li> 70 <li><strong>Upload Directory:</strong> {{.Data.Config.UploadDir}}</li> 71 <li><strong>Server Port:</strong> {{.Data.Config.ServerPort}}</li> 72 <li><strong>Gallery Size:</strong> {{.Data.Config.GallerySize}}</li> 73 <li><strong>Items per Page:</strong> {{.Data.Config.ItemsPerPage}}</li> 74 </ul> 75 76 <h4>Configuration:</h4> 77 <p>Working directory is specified via the <code>-d</code> flag on launch.</p> 78 <p>Webserver port is specified via the <code>-p</code> flag on launch.</p> 79 </div> 80 </div> 81 </div> 82 83 <!-- Database Tab --> 84 <div id="admin-content-database" class="admin-tab-content"> 85 <h2>Database Maintenance</h2> 86 87 <form method="post" class="admin-form-inline"> 88 <input type="hidden" name="active_tab" value="database"> 89 <input type="hidden" name="action" value="backup"> 90 <button type="submit" class="admin-btn admin-btn--success"> 91 Backup Database 92 </button> 93 <small class="admin-hint admin-hint--inline">Creates a timestamped backup of the database file</small> 94 </form> 95 96 <form method="post"> 97 <input type="hidden" name="active_tab" value="database"> 98 <input type="hidden" name="action" value="vacuum"> 99 <button type="submit" class="admin-btn admin-btn--purple"> 100 Vacuum Database 101 </button> 102 <small class="admin-hint admin-hint--inline">Reclaims unused space and optimizes database performance</small> 103 </form> 104 105 <hr class="admin-divider"> 106 <h3>File Properties</h3> 107 <p class="admin-hint"> 108 Compute auto-detected properties (filetype, duration, orientation, resolution) for any files that do not yet have them. 109 </p> 110 <form method="post"> 111 <input type="hidden" name="active_tab" value="database"> 112 <input type="hidden" name="action" value="compute_properties"> 113 <button type="submit" class="admin-btn admin-btn--teal"> 114 Compute Missing Properties 115 </button> 116 <small class="admin-hint admin-hint--inline">Processes only files with no existing properties</small> 117 </form> 118 </div> 119 120 <!-- Aliases Tab --> 121 <div id="admin-content-aliases" class="admin-tab-content"> 122 <h2>Tag Aliases</h2> 123 <p> 124 Define tag aliases so that multiple tag values are treated as equivalent when searching or filtering. 125 For example, if you alias "colour/blue" with "colour/navy", searching for either will show files tagged with both. 126 </p> 127 128 <div id="aliases-section" class="admin-section"> 129 <div id="alias-groups"></div> 130 131 <button onclick="addAliasGroup()" class="admin-btn admin-btn--success admin-btn--sm admin-btn--top"> 132 + Add Alias Group 133 </button> 134 135 <form method="post" id="aliases-form" class="admin-form-top"> 136 <input type="hidden" name="active_tab" value="aliases"> 137 <input type="hidden" name="action" value="save_aliases"> 138 <input type="hidden" name="aliases_json" id="aliases_json"> 139 <button type="submit" class="admin-btn admin-btn--primary"> 140 Save Aliases 141 </button> 142 </form> 143 </div> 144 </div> 145 146 <!-- Sed Rules Tab --> 147 <div id="admin-content-sedrules" class="admin-tab-content"> 148 <div class="config-container"> 149 <div class="config-split"> 150 <h2>Sed Rules for Notes</h2> 151 <p> 152 Define sed rules that can be applied to notes in the Notes editor. 153 These rules will appear as buttons in the notes interface for quick text transformations. 154 </p> 155 156 <div id="sedrules-section" class="admin-section"> 157 <div id="sed-rules"></div> 158 159 <button onclick="addSedRule()" class="admin-btn admin-btn--success admin-btn--sm admin-btn--top"> 160 + Add Sed Rule 161 </button> 162 163 <form method="post" action="/admin" id="sedrules-form" class="admin-form-top"> 164 <input type="hidden" name="active_tab" value="sedrules"> 165 <input type="hidden" name="action" value="save_sed_rules"> 166 <input type="hidden" name="sed_rules_json" id="sed_rules_json"> 167 <button type="submit" class="admin-btn admin-btn--primary"> 168 Save Sed Rules 169 </button> 170 </form> 171 </div> 172 </div> 173 174 <div class="config-split"> 175 <h4 class="admin-example-heading">Example Sed Commands:</h4> 176 <ul class="admin-example-list"> 177 <li><code>s?[?&]brandIds=[0-9]\+&productId=[0-9]\+??g</code> - Remove URL parameters</li> 178 <li><code>s/[[:space:]]*$//</code> - Remove trailing whitespace</li> 179 <li><code>s/ \+/ /g</code> - Replace multiple spaces with single space</li> 180 <li><code>s/https\?:\/\/[^ ]*//g</code> - Remove URLs</li> 181 <li><code>/^$/d</code> - Delete empty lines</li> 182 </ul> 183 </div> 184 </div> 185 </div> 186 187 <!-- Orphans Tab --> 188 <div id="admin-content-orphans" class="admin-tab-content"> 189 <h2>Orphaned Files</h2> 190 191 <!-- Files on disk, not in DB --> 192 <h3 class="admin-orphan-heading">Disk Orphans</h3> 193 <p class="admin-hint admin-hint--spaced"> 194 These files exist in the upload directory but are not tracked in the database. 195 </p> 196 {{if .Data.OrphanData.Orphans}} 197 <ul class="admin-orphan-list"> 198 {{range .Data.OrphanData.Orphans}} 199 <li class="admin-orphan-item">{{.}}</li> 200 {{end}} 201 </ul> 202 {{else}} 203 <div class="admin-alert admin-alert--success"> 204 <strong>✓ No disk orphans found!</strong> 205 </div> 206 {{end}} 207 208 <!-- Files in DB, not on disk --> 209 <h3 class="admin-orphan-heading admin-orphan-heading--db">Database Orphans</h3> 210 <p class="admin-hint admin-hint--spaced"> 211 These files are tracked in the database but are missing from the upload directory. 212 </p> 213 {{if .Data.OrphanData.ReverseOrphans}} 214 <ul class="admin-orphan-list"> 215 {{range .Data.OrphanData.ReverseOrphans}} 216 <li class="admin-orphan-item">{{.}}</li> 217 {{end}} 218 </ul> 219 {{else}} 220 <div class="admin-alert admin-alert--success"> 221 <strong>✓ No database orphans found!</strong> 222 </div> 223 {{end}} 224 </div> 225 226 <!-- Thumbnails Tab --> 227 <div id="admin-content-thumbnails" class="admin-tab-content"> 228 <h2>Thumbnail Management</h2> 229 230 <!-- Sub-tab Navigation --> 231 <div class="admin-subtab-nav"> 232 <button onclick="showThumbnailSubTab('missing')" id="thumb-subtab-missing" class="thumb-subtab-btn thumb-subtab-btn--active"> 233 Missing ({{len .Data.MissingThumbnails}}) 234 </button> 235 <button onclick="showThumbnailSubTab('regenerate')" id="thumb-subtab-regenerate" class="thumb-subtab-btn"> 236 Regenerate 237 </button> 238 </div> 239 240 <!-- Missing Thumbnails Sub-tab --> 241 <div id="thumb-content-missing"> 242 <h3>Missing Thumbnails ({{len .Data.MissingThumbnails}})</h3> 243 244 {{if .Data.MissingThumbnails}} 245 <form method="post" action="/thumbnails/generate" class="admin-form-inline"> 246 <input type="hidden" name="action" value="generate_all"> 247 <button type="submit" onclick="return confirm('Generate thumbnails for all {{len .Data.MissingThumbnails}} videos? This may take a while.');" class="admin-btn admin-btn--success"> 248 Generate All Missing Thumbnails 249 </button> 250 <small class="admin-hint admin-hint--inline">Uses timestamp 00:00:05 for all videos</small> 251 </form> 252 253 <div class="admin-thumb-grid"> 254 {{range .Data.MissingThumbnails}} 255 <div class="admin-thumb-card"> 256 <h4 class="admin-thumb-card__title"> 257 <a href="/file/{{.ID}}" target="_blank">{{.Filename}}</a> 258 </h4> 259 <p class="admin-thumb-card__id">ID: {{.ID}}</p> 260 261 <video class="admin-thumb-card__video" title="Click to capture frame"> 262 <source src="/uploads/{{.EscapedFilename}}"> 263 </video> 264 265 <form method="post" action="/thumbnails/generate" class="admin-thumb-card__form"> 266 <input type="hidden" name="action" value="generate_single"> 267 <input type="hidden" name="file_id" value="{{.ID}}"> 268 <input type="hidden" name="redirect" value="admin"> 269 270 <div class="admin-thumb-card__timestamp-row"> 271 <label class="admin-thumb-card__timestamp-label">Timestamp:</label> 272 <input type="text" name="timestamp" value="00:00:05" placeholder="00:00:05" 273 class="admin-thumb-card__timestamp-input"> 274 </div> 275 276 <button type="submit" class="admin-btn admin-btn--primary admin-btn--full"> 277 Generate Thumbnail 278 </button> 279 </form> 280 </div> 281 {{end}} 282 </div> 283 {{else}} 284 <div class="admin-alert admin-alert--success"> 285 <strong>✓ All videos have thumbnails!</strong> 286 </div> 287 {{end}} 288 </div> 289 290 <!-- Regenerate Sub-tab --> 291 <div id="thumb-content-regenerate" class="admin-tab-content"> 292 <h3>Regenerate Thumbnail</h3> 293 294 <div class="admin-regen-layout"> 295 296 <div class="admin-regen-form-col"> 297 <div class="admin-tip-box"> 298 <strong>Tip:</strong> Enter a file ID to regenerate its thumbnail with a custom timestamp. You can find file IDs in the URL when viewing a file (e.g., /file/312). 299 </div> 300 301 <form method="post" action="/thumbnails/generate" class="admin-regen-form"> 302 <input type="hidden" name="action" value="generate_single"> 303 <input type="hidden" name="redirect" value="admin"> 304 305 <div class="admin-form-group"> 306 <label for="file_id" class="admin-form-label">File ID:</label> 307 <input type="text" id="file_id" name="file_id" required 308 class="admin-mono-input" 309 placeholder="e.g., 312"> 310 <small class="admin-hint">Enter the ID of the video file</small> 311 </div> 312 313 <div class="admin-form-group"> 314 <label for="timestamp" class="admin-form-label">Timestamp:</label> 315 <input type="text" id="timestamp" name="timestamp" value="00:00:05" required 316 class="admin-mono-input" 317 placeholder="00:00:05"> 318 <small class="admin-hint">Format: HH:MM:SS (e.g., 00:00:05 for 5 seconds)</small> 319 </div> 320 321 <button type="submit" class="admin-btn admin-btn--primary admin-btn--full"> 322 Generate/Regenerate Thumbnail 323 </button> 324 </form> 325 </div> 326 327 {{template "_recent" .}} 328 329 </div> 330 </div> 331 </div> 332 333 <script>window.initialAliasGroups = {{.Data.Config.TagAliases}};</script> 334 <script>window.initialSedRules = {{.Data.Config.SedRules}};</script> 335 <script>window.activeAdminTab = "{{.Data.ActiveTab}}";</script> 336 <script src="/static/tag-alias.js" defer></script> 337 <script src="/static/sed-rules.js" defer></script> 338 <script src="/static/admin-tabs.js"></script> 339 <script src="/static/common.js" defer></script> 340 <script src="/static/hover-preview.js" defer></script> 341 342 {{template "_footer"}}