seperate routes, provide robust handler types, use mux

This commit is contained in:
sentriz
2019-07-14 19:32:36 +01:00
parent cbe709025e
commit 5444b328fd
77 changed files with 11880 additions and 1011 deletions

13
assets/assets.go Normal file
View File

@@ -0,0 +1,13 @@
package assets
import "strings"
// PrefixDo runs a given callback for every path in our assets with
// the given prefix
func PrefixDo(pre string, cb func(path string, asset *EmbeddedAsset)) {
for path, asset := range Bytes {
if strings.HasPrefix(path, pre) {
cb(path, asset)
}
}
}

10809
assets/assets_gen.go Normal file

File diff suppressed because it is too large Load Diff

29
assets/layouts/base.tmpl Normal file
View File

@@ -0,0 +1,29 @@
{{ define "layout" }}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>gonic</title>
{{ template "head" }}
</head>
<body>
<div id="content">
<div id="header">
<img src="/admin/static/gonic.png">
</div>
{{ if .Flashes }}
{{ $flash := index .Flashes 0 }}
<div class="padded mono flash-{{ $flash.Type }}">
<i class="mdi mdi-alert-circle"></i> {{ $flash.Message }}
</div>
{{ end }}
{{ template "content" . }}
<div class="side-padded mono text-right">
senan kelly, 2019
<span class="light">&#124;</span>
<a href="https://senan.xyz/g/gonic">github</a>
</div>
</div>
</body>
</html>
{{ end }}

10
assets/layouts/user.tmpl Normal file
View File

@@ -0,0 +1,10 @@
{{ define "content" }}
<div class="side-padded light text-right mono">
welcome {{ .User.Name }}
&#124;
<a href="/admin/home">home</a>
&#124;
<a href="/admin/logout">logout <i class="mdi mdi-logout-variant"></i></a>
</div>
{{ template "user" . }}
{{ end }}

View File

@@ -0,0 +1,12 @@
{{ define "user" }}
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-account-key"></i> changing account password
</div>
<form action="/admin/change_own_password_do" method="post">
<input type="password" id="password_one" name="password_one" placeholder="new password">
<input type="password" id="password_two" name="password_two" placeholder="verify new password">
<input type="submit" value="change">
</form>
</div>
{{ end }}

View File

@@ -0,0 +1,12 @@
{{ define "user" }}
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-account-key"></i> changing {{ .SelectedUser.Name }}'s password
</div>
<form action="/admin/change_password_do?user={{ .SelectedUser.Name }}" method="post">
<input type="password" id="password_one" name="password_one" placeholder="new password">
<input type="password" id="password_two" name="password_two" placeholder="verify new password">
<input type="submit" value="change">
</form>
</div>
{{ end }}

View File

@@ -0,0 +1,13 @@
{{ define "user" }}
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-account-plus"></i> creating new user
</div>
<form action="/admin/create_user_do" method="post">
<input type="text" id="username" name="username" placeholder="username">
<input type="password" id="password_one" name="password_one" placeholder="password">
<input type="password" id="password_two" name="password_two" placeholder="verify password">
<input type="submit" value="create">
</form>
</div>
{{ end }}

View File

@@ -0,0 +1,13 @@
{{ define "user" }}
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-account-remove"></i> deleting user {{ .SelectedUser.Name }}
</div>
<div class="text-right">
are you sure?
</div>
<form action="/admin/delete_user_do?user={{ .SelectedUser.Name }}" method="post">
<input type="submit" value="yes">
</form>
</div>
{{ end }}

95
assets/pages/home.tmpl Normal file
View File

@@ -0,0 +1,95 @@
{{ define "user" }}
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-chart-arc"></i> stats
</div>
<div class="block-right">
<table id="stats" class="text-right">
<tr>
<td>artists:</td> <td>{{ .ArtistCount }}</td>
</tr>
<tr>
<td>albums:</td> <td>{{ .AlbumCount }}</td>
</tr>
<tr>
<td>tracks:</td> <td>{{ .TrackCount }}</td>
</tr>
</table>
</div>
</div>
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-lastfm"></i> last.fm
</div>
<div class="text-right">
{{ if .User.IsAdmin }}
<a href="/admin/update_lastfm_api_key">update api key</a><br/>
{{ end }}
{{ if .CurrentLastFMAPIKey }}
<span class="light">current status</span>
{{ if .User.LastFMSession }}
linked
<span class="light">&#124;</span>
<a href="/admin/unlink_lastfm_do">unlink</a><br/>
{{ else }}
<span class="angry">unlinked</span>
<a href="https://www.last.fm/api/auth/?api_key={{ .CurrentLastFMAPIKey }}&cb={{ .RequestRoot }}/admin/link_lastfm_do">link</a><br/>
{{ end }}
{{ else if not .User.IsAdmin }}
<span class="light">api key not set. please ask your admin to set it</span>
{{ end }}
</div>
</div>
<div class="padded box mono">
{{ if .User.IsAdmin }}
{{/* admin panel to manage all users */}}
<div class="box-title">
<i class="mdi mdi-account-multiple"></i> users
</div>
<div class="text-right">
{{ range $user := .AllUsers }}
<i>{{ $user.Name }}</i>
<span class="light no-small">{{ $user.CreatedAt.Format "jan 02, 2006" }}</span>
<span class="light">&#124;</span>
<a href="/admin/change_password?user={{ $user.Name }}">change password</a>
<span class="light">&#124;</span>
<a href="/admin/delete_user?user={{ $user.Name }}">delete</a><br/>
{{ end }}
<a href="/admin/create_user" class="button">create new</a>
</div>
{{ else }}
{{/* user panel to manage themselves */}}
<div class="box-title">
<i class="mdi mdi-account"></i> your account
</div>
<div class="text-right">
<a href="/admin/change_own_password" class="button">change password</a>
</div>
{{ end }}
</div>
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-folder-multiple"></i> recent folders
</div>
<div class="block-right text-right">
{{ if eq (len .RecentFolders) 0 }}
<span class="light">no folders yet</span>
{{ end }}
<table id="recent-folders">
{{ range $folder := .RecentFolders }}
<tr>
<td class="text-right">{{ $folder.RightPath }}</td>
<td class="no-small"><span class="light" title="{{ $folder.ModifiedAt }}">{{ $folder.ModifiedAt | humanDate }}</span></td>
</tr>
{{ end }}
</table>
{{- if not .IsScanning -}}
<a href="/admin/start_scan_do">start scan</a>
{{- if not .LastScanTime.IsZero -}}
<br>
<span class="light" title="{{ .LastScanTime }}">scanned {{ .LastScanTime | humanDate }}</span>
{{ end }}
{{ end }}
</div>
</div>
{{ end }}

12
assets/pages/login.tmpl Normal file
View File

@@ -0,0 +1,12 @@
{{ define "content" }}
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-login-variant"></i> login
</div>
<form action="/admin/login_do" method="post">
<input type="text" id="username" name="username" placeholder="username">
<input type="password" id="password" name="password" placeholder="password">
<input type="submit" value="login">
</form>
</div>
{{ end }}

View File

@@ -0,0 +1,16 @@
{{ define "user" }}
<div class="padded box mono">
<div class="box-title">
<i class="mdi mdi-key-change"></i> updating last.fm keys
</div>
<div class="text-right">
<span class="light">current key</span> <i>{{ default "not set" .CurrentLastFMAPIKey }}</i><br/>
<span class="light">current secret</span> <i>{{ default "not set" .CurrentLastFMAPISecret }}</i>
</div>
<form action="/admin/update_lastfm_api_key_do" method="post">
<input type="text" id="api_key" name="api_key" placeholder="new key">
<input type="text" id="secret" name="secret" placeholder="new secret">
<input type="submit" value="update">
</form>
</div>
{{ end }}

View File

@@ -0,0 +1,8 @@
{{ define "head" }}
<link rel="stylesheet" href="/admin/static/reset.css">
<link rel="stylesheet" href="https://cdn.materialdesignicons.com/3.6.95/css/materialdesignicons.min.css">
<link rel="stylesheet" href="/admin/static/main.css">
<link rel="shortcut icon" href="/admin/static/favicon.ico" type="image/x-icon">
<link rel="icon" href="/admin/static/favicon.ico" type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
{{ end }}

BIN
assets/static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
assets/static/gonic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

159
assets/static/main.css Normal file
View File

@@ -0,0 +1,159 @@
:root {
--pad-size: 1.75rem;
}
body {
max-width: 780px;
margin: 0 auto;
padding: 0 var(--pad-size);
overflow-y: scroll;
font-size: 14px;
}
table td:first-child::after {
content: " ";
white-space: pre;
}
@media only screen and (max-width: 780px) {
table#recent-folders td:first-child::after {
content: "";
}
.no-small {
display: none;
}
body {
font-size: 12px;
}
}
* {
font-size: 1rem;
}
form input[type],
form select,
form textarea {
margin-bottom: 0;
}
form {
max-width: 400px;
margin-left: auto;
margin-right: 0;
display: flex;
flex-direction: column;
align-items: flex-end;
}
form > * {
width: 100%;
}
form input {
padding: 3px;
border: 1px solid #ccc;
box-sizing: border-box;
}
form input[type=password],
form input[type=text] {
margin-bottom: 0.25rem;
}
form input[type=submit] {
width: 8rem;
background-color: white;
cursor: pointer;
}
div {
margin: 0;
padding: 0;
}
table td,
table th {
border: none;
margin: 0;
}
table {
overflow: hidden;
white-space: nowrap;
}
a,
a:visited {
color: #0064c1;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
#content > * {
margin: var(--pad-size) 0;
}
#header {
border-bottom: 2px solid #0000001a;
}
#header img {
width: 60%;
display: block;
margin: 0 auto;
height: auto;
}
.flash-warning {
background-color: #fd1b1b1c;
border-right: 2px solid #fd1b1b1c;
border-bottom: 2px solid #fd1b1b1c;
}
.flash-normal {
background-color: #15ff5424;
border-right: 2px solid #15ff5424;
border-bottom: 2px solid #15ff5424;
}
.box {
background-color: #00000005;
border-right: 2px solid #0000000c;
border-bottom: 2px solid #0000000c;
}
.text-right {
text-align: right;
}
.block-right > * {
margin-left: auto;
}
.mono {
font-family: monospace;
}
.box-title {
margin-bottom: 0.5rem;
}
.padded {
padding: 1rem;
}
.side-padded {
padding: 0 1rem;
}
.angry {
background-color: #f4433669;
}
.light {
color: #00000082;
}

40
assets/static/reset.css Normal file
View File

@@ -0,0 +1,40 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* html5 display-role reset for older browsers */
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after, q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}