refactor javascript and fix minor bugs

This commit is contained in:
Adora Laura Kalb 2023-05-05 15:16:54 +02:00
parent 545ade9c28
commit 962c5899df
Signed by: adoralaura
GPG key ID: 7A4552166FC8C056
17 changed files with 235 additions and 135 deletions

View file

@ -7,3 +7,16 @@ services:
POSTGRES_PASSWORD: example
POSTGRES_USER: go-urlsh
POSTGRES_DB: go-urlsh
volumes:
- ./postgres-data:/var/lib/postgresql/data
go-urlsh:
image: codeberg.org/lauralani/go-urlsh:0.1
restart: always
ports:
- 127.0.0.1:3000:3000
depends_on:
- db
environment:
# format: postgresql://<username>:<password>@<database_ip>/<database>
DATABASE_URL: postgres://

View file

@ -3,12 +3,12 @@
BINDADDR=127.0.0.1
# Port to bind to
# default: 2345
PORT=2345
# default: 3000
PORT=3000
# List of trusted proxy IPs separated by colons
# default: 127.0.0.1,::1
TRUSTEDPROXIES=127.0.0.1,::1
# TRUSTEDPROXIES=127.0.0.1,::1
# Postgresql connection string
# format: postgresql://<username>:<password>@<database_ip>/todos?sslmode=verify-ca

View file

@ -13,6 +13,10 @@ import (
)
func HandleLinkGetAll(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
}
var links []models.Link
err := models.DB.NewSelect().Model(&links).Scan(context.Background())
@ -23,7 +27,10 @@ func HandleLinkGetAll(c *fiber.Ctx) error {
for _, link := range links {
log.Printf("id: %v, url: %v", link.ID, link.URL)
}
c.JSON(links)
err = c.JSON(links)
if err != nil {
log.Println(err)
}
return nil
}
@ -32,11 +39,10 @@ func HandleLinkGet(c *fiber.Ctx) error {
}
func HandleLinkPost(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) && !db.IsApiKeyValid(c.GetRespHeader("x-api-key", "")) {
return fiber.NewError(fiber.StatusUnauthorized, "401 Unauthorized")
}
// TODO: Add API-Key Auth
var newlink models.Link
err := json.Unmarshal(c.Body(), &newlink)
@ -61,7 +67,11 @@ func HandleLinkPost(c *fiber.Ctx) error {
c.Append("Location", c.BaseURL()+"/api/v1/links/"+newlink.ID)
c.Status(fiber.StatusCreated)
c.JSON(newlink)
err = c.JSON(newlink)
if err != nil {
log.Println(err)
}
return nil
}
@ -92,5 +102,12 @@ func HandleLinkDelete(c *fiber.Ctx) error {
log.Println(err.Error())
return fiber.NewError(fiber.StatusInternalServerError, "500 Internal Server Error")
}
c.Status(fiber.StatusNoContent)
err = c.SendString("204 No Content")
if err != nil {
log.Println(err)
}
return nil
}

View file

@ -7,7 +7,6 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/gofiber/fiber/v2"
"log"
"time"
@ -34,7 +33,6 @@ func HandleUserPost(c *fiber.Ctx) error {
salt := misc.RandomString(15)
created := time.Now()
hashbytes := sha256.Sum256([]byte(salt + newuser.Password))
fmt.Printf("%x\n", hashbytes)
hash := hex.EncodeToString(hashbytes[:])

27
internal/db/apikey.go Normal file
View file

@ -0,0 +1,27 @@
package db
import (
"codeberg.org/lauralani/go-urlsh/models"
"context"
"log"
)
// IsApiKeyValid checks the database if ApiKey val is valid.
//
// Returns true if it's valid, false if not.
func IsApiKeyValid(val string) bool {
if val == "" {
return false
}
count, err := models.DB.NewSelect().Model((*models.ApiKey)(nil)).Where("key = ?", val).Count(context.Background())
if err != nil {
log.Printf("Error checking apikey validity for key %v\n", val)
return false
}
if count < 1 {
return false
} else {
return true
}
}

View file

@ -12,6 +12,12 @@ import (
)
func HandleAdminLinkNewGet(c *fiber.Ctx) error {
if !db.IsCookieValid(c.Cookies(misc.CookieName, "")) {
c.Location("/admin/")
c.Status(fiber.StatusSeeOther)
return nil
}
return c.Render("add_link", nil)
}

View file

@ -8,6 +8,7 @@ import (
type ApiKey struct {
bun.BaseModel `bun:"table:apikeys"`
Key string `bun:"key,pk,type:uuid,default:gen_random_uuid()" json:"key,omitempty"`
UserName string `bun:"username,notnull" json:"username"`
Created time.Time `bun:"created,default:now()" json:"created"`
UserName string `bun:"username,notnull" json:"username,omitempty"`
Created time.Time `bun:"created,default:now()" json:"created,omitempty"`
Description string `bun:"description,notnull" json:"description"`
}

View file

@ -36,7 +36,7 @@
</ul>
</nav>
<main class="container" style="padding: 0">
<form action="javascript:HandleSubmit();" style="margin: 0">
<form action="javascript:HandleLinkAddSubmit();" style="margin: 0">
<fieldset id="form_fields">
<hgroup>
<h1>Add new shortlink</h1>
@ -46,7 +46,7 @@
<input type="text" name="linkname" id="linkname" placeholder="shortlink">
<label for="link">Link <i style="color: red;">*</i></label>
<input type="text" name="link" id="link" placeholder="https://" onchange="HandleChange()">
<input type="text" name="link" id="link" placeholder="https://" onchange="HandleLinkFieldChange()">
<label for="description">Description</label>
<input type="text" name="description" id="description" placeholder="">

View file

@ -8,8 +8,7 @@
<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>
<script src="/admin/main.js" defer></script>
</head>

View file

@ -8,8 +8,7 @@
<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/links.js" defer></script>
<script src="/admin/misc.js" defer></script>
<script src="/admin/main.js" defer></script>
<style>
td {
@ -20,58 +19,58 @@
</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/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>
<th>URL</th>
<th> </th>
<th> </th>
</tr>
</thead>
<tbody>
{{range .}}
<tr>
<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 style="text-align: right;">
<a href="javascript: HandleDelete('{{.ID}}');">
Delete
</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</main>
{{template "partials/footer" .}}
<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>
<th>URL</th>
<th> </th>
<th> </th>
</tr>
</thead>
<tbody>
{{range .}}
<tr>
<td><a href="javascript: HandleLinkIndexCopy('{{.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 style="text-align: right;">
<a href="javascript: HandleLinkIndexDelete('{{.ID}}');">
Delete
</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</main>
{{template "partials/footer" .}}
</body>
</html>

View file

@ -36,7 +36,7 @@
</ul>
</nav>
<main class="container" style="padding: 0">
<form action="javascript:HandleSubmit();" style="margin: 0">
<form action="javascript:HandleLinkAddSubmit();" style="margin: 0">
<fieldset id="form_fields">
<hgroup>
<h1>Add new shortlink</h1>
@ -46,7 +46,7 @@
<input type="text" name="linkname" id="linkname" placeholder="shortlink">
<label for="link">Link <i style="color: red;">*</i></label>
<input type="text" name="link" id="link" placeholder="https://" onchange="HandleChange()">
<input type="text" name="link" id="link" placeholder="https://" onchange="HandleLinkFieldChange()">
<label for="description">Description</label>
<input type="text" name="description" id="description" placeholder="">

View file

@ -1,37 +0,0 @@
async function HandleSubmit() {
document.getElementById("submit").active = false
let slug = document.getElementById("linkname").value
let url = document.getElementById("link").value
let description = document.getElementById("description").value
let body = {
"id" : slug,
"url" : url,
"description" : description
}
let response = await fetch("/api/v1/links", {
credentials: "include",
body: JSON.stringify(body),
mode: "same-origin",
method: "POST"
});
if (!response.ok) {
document.getElementById("dialog-heading").textContent = "Error"
document.getElementById("dialog-text").textContent = "The following error occured during the request: " + response.statusText
document.getElementById('dialog-info').showModal()
document.getElementById("submit").active = true
}
document.location = "/admin/"
}
async function HandleChange() {
console.log("HandleChange")
let buttonactive = true
if (document.getElementById("link").value === "")
{
buttonactive = false
}
document.getElementById("submit").active = buttonactive
}

View file

@ -8,8 +8,7 @@
<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>
<script src="/admin/main.js" defer></script>
</head>

View file

@ -8,8 +8,7 @@
<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/links.js" defer></script>
<script src="/admin/misc.js" defer></script>
<script src="/admin/main.js" defer></script>
<style>
td {
@ -54,7 +53,7 @@
<tbody>
{{range .}}
<tr>
<td><a href="javascript: HandleCopy('{{.ID}}');" data-tooltip="click to copy">{{.ID}}</a></td>
<td><a href="javascript: HandleLinkIndexCopy('{{.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}}">
@ -62,7 +61,7 @@
</a>
</td>
<td style="text-align: right;">
<a href="javascript: HandleDelete('{{.ID}}');">
<a href="javascript: HandleLinkIndexDelete('{{.ID}}');">
Delete
</a>
</td>

View file

@ -1,16 +0,0 @@
async function HandleDelete(id){
let response = await fetch("/api/v1/links/" + id, {
credentials: "include",
mode: "same-origin",
method: "DELETE"
});
if (!response.ok) {
console.log("error deleting " + id + ": " + response.statusText)
}
document.location = "/admin/"
}
async function HandleCopy(id) {
let host = window.location.protocol + "//" + window.location.host;
await navigator.clipboard.writeText(host + "/" + id);
}

99
web/main.js Normal file
View file

@ -0,0 +1,99 @@
// Link overview
async function HandleLinkIndexDelete(id){
let response = await fetch("/api/v1/links/" + id, {
credentials: "include",
mode: "same-origin",
method: "DELETE"
});
if (!response.ok) {
console.log("error deleting " + id + ": " + response.statusText)
}
document.location = "/admin/"
}
async function HandleLinkIndexCopy(id) {
let host = window.location.protocol + "//" + window.location.host;
await navigator.clipboard.writeText(host + "/" + id);
}
// Link Add
async function HandleLinkAddSubmit() {
document.getElementById("submit").active = false
let slug = document.getElementById("linkname").value
let url = document.getElementById("link").value
let description = document.getElementById("description").value
let body = {
"id" : slug,
"url" : url,
"description" : description
}
let response = await fetch("/api/v1/links", {
credentials: "include",
body: JSON.stringify(body),
mode: "same-origin",
method: "POST"
});
if (!response.ok) {
document.getElementById("dialog-heading").textContent = "Error"
document.getElementById("dialog-text").textContent = "The following error occured during the request: " + response.statusText
document.getElementById('dialog-info').showModal()
document.getElementById("submit").active = true
}
document.location = "/admin/"
}
async function HandleLinkFieldChange() {
console.log("HandleChange")
let buttonactive = true
if (document.getElementById("link").value === "")
{
buttonactive = false
}
document.getElementById("submit").active = buttonactive
}
// ApiKey Add
async function HandleApiKeyNewSubmit() {
let button = document.getElementById("submit")
let description = document.getElementById("description")
button.active = false
button.setAttribute("aria-busy", "true")
let body = {
"description" : description
}
let response = await fetch("/api/v1/apikeys", {
credentials: "include",
body: JSON.stringify(body),
mode: "same-origin",
method: "POST"
});
if (response.ok) {
let data = await response.json()
document.getElementById("dialog-heading").textContent = "New API-Key"
document.getElementById("dialog-text").textContent = "Here is your new API Key. Copy it NOW, it won't be shown again."
document.getElementById("dialog-apikey").textContent = data.key
document.getElementById('dialog-info').showModal()
}
}
async function HandleApiKeyModalClose() {
let modal = document.getElementById('dialog-info');
modal.close()
}
// General
function Logout() {
document.cookie = 'gourlsh_auth=; Max-Age=-1; path=/; domain=' + location.hostname;
document.location = "/admin/login"
}

View file

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