more polish, add picocss, add more js and web features

This commit is contained in:
Adora Laura Kalb 2023-05-04 17:16:56 +02:00
parent a5b6232c77
commit ce47b12e09
Signed by: adoralaura
GPG key ID: 7A4552166FC8C056
25 changed files with 593 additions and 1862 deletions

View file

@ -0,0 +1,9 @@
services:
db:
image: postgres
restart: always
environment:
POSTGRES_PASSWORD: example
POSTGRES_USER: go-urlsh
POSTGRES_DB: go-urlsh

View file

@ -58,6 +58,7 @@ func SetupFiber() error {
fiberapp.Get("/admin/", web.HandleAdminLinkIndexGet)
fiberapp.Get("/admin/links/new", web.HandleAdminLinkNewGet)
fiberapp.Get("/admin/links/edit/:id", web.HandleAdminLinkEditGet)
fiberapp.Static("/admin/", "./web")

View file

@ -1,17 +1,56 @@
package web
import (
"codeberg.org/lauralani/go-urlsh/internal/db"
"codeberg.org/lauralani/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/models"
"context"
"database/sql"
"fmt"
"github.com/gofiber/fiber/v2"
"log"
)
func HandleAdminLinkNewGet(c *fiber.Ctx) error {
return c.Render("add_link", nil)
}
func HandleAdminLinkEditGet(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
}
var link = new(models.Link)
var id = c.Params("id", "")
if id == "" {
log.Println("[HandleAdminLinkEditGet] no id param given")
return fiber.NewError(fiber.StatusBadRequest, "400 Bad Request")
}
err := models.DB.NewSelect().Model(link).Where("id = ?", id).Scan(context.Background())
if err != nil {
if err == sql.ErrNoRows {
log.Printf("[HandleAdminLinkEditGet] Shortlink %v not found\n", id)
return fiber.NewError(fiber.StatusNotFound, "404 Not Found")
} else {
log.Printf("[HandleAdminLinkEditGet] Error querying Shortlink %v from database: %v\n", id, err)
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
}
return c.Render("edit_link", link)
}
func HandleAdminLinkIndexGet(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/login")
c.Status(fiber.StatusSeeOther)
return nil
} else {
var links []models.Link
err := models.DB.NewSelect().Model(&links).Scan(context.Background())
@ -20,3 +59,4 @@ func HandleAdminLinkIndexGet(c *fiber.Ctx) error {
}
return c.Render("links", links)
}
}

View file

@ -1,6 +1,7 @@
package web
import (
"codeberg.org/lauralani/go-urlsh/internal/db"
"codeberg.org/lauralani/go-urlsh/internal/misc"
"codeberg.org/lauralani/go-urlsh/models"
"context"
@ -31,8 +32,14 @@ func HandleAdminLoginPost(c *fiber.Ctx) error {
if passwordsum == user.PasswordHash {
// Passwords match
var expires time.Time
if login.Remember == "on" {
expires = time.Now().Add(30 * 24 * time.Hour)
} else {
expires = time.Now().Add(24 * time.Hour)
}
expires := time.Now().Add(30 * 24 * time.Hour)
key := uuid.New().String()
dblogin := new(models.Login)
@ -68,5 +75,11 @@ func HandleAdminLoginPost(c *fiber.Ctx) error {
}
func HandleAdminLoginGet(c *fiber.Ctx) error {
if db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
} else {
return c.Render("login", nil)
}
}

View file

@ -15,4 +15,5 @@ type Login struct {
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
Remember string `json:"remember"`
}

View file

@ -1,30 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Add new Shortlink - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/style.css">
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/link_add.js" defer></script>
<script src="/admin/misc.js" defer></script>
</head>
<body>
<form action="javascript:void(0);" style="margin-top: 2em">
<nav class="container-fluid">
<ul>
<li>
<a href="/admin/" class="contrast"><strong>go-urlsh</strong></a>
</li>
</ul>
<ul>
<li>
<a href="/admin/">Links</a>
</li>
<li>
<a href="/admin/apikeys/">API Keys</a>
</li>
<li>
<a href="javascript: void(0)" >Users (coming soon)</a>
</li>
<li>
<a href="javascript: Logout()" style="color: red;">Logout</a>
</li>
</ul>
</nav>
<main class="container" style="padding: 0">
<form action="javascript:HandleSubmit();" style="margin: 0">
<fieldset id="form_fields">
<legend>Add a new Shortlink</legend>
<p>* shows a required field</p>
<hgroup>
<h1>Add new shortlink</h1>
<h2><i style="color: red;">*</i> marks required fields</h2>
</hgroup>
<label for="linkname">Shortlink Name (leave empty for random)</label>
<input type="text" name="linkname" id="linkname" placeholder="shortlink">
<label for="link">Link *</label>
<input type="text" name="link" id="link" placeholder="https://" style="width: 97%" onchange="HandleChange()">
<label for="link">Link <i style="color: red;">*</i></label>
<input type="text" name="link" id="link" placeholder="https://" onchange="HandleChange()">
<label for="description">Description</label>
<input type="text" name="description" id="description" placeholder="" style="width: 97%">
<input type="text" name="description" id="description" placeholder="">
<div>
<input type="submit" id="submit" value="Add" onclick="HandleSubmit()" style="margin-top: 1em;">
<input type="submit" id="submit" value="Add" style="margin-top: 1em;">
</div>
</fieldset>
@ -38,16 +67,8 @@
</dialog>
</form>
<footer>
<p>go-urlsh - <a href="/">Home</a> -
<a href="https://codeberg.org/lauralani/go-urlsh" target="_blank">Source Code</a> -
<a href="https://codeberg.org/lauralani/go-urlsh/src/branch/main/LICENSE" target="_blank">License</a>
</p>
</footer>
<script src="/admin/link_add.js"></script>
</main>
{{template "partials/footer" .}}
</body>
</html>

74
views/edit_link.tmpl Normal file
View file

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Edit Shortlink - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/link_add.js" defer></script>
<script src="/admin/misc.js" defer></script>
</head>
<body>
<nav class="container-fluid">
<ul>
<li>
<a href="/admin/" class="contrast"><strong>go-urlsh</strong></a>
</li>
</ul>
<ul>
<li>
<a href="/admin/">Links</a>
</li>
<li>
<a href="/admin/apikeys/">API Keys</a>
</li>
<li>
<a href="javascript: void(0)" >Users (coming soon)</a>
</li>
<li>
<a href="javascript: Logout()" style="color: red;">Logout</a>
</li>
</ul>
</nav>
<main class="container" style="padding: 0">
<form action="javascript:HandleSubmit();" style="margin: 0">
<fieldset id="form_fields">
<hgroup>
<h1>Edit shortlink</h1>
<h2><i style="color: red;">*</i> marks required fields</h2>
</hgroup>
<label for="linkname">Shortlink Name (to change this, delete the shortlink and add it again)</label>
<input type="text" name="linkname" id="linkname" value="{{.ID}}" data-tooltip="" readonly>
<label for="link">Link <i style="color: red;">*</i></label>
<input type="text" name="link" id="link" value="{{.URL}}">
<label for="description">Description</label>
<input type="text" name="description" id="description" value="{{.Description}}">
<div>
<input type="submit" id="submit" value="Save" style="margin-top: 1em;">
</div>
</fieldset>
<dialog id="dialog-info">
<h2 id="dialog-heading"></h2>
<p id="dialog-text"></p>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
</form>
</main>
{{template "partials/footer" .}}
</body>
</html>

View file

@ -1,20 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Shortlinks - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/style.css">
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/links.js" defer></script>
<script src="/admin/misc.js" defer></script>
<style>
td { vertical-align: middle; }
td {
vertical-align: middle;
text-align: left;
}
</style>
</head>
<body>
<a href="/admin/links/new"><button type="button" id="add-new-button">Add new Shortlink</button></a>
<table>
<nav class="container-fluid">
<ul>
<li>
<a href="/admin/" class="contrast"><strong>go-urlsh</strong></a>
</li>
</ul>
<ul>
<li>
<a href="/admin/links/new" style="color: greenyellow;">Add new Shortlink</a>
</li>
<li>
<a href="/admin/apikeys/">API Keys</a>
</li>
<li>
<a href="javascript: void(0)" >Users (coming soon)</a>
</li>
<li>
<a href="javascript: Logout()" style="color: red;">Logout</a>
</li>
</ul>
</nav>
<main class="container">
<table class="n-table">
<thead>
<tr>
<th>Shortlink</th>
@ -26,32 +54,24 @@
<tbody>
{{range .}}
<tr>
<td><a href="#" onclick="HandleCopy('{{.ID}}')">{{.ID}}</a></td>
<td><a href="{{.URL}}" target="_blank">{{.URL}}</a></td>
<td><a href="javascript: HandleCopy('{{.ID}}');" data-tooltip="click to copy">{{.ID}}</a></td>
<td><a href="{{.URL}}" target="_blank" {{if .Description}}data-tooltip="{{.Description}}"{{end}}>{{.URL}}</a></td>
<td style="text-align: right;">
<button type="button" id="edit-button" onclick="HandleEdit('{{.ID}}')">
<a href="/admin/links/edit/{{.ID}}">
Edit
</button>
</a>
</td>
<td style="text-align: right;">
<button type="button" id="delete-button" onclick="HandleDelete('{{.ID}}')">
<a href="javascript: HandleDelete('{{.ID}}');">
Delete
</button>
</a>
</td>
</tr>
{{end}}
</tbody>
</table>
<footer>
<p>go-urlsh - <a href="/admin/">Home</a> -
<a href="https://codeberg.org/lauralani/go-urlsh" target="_blank">Source Code</a> -
<a href="https://codeberg.org/lauralani/go-urlsh/src/branch/main/LICENSE" target="_blank">License</a>
</p>
</footer>
<script src="/admin/links.js"></script>
</main>
{{template "partials/footer" .}}
</body>
</html>

View file

@ -1,22 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Page Title</title>
<title>Login - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/style.css">
<link rel="stylesheet" href="/admin/pico.min.css">
<style>
</style>
</head>
<body>
<h3 id="form-elements">Form elements</h3>
<form method="post" action="/admin/login">
<main class="container">
<article class="grid">
<div>
<hgroup>
<h1 style='--color: var(--h1-color); font-weight: var(--font-weight); font-size: var(--font-size); font-family: var(--font-family);'>Sign in</h1>
</hgroup>
<form method="post" action="/admin/login" style="margin-bottom: 0;" autocomplete="off">
<label for="username">Username</label>
<input type="text" name="username" id="username" placeholder="username">
<input type="text" name="username" id="username" placeholder="Username">
<label for="password">Password</label>
<input type="hidden" name="password" id="password" placeholder="password">
<input type="password" name="password" id="password" placeholder="*******">
<input type="submit" value="Login">
<fieldset>
<label for="remember">
<input type="checkbox" role="switch" id="remember" name="remember" />
Stay logged in
</label>
</fieldset>
<input type="submit" value="Login" class="contrast">
</form>
</div>
</article>
</main>
</body>
</html>

View file

@ -0,0 +1,8 @@
<footer class="container-fluid">
<small>
&#169; 2023 Laura Kalb -
<a href="https://codeberg.org/lauralani/go-urlsh" target="_blank" class="secondary">Source Code</a> -
<a href="https://picocss.com" target="_blank" class="secondary">Theme: PicoCSS</a> -
<a href="https://codeberg.org/lauralani/go-urlsh/src/branch/main/LICENSE" target="_blank" class="secondary">License</a>
</small>
</footer>

View file

@ -1,28 +1,61 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Add new Shortlink - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/simple.min.css">
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/link_add.js" defer></script>
<script src="/admin/misc.js" defer></script>
</head>
<body>
<form action="javascript:void(0);" style="margin-top: 2em">
<nav class="container-fluid">
<ul>
<li>
<a href="/admin/" class="contrast"><strong>go-urlsh</strong></a>
</li>
</ul>
<ul>
<li>
<a href="/admin/">Links</a>
</li>
<li>
<a href="/admin/apikeys/">API Keys</a>
</li>
<li>
<a href="javascript: void(0)" >Users (coming soon)</a>
</li>
<li>
<a href="javascript: Logout()" style="color: red;">Logout</a>
</li>
</ul>
</nav>
<main class="container" style="padding: 0">
<form action="javascript:HandleSubmit();" style="margin: 0">
<fieldset id="form_fields">
<legend>Add a new Shortlink</legend>
<p>* shows a required field</p>
<hgroup>
<h1>Add new shortlink</h1>
<h2><i style="color: red;">*</i> marks required fields</h2>
</hgroup>
<label for="linkname">Shortlink Name (leave empty for random)</label>
<input type="text" name="linkname" id="linkname" placeholder="shortlink">
<label for="link">Link *</label>
<input type="text" name="link" id="link" placeholder="https://" style="width: 100%" onchange="HandleChange()">
<label for="link">Link <i style="color: red;">*</i></label>
<input type="text" name="link" id="link" placeholder="https://" onchange="HandleChange()">
<label for="description">Description</label>
<input type="text" name="description" id="description" placeholder="" style="width: 100%">
<input type="text" name="description" id="description" placeholder="">
<input type="submit" id="submit" value="Login" onclick="HandleSubmit()" disabled>
<div>
<input type="submit" id="submit" value="Add" style="margin-top: 1em;">
</div>
</fieldset>
<dialog id="dialog-info">
@ -34,16 +67,8 @@
</dialog>
</form>
<footer>
<p>go-urlsh - <a href="/">Home</a> -
<a href="https://codeberg.org/lauralani/go-urlsh" target="_blank">Source Code</a> -
<a href="https://codeberg.org/lauralani/go-urlsh/src/branch/main/LICENSE" target="_blank">License</a>
</p>
</footer>
<script src="/admin/links.js"></script>
</main>
{{template "partials/footer" .}}
</body>
</html>

74
web/custom.css Normal file
View file

@ -0,0 +1,74 @@
/* Grid */
body>main {
display: flex;
flex-direction: column;
justify-content: center;
min-height: calc(100vh - 7rem);
padding: 1rem 0;
}
article {
padding: 0;
overflow: hidden;
}
article div {
padding: 1rem;
}
@media (min-width: 576px) {
body>main {
padding: 1.25rem 0;
}
article div {
padding: 1.25rem;
}
}
@media (min-width: 768px) {
body>main {
padding: 1.5rem 0;
}
article div {
padding: 1.5rem;
}
}
@media (min-width: 992px) {
body>main {
padding: 1.75rem 0;
}
article div {
padding: 1.75rem;
}
}
@media (min-width: 1200px) {
body>main {
padding: 2rem 0;
}
article div {
padding: 2rem;
}
}
/* Nav */
summary[role="link"].secondary:is([aria-current], :hover, :active, :focus) {
background-color: transparent;
color: var(--secondary-hover);
}
@media (min-width: 992px) {
.grid>div:nth-of-type(2) {
display: block;
}
}
/* Footer */
body>footer {
padding: 1rem 0;
}

74
web/link_edit.html Normal file
View file

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Edit Shortlink - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/link_add.js" defer></script>
<script src="/admin/misc.js" defer></script>
</head>
<body>
<nav class="container-fluid">
<ul>
<li>
<a href="/admin/" class="contrast"><strong>go-urlsh</strong></a>
</li>
</ul>
<ul>
<li>
<a href="/admin/">Links</a>
</li>
<li>
<a href="/admin/apikeys/">API Keys</a>
</li>
<li>
<a href="javascript: void(0)" >Users (coming soon)</a>
</li>
<li>
<a href="javascript: Logout()" style="color: red;">Logout</a>
</li>
</ul>
</nav>
<main class="container" style="padding: 0">
<form action="javascript:HandleSubmit();" style="margin: 0">
<fieldset id="form_fields">
<hgroup>
<h1>Edit shortlink</h1>
<h2><i style="color: red;">*</i> marks required fields</h2>
</hgroup>
<label for="linkname">Shortlink Name (to change this, delete the shortlink and add it again)</label>
<input type="text" name="linkname" id="linkname" value="{{.ID}}" data-tooltip="" readonly>
<label for="link">Link <i style="color: red;">*</i></label>
<input type="text" name="link" id="link" value="{{.URL}}">
<label for="description">Description</label>
<input type="text" name="description" id="description" value="{{.Description}}">
<div>
<input type="submit" id="submit" value="Save" style="margin-top: 1em;">
</div>
</fieldset>
<dialog id="dialog-info">
<h2 id="dialog-heading"></h2>
<p id="dialog-text"></p>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
</form>
</main>
{{template "partials/footer" .}}
</body>
</html>

View file

@ -1,16 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Shortlinks - go-urlsh</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel="stylesheet" href="/admin/style.css">
<link rel="stylesheet" href="/admin/pico.min.css">
<link rel="stylesheet" href="/admin/custom.css">
<script src="/admin/links.js" defer></script>
<script src="/admin/misc.js" defer></script>
<style>
td {
vertical-align: middle;
text-align: left;
}
</style>
</head>
<body>
<table>
<body>
<nav class="container-fluid">
<ul>
<li>
<a href="/admin/" class="contrast"><strong>go-urlsh</strong></a>
</li>
</ul>
<ul>
<li>
<a href="/admin/links/new" style="color: greenyellow;">Add new Shortlink</a>
</li>
<li>
<a href="/admin/apikeys/">API Keys</a>
</li>
<li>
<a href="javascript: void(0)" >Users (coming soon)</a>
</li>
<li>
<a href="javascript: Logout()" style="color: red;">Logout</a>
</li>
</ul>
</nav>
<main class="container">
<table class="n-table">
<thead>
<tr>
<th>Shortlink</th>
@ -22,33 +54,24 @@
<tbody>
{{range .}}
<tr>
<td><a onclick="HandleCopy('{{.id}}')">{{.ID}}</a></td>
<td><a href="{{.URL}}" target="_blank">{{.URL}}</a></td>
<td>
<button type="button" id="edit-button" onclick="HandleEdit('{{.ID}}')">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" /></svg>
</button>
<td><a href="javascript: HandleCopy('{{.ID}}');" data-tooltip="click to copy">{{.ID}}</a></td>
<td><a href="{{.URL}}" target="_blank" {{if .Description}}data-tooltip="{{.Description}}"{{end}}>{{.URL}}</a></td>
<td style="text-align: right;">
<a href="/admin/links/edit/{{.ID}}">
Edit
</a>
</td>
<td>
<button type="button" id="delete-button" onclick="HandleDelete('{{.ID}}')">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /></svg>
</button>
<td style="text-align: right;">
<a href="javascript: HandleDelete('{{.ID}}');">
Delete
</a>
</td>
</tr>
{{end}}
</tbody>
</table>
<footer>
<p>go-urlsh - <a href="/">Home</a> -
<a href="https://codeberg.org/lauralani/go-urlsh" target="_blank">Source Code</a> -
<a href="https://codeberg.org/lauralani/go-urlsh/src/branch/main/LICENSE" target="_blank">License</a>
</p>
</footer>
<script src="/admin/links.js"></script>
</main>
{{template "partials/footer" .}}
</body>
</html>

4
web/misc.js Normal file
View file

@ -0,0 +1,4 @@
function Logout() {
document.cookie = 'gourlsh_auth=; Max-Age=-1; path=/; domain=' + location.hostname;
document.location = "/admin/login"
}

5
web/pico.classless.min.css vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

5
web/pico.min.css vendored Normal file

File diff suppressed because one or more lines are too long

BIN
web/pico.min.css.gz Normal file

Binary file not shown.

1
web/pico.min.css.map Normal file

File diff suppressed because one or more lines are too long

BIN
web/pico.min.css.map.gz Normal file

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.