#109 move last url to new layout
Some checks failed
Build Docker Image / Explore-Gitea-Actions (push) Failing after 7s

This commit is contained in:
Tim
2024-08-24 13:40:03 +02:00
parent b9190487bd
commit 148d064858
12 changed files with 92 additions and 170 deletions

View File

@@ -3,7 +3,7 @@ tmp_dir = "tmp"
[build]
args_bin = []
bin = "/bin/bash -c 'FRONTEND_URL=xxx ./tmp/main'"
bin = "./tmp/main"
cmd = "templ generate && go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["static", "migrations", "node_modules", "tmp"]

3
go.mod
View File

@@ -4,7 +4,9 @@ go 1.22.5
require (
firebase.google.com/go v3.13.0+incompatible
github.com/a-h/templ v0.2.747
github.com/golang-migrate/migrate/v4 v4.17.1
github.com/joho/godotenv v1.5.1
github.com/mattn/go-sqlite3 v1.14.22
github.com/prometheus/client_golang v1.20.1
google.golang.org/api v0.193.0
@@ -19,7 +21,6 @@ require (
cloud.google.com/go/iam v1.1.12 // indirect
cloud.google.com/go/longrunning v0.5.11 // indirect
cloud.google.com/go/storage v1.43.0 // indirect
github.com/a-h/templ v0.2.747 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect

2
go.sum
View File

@@ -105,6 +105,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=

View File

@@ -11,11 +11,12 @@ import (
func getHandler(db *sql.DB) http.Handler {
var router = http.NewServeMux()
router.HandleFunc("/", service.HandleStaticUi)
router.HandleFunc("/", service.HandleIndexAnd404)
// Serve static files (CSS, JS and images)
router.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
router.HandleFunc("/app", service.App)
router.HandleFunc("POST /api/workout", service.NewWorkout(db))
router.HandleFunc("GET /api/workout", service.GetWorkouts(db))
router.HandleFunc("DELETE /api/workout", service.DeleteWorkout(db))

View File

@@ -7,6 +7,7 @@ import (
"log"
"net/http"
"github.com/joho/godotenv"
_ "github.com/mattn/go-sqlite3"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
@@ -14,6 +15,11 @@ import (
func main() {
log.Println("Starting server...")
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
db, err := sql.Open("sqlite3", "./data.db")
if err != nil {
log.Fatal("Could not open Database data.db: ", err)

View File

@@ -7,14 +7,14 @@ import (
)
func EnableCors(next http.Handler) http.Handler {
var frontent_url = os.Getenv("FRONTEND_URL")
if frontent_url == "" {
log.Fatal("FRONTEND_URL is not set")
var base_url = os.Getenv("BASE_URL")
if base_url == "" {
log.Fatal("BASE_URL is not set")
}
log.Println("FRONTEND_URL is", frontent_url)
log.Println("BASE_URL is", base_url)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", frontent_url)
w.Header().Set("Access-Control-Allow-Origin", base_url)
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Authorization")

View File

@@ -7,7 +7,7 @@ import (
"github.com/a-h/templ"
)
func HandleStaticUi(w http.ResponseWriter, r *http.Request) {
func HandleIndexAnd404(w http.ResponseWriter, r *http.Request) {
var comp templ.Component = nil
if r.URL.Path != "/" {
comp = templates.Layout(templates.NotFound())

View File

@@ -1,6 +1,7 @@
package service
import (
"me-fit/templates"
"me-fit/utils"
"database/sql"
@@ -22,6 +23,13 @@ var (
)
)
func App(w http.ResponseWriter, r *http.Request) {
comp := templates.App()
layout := templates.Layout(comp)
layout.Render(r.Context(), w)
w.WriteHeader(http.StatusOK)
}
func NewWorkout(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
metrics.WithLabelValues("new").Inc()

65
templates/app.templ Normal file
View File

@@ -0,0 +1,65 @@
package templates
templ App() {
<main class="mx-2">
<form
class="max-w-xl mx-auto flex flex-col gap-4 justify-center mt-10"
>
<h2 class="text-4xl mb-8">Track your workout</h2>
<input
id="date"
type="date"
class="input input-bordered"
value=""
name="date"
/>
<select class="select select-bordered w-full" name="type">
<option>Push Ups</option>
<option>Pull Ups</option>
</select>
<input
type="number"
class="input input-bordered"
placeholder="Sets"
name="sets"
/>
<input
type="number"
class="input input-bordered"
placeholder="Reps"
name="reps"
/>
<button class="btn btn-primary self-end">Save</button>
</form>
<!-- <div class="overflow-x-auto mx-auto max-w-screen-lg"> -->
<!-- <h2 class="text-4xl mt-14 mb-8">Workout history</h2> -->
<!-- <table class="table table-auto max-w-full"> -->
<!-- <thead> -->
<!-- <tr> -->
<!-- <th>Date</th> -->
<!-- <th>Type</th> -->
<!-- <th>Sets</th> -->
<!-- <th>Reps</th> -->
<!-- <th></th> -->
<!-- </tr> -->
<!-- </thead> -->
<!-- -->
<!-- <tbody> -->
<!-- <tr> -->
<!-- <th>{workout.date}</th> -->
<!-- <th>{workout.type}</th> -->
<!-- <th>{workout.sets}</th> -->
<!-- <th>{workout.reps}</th> -->
<!-- <th> -->
<!-- <div class="tooltip" data-tip="Delete Entry"> -->
<!-- <button on:click={() => deleteWorkout(workout.id)}> -->
<!-- <MdiDelete class="text-gray-400 text-lg"></MdiDelete> -->
<!-- </button> -->
<!-- </div> -->
<!-- </th> -->
<!-- </tr> -->
<!-- </tbody> -->
<!-- </table> -->
<!-- </div> -->
</main>
}

View File

@@ -1,4 +1,3 @@
package templates
templ header() {

View File

@@ -1,160 +0,0 @@
<script lang="ts">
import { PUBLIC_BASE_API_URL } from '$env/static/public';
import { onMount } from 'svelte';
import type { Auth } from 'firebase/auth';
import { goto } from '$app/navigation';
import { addToast } from '$lib/toast';
import MdiDelete from '~icons/mdi/delete';
var auth: Auth | null = null;
let workouts: any[];
$: workouts = [];
async function handleSubmit(_submit: SubmitEvent) {
const form = _submit.target as HTMLFormElement;
const formData = new FormData(form);
try {
const response = await fetch(PUBLIC_BASE_API_URL + '/workout', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + (await auth?.currentUser?.getIdToken())
},
body: formData
});
if (response.ok) {
fetchWorkouts();
resetForm();
} else {
addToast('Failed to save workout: ' + (await response.text()), 'error');
}
} catch (error: any) {
addToast('Failed to save workout: ' + error.message, 'error');
}
}
function resetForm() {
const form = document.querySelector('form') as HTMLFormElement;
form.reset();
const date = new Date();
const dateInput = document.getElementById('date') as HTMLInputElement;
dateInput.value = date.toISOString().split('T')[0];
}
async function fetchWorkouts() {
try {
const response = await fetch(PUBLIC_BASE_API_URL + '/workout', {
headers: {
Authorization: 'Bearer ' + (await auth?.currentUser?.getIdToken())
},
method: 'GET'
});
if (response.ok) {
workouts = await response.json();
workouts = workouts.map((workout: any) => {
workout.date = new Date(workout.date).toLocaleDateString();
return workout;
});
} else {
addToast('Failed to fetch workouts: ' + (await response.text()), 'error');
}
} catch (error: any) {
addToast('Failed to fetch workouts: ' + error.message, 'error');
}
}
async function deleteWorkout(id: string) {
console.log('Deleting workout with id: ', id);
try {
const data = new FormData();
data.append('id', id);
const response = await fetch(PUBLIC_BASE_API_URL + '/workout', {
headers: {
Authorization: 'Bearer ' + (await auth?.currentUser?.getIdToken())
},
body: data,
method: 'DELETE'
});
if (response.ok) {
workouts = workouts.filter((workout) => workout.id !== id);
} else {
addToast('Failed to delete workout: ' + (await response.text()), 'error');
}
} catch (error: any) {
addToast('Failed to delete workout: ' + error.message, 'error');
}
}
onMount(async () => {
const authImp = import('$lib/firebase');
resetForm();
auth = (await authImp).auth;
await auth.authStateReady();
if (!auth?.currentUser) {
goto('/signin');
return;
}
await fetchWorkouts();
});
</script>
<main class="mx-2">
<form
class="max-w-xl mx-auto flex flex-col gap-4 justify-center mt-10"
on:submit|preventDefault={handleSubmit}
>
<h2 class="text-4xl mb-8">Track your workout</h2>
<input id="date" type="date" class="input input-bordered" value={new Date()} name="date" />
<select class="select select-bordered w-full" name="type">
<option>Push Ups</option>
<option>Pull Ups</option>
</select>
<input type="number" class="input input-bordered" placeholder="Sets" name="sets" />
<input type="number" class="input input-bordered" placeholder="Reps" name="reps" />
<button class="btn btn-primary self-end">Save</button>
</form>
<div class="overflow-x-auto mx-auto max-w-screen-lg">
<h2 class="text-4xl mt-14 mb-8">Workout history</h2>
<table class="table table-auto max-w-full">
<thead>
<tr>
<th>Date</th>
<th>Type</th>
<th>Sets</th>
<th>Reps</th>
<th></th>
</tr>
</thead>
<tbody>
{#each workouts as workout}
<tr>
<th>{workout.date}</th>
<th>{workout.type}</th>
<th>{workout.sets}</th>
<th>{workout.reps}</th>
<th>
<div class="tooltip" data-tip="Delete Entry">
<button on:click={() => deleteWorkout(workout.id)}>
<MdiDelete class="text-gray-400 text-lg"></MdiDelete>
</button>
</div>
</th>
</tr>
{/each}
</tbody>
</table>
</div>
</main>