admin.html (13676B)
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"> 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 190 <h2>Orphaned Files</h2> 191 192 <div class="config-container"> 193 <div class="config-split"> 194 <h3 class="admin-orphan-heading">Untracked Files</h3> 195 {{if .Data.OrphanData.Orphans}} 196 <ul class="admin-orphan-list"> 197 {{range .Data.OrphanData.Orphans}}<li class="admin-orphan-item">{{.Filename}}</li> 198 {{end}} 199 </ul> 200 {{else}}<strong>✓ No untracked files found!</strong> 201 {{end}} 202 203 </div> 204 <div class="config-split"> 205 206 <h3 class="admin-orphan-heading">Missing Files</h3> 207 {{if .Data.OrphanData.ReverseOrphans}} 208 <ul class="admin-orphan-list"> 209 {{range .Data.OrphanData.ReverseOrphans}}<li class="admin-orphan-item"><a href="/file/{{.ID}}" target="_blank">{{.Filename}}</a></li> 210 {{end}} 211 </ul> 212 {{else}}<strong>✓ No missing files found!</strong> 213 {{end}} 214 215 </div> 216 </div> 217 </div> 218 219 <!-- Thumbnails Tab --> 220 <div id="admin-content-thumbnails" class="admin-tab-content"> 221 <h2>Thumbnail Management</h2> 222 223 <!-- Sub-tab Navigation --> 224 <div class="admin-subtab-nav"> 225 <button onclick="showThumbnailSubTab('missing')" id="thumb-subtab-missing" class="thumb-subtab-btn thumb-subtab-btn--active"> 226 Missing ({{len .Data.MissingThumbnails}}) 227 </button> 228 <button onclick="showThumbnailSubTab('regenerate')" id="thumb-subtab-regenerate" class="thumb-subtab-btn"> 229 Regenerate 230 </button> 231 </div> 232 233 <!-- Missing Thumbnails Sub-tab --> 234 <div id="thumb-content-missing"> 235 <h3>Missing Thumbnails ({{len .Data.MissingThumbnails}})</h3> 236 237 {{if .Data.MissingThumbnails}} 238 <form method="post" action="/thumbnails/generate" class="admin-form-inline"> 239 <input type="hidden" name="action" value="generate_all"> 240 <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"> 241 Generate All Missing Thumbnails 242 </button> 243 <small class="admin-hint admin-hint--inline">Uses timestamp 00:00:05 for all videos</small> 244 </form> 245 246 <div class="admin-thumb-grid"> 247 {{range .Data.MissingThumbnails}} 248 <div class="admin-thumb-card"> 249 <h4 class="admin-thumb-card__title"> 250 <a href="/file/{{.ID}}" target="_blank">{{.Filename}}</a> 251 </h4> 252 <p class="admin-thumb-card__id">ID: {{.ID}}</p> 253 254 <video class="admin-thumb-card__video" title="Click to capture frame"> 255 <source src="/uploads/{{.EscapedFilename}}"> 256 </video> 257 258 <form method="post" action="/thumbnails/generate" class="admin-thumb-card__form"> 259 <input type="hidden" name="action" value="generate_single"> 260 <input type="hidden" name="file_id" value="{{.ID}}"> 261 <input type="hidden" name="redirect" value="admin"> 262 263 <div class="admin-thumb-card__timestamp-row"> 264 <label class="admin-thumb-card__timestamp-label">Timestamp:</label> 265 <input type="text" name="timestamp" value="00:00:05" placeholder="00:00:05" 266 class="admin-thumb-card__timestamp-input"> 267 </div> 268 269 <button type="submit" class="admin-btn admin-btn--primary admin-btn--full"> 270 Generate Thumbnail 271 </button> 272 </form> 273 </div> 274 {{end}} 275 </div> 276 {{else}} 277 <div class="admin-alert admin-alert--success"> 278 <strong>✓ All videos have thumbnails!</strong> 279 </div> 280 {{end}} 281 </div> 282 283 <!-- Regenerate Sub-tab --> 284 <div id="thumb-content-regenerate" class="admin-tab-content"> 285 <h3>Regenerate Thumbnail</h3> 286 287 <div class="admin-regen-layout"> 288 289 <div class="admin-regen-form-col"> 290 <div class="admin-tip-box"> 291 <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). 292 </div> 293 294 <form method="post" action="/thumbnails/generate" class="admin-regen-form"> 295 <input type="hidden" name="action" value="generate_single"> 296 <input type="hidden" name="redirect" value="admin"> 297 298 <div class="admin-form-group"> 299 <label for="file_id" class="admin-form-label">File ID:</label> 300 <input type="text" id="file_id" name="file_id" required 301 class="admin-mono-input" 302 placeholder="e.g., 312"> 303 <small class="admin-hint">Enter the ID of the video file</small> 304 </div> 305 306 <div class="admin-form-group"> 307 <label for="timestamp" class="admin-form-label">Timestamp:</label> 308 <input type="text" id="timestamp" name="timestamp" value="00:00:05" required 309 class="admin-mono-input" 310 placeholder="00:00:05"> 311 <small class="admin-hint">Format: HH:MM:SS (e.g., 00:00:05 for 5 seconds)</small> 312 </div> 313 314 <button type="submit" class="admin-btn admin-btn--primary admin-btn--full"> 315 Generate/Regenerate Thumbnail 316 </button> 317 </form> 318 </div> 319 320 {{template "_recent" .}} 321 322 </div> 323 </div> 324 </div> 325 326 <script>window.initialAliasGroups = {{.Data.Config.TagAliases}};</script> 327 <script>window.initialSedRules = {{.Data.Config.SedRules}};</script> 328 <script>window.activeAdminTab = "{{.Data.ActiveTab}}";</script> 329 <script src="/static/admin-tag-alias.js" defer></script> 330 <script src="/static/admin-sed-rules.js" defer></script> 331 <script src="/static/admin-tabs.js"></script> 332 <script src="/static/common.js" defer></script> 333 <script src="/static/hover-preview.js" defer></script> 334 335 {{template "_footer"}}