kontor-echo: setup project to show homepage
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
Gitea Actions Demo / build-and-test (push) Failing after 11s

This commit is contained in:
Thomas Peetz
2026-11-09 17:02:59 +01:00
parent 074abd3c85
commit debdf3d0b0
63 changed files with 2411 additions and 485 deletions
+6 -2
View File
@@ -1,5 +1,5 @@
# ---------- Stage 1: Build ----------
FROM docker.io/library/golang:1.25-alpine AS builder
FROM docker.io/library/golang:1.26-alpine AS builder
# Set the working directory
WORKDIR /app
# Ensure a portable, static-ish binary
@@ -9,8 +9,12 @@ COPY go.mod go.sum ./
RUN go mod download
# Copy the source code
COPY . .
ARG VERSION="dev"
ARG COMMIT="unset"
ARG DATE="unset"
# Build the Go application (strip debug info for smaller size)
RUN go build -trimpath -ldflags="-s -w" -o kontor cmd/kontor/main.go
RUN go build -trimpath -ldflags="-s -w -X kontor-api-echo/internal/globals.Version=${VERSION} -X kontor-api-echo/internal/globals.Commit=${COMMIT} -X kontor-api-echo/internal/globals.Date=${DATE}" -o kontor cmd/kontor-echo/main.go
# ---------- Stage 2: Final ----------
FROM alpine:latest
+8
View File
@@ -0,0 +1,8 @@
package assets
import (
"embed"
)
//go:embed style/* icons/*
var StaticFiles embed.FS
Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

+174
View File
@@ -0,0 +1,174 @@
* {
box-sizing: border-box;
}
body {
font-family: Arial;
padding: 10px;
background: lightgrey;
padding-top: 56px;
padding-bottom: 56px;
}
/* Header/Blog Title */
.header {
padding: 30px;
text-align: center;
background-color: lightblue;
}
.header h1 {
font-size: 50px;
}
/* Style the top navigation bar */
.topnav {
overflow: hidden;
background-color: darkgrey;
}
/* Style the topnav links */
.topnav a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
/* Change color on hover */
.topnav a:hover {
background-color: #ddd;
color: black;
}
.subnav {
overflow: hidden;
background-color: grey;
}
.subnav a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
.form-inline {
display: flex;
flex-flow: row wrap;
align-items: center;
padding-top: 10px;
padding-left: 20px;
}
.form-inline label {
margin: 5px 10px 5px 0;
}
.form-inline input {
padding-right: 50px;
margin-right: 10px;
}
.form-inline button {
padding: 10px 20px;
background-color: dodgerblue;
border: 1px solid #ddd;
color: white;
border-radius: 10px;
}
.form-inline button:hover {
background-color: royalblue;
}
.pill-nav a {
display: inline-block;
color: white;
background-color: dodgerblue;
text-align: center;
padding: 10px;
text-decoration: none;
border-radius: 10px;
margin-left: 20px;
}
/* Change the color of links on mouse-over */
.pill-nav a:hover {
background-color: royalblue;
color: white;
}
/* Add a color to the active/current link */
.pill-nav a.active {
background-color: royalblue;
color: white;
}
.maincolumn {
float:left;
width: 100%;
}
/* Create two unequal columns that floats next to each other */
/* Left column */
.leftcolumn {
float: left;
width: 75%;
}
/* Right column */
.rightcolumn {
float: left;
width: 25%;
background-color: #f1f1f1;
padding-left: 20px;
}
/* Fake image */
.fakeimg {
background-color: #aaa;
width: 100%;
padding: 20px;
}
/* Add a card effect for articles */
.card {
background-color: white;
padding: 20px;
margin-top: 20px;
}
/* Clear floats after the columns */
.row::after {
content: "";
display: table;
clear: both;
}
/* Footer */
.footer {
padding: 20px;
text-align: center;
background: #ddd;
margin-top: 20px;
}
/* Responsive layout - when the screen is less than 800px wide, make the two columns stack on top of each other instead of next to each other */
@media screen and (max-width: 800px) {
.leftcolumn, .rightcolumn {
width: 100%;
padding: 0;
}
}
/* Responsive layout - when the screen is less than 400px wide, make the navigation links stack on top of each other instead of next to each other */
@media screen and (max-width: 400px) {
.topnav a {
float: none;
width: 100%;
}
}
+2 -1
View File
@@ -31,7 +31,8 @@ func main() {
defer stop()
sc := echo.StartConfig{
Address: ":1323",
//Address: fmt.Sprintf(":%d", config.ServerConfig.Server.Port),
Address: ":8400",
GracefulTimeout: 10 * time.Second,
}
if err := sc.Start(ctx, e); err != nil {
-2
View File
@@ -20,7 +20,6 @@ func main() {
}
e := echo.New()
e.GET("/health", handler.GetHealth)
e.POST("/login", handler.Login)
skipper := func(c echo.Context) bool {
@@ -47,7 +46,6 @@ func main() {
group := e.Group("/api")
group.Use(echojwt.WithConfig(echojwt.Config{SigningKey: []byte("secret")}))
handler.SetupComicRoutes(group)
handler.SetupMediaRoutes(group)
e.Logger.Fatal(e.Start(":8400"))
log.Println("Kontor finished")
+5 -6
View File
@@ -6,18 +6,18 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/google/uuid v1.6.0
github.com/labstack/echo-jwt/v4 v4.4.0
github.com/labstack/echo-jwt/v5 v5.0.2
github.com/labstack/echo/v4 v4.15.0
github.com/labstack/echo/v5 v5.4.4
github.com/stretchr/testify v1.11.1
github.com/uptrace/bun v1.3.0
github.com/uptrace/bun/dialect/pgdialect v1.2.16
github.com/uptrace/bun/dialect/pgdialect v1.3.0
github.com/uptrace/bun/driver/pgdriver v1.2.16
golang.org/x/crypto v0.54.0
)
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/labstack/echo-jwt/v5 v5.0.2 // indirect
github.com/mattn/go-sqlite3 v1.14.34 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
@@ -42,12 +42,11 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.1.0 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/spf13/viper v1.21.0
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/uptrace/bun/dialect/sqlitedialect v1.3.0
@@ -60,7 +59,7 @@ require (
go.opentelemetry.io/otel/trace v1.39.0 // indirect
go.yaml.in/yaml/v3 v3.0.6 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sys v0.49.0 // indirect
golang.org/x/sys v0.50.0 // indirect
golang.org/x/text v0.43.0 // indirect
golang.org/x/time v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+40 -12
View File
@@ -5,6 +5,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
@@ -13,12 +15,18 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo-jwt/v4 v4.4.0 h1:nrXaEnJupfc2R4XChcLRDyghhMZup77F8nIzHnBK19U=
@@ -49,11 +57,11 @@ github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
@@ -71,12 +79,10 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc=
github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM=
github.com/uptrace/bun v1.3.0 h1:hUcbfRpbQk4/upFSI+uZf6jSJw5A0lkIKt0XdpvbUZo=
github.com/uptrace/bun v1.3.0/go.mod h1:wNltaKJk4JtOt4SG5I5zmA7v0/Mzjh1+/S906Rayd3Y=
github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI9/Z5tUv3DE1NVL4=
github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo=
github.com/uptrace/bun/dialect/pgdialect v1.3.0 h1:0ZkGqGugxaPCxko4DV2f4d7i+FnM2Qj3rNpChfhiv54=
github.com/uptrace/bun/dialect/pgdialect v1.3.0/go.mod h1:kP5HGEpAX8iJCqj3WHm04XnoeBPacdCN1P2HiwMtHpg=
github.com/uptrace/bun/dialect/sqlitedialect v1.3.0 h1:XzxkORYksxkLsIQLJHFYUb3yDvqSQHzTungyKpJhHTw=
github.com/uptrace/bun/dialect/sqlitedialect v1.3.0/go.mod h1:x2D1sY50Itio0I/FVdNe30oMUT2ObiAS+oILcMR+Wys=
github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU=
@@ -102,19 +108,21 @@ golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/sys v0.50.0 h1:dkKZEFTEKaLr1sjhs0WiKqoi8Jj5nHbS3Njmx1UoMJs=
golang.org/x/sys v0.50.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
golang.org/x/tools v0.51.0 h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI=
golang.org/x/tools v0.51.0/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -122,11 +130,31 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.30.2 h1:4yPaaq9dXYXZ2V8s1UgrC3KIj580l2N4ClrLwnbv2so=
modernc.org/ccgo/v4 v4.30.2/go.mod h1:yZMnhWEdW0qw3EtCndG1+ldRrVGS+bIwyWmAWzS0XEw=
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ=
modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
+2 -2
View File
@@ -138,8 +138,8 @@ func InitConfig() error {
viper.SetDefault("db.user", "kontor")
viper.SetDefault("db.password", "kontor")
viper.SetDefault("db.path", "data/kontor.db")
viper.SetDefault("server.port", 8550)
viper.SetDefault("gin.mode", "release")
viper.SetDefault("server.host", "127.0.0.1")
viper.SetDefault("server.port", 8400)
// Read the config file
if err := viper.ReadInConfig(); err != nil {
// Handle the case where config file is not found
+114
View File
@@ -0,0 +1,114 @@
package handlers
import (
"kontor-api-echo/internal/config"
"kontor-api-echo/internal/globals"
"kontor-api-echo/internal/helpers"
"kontor-api-echo/internal/models"
"kontor-api-echo/internal/repository"
"kontor-api-echo/internal/service"
"net/http"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
type AuthHandler struct {
Service *service.AuthService
Secret []byte
}
func NewAuthHandler(db *bun.DB) *AuthHandler {
handler := &AuthHandler{Service: service.NewAuthService(repository.NewProfileRepository(db)), Secret: config.ServerConfig.ApiSecret}
return handler
}
func (handler *AuthHandler) Login(c *echo.Context) error {
var input models.LoginInput
if err := c.Bind(&input); err != nil {
return c.JSON(http.StatusBadRequest, map[string]any{"error": err.Error()})
}
profile_id, err := handler.Service.LoginCheck(input.Email, input.Password)
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]any{"error": "username or password is incorrect."})
}
token, err := helpers.GenerateToken(profile_id)
logger := c.Logger()
if err != nil {
logger.Info("Login", "GenerateToken error", err.Error())
}
return c.JSON(http.StatusOK, map[string]any{"token": token})
}
func (handler *AuthHandler) LoginPage(c *echo.Context) error {
cookie, _ := c.Cookie(globals.UserKey)
user := cookie.Value
logger := c.Logger()
logger.Info("LoginPage", globals.UserKey, user)
data := globals.GetDefaultValues(user, "Please logout first")
if user != "" {
return c.Render(http.StatusBadRequest, "login", data)
}
data["message"] = ""
return c.Render(http.StatusOK, "login", data)
}
func (handler *AuthHandler) LoginPost(c *echo.Context) error {
cookie, _ := c.Cookie(globals.UserKey)
user := cookie.Value
logger := c.Logger()
logger.Info("LoginPost", "user", user)
data := globals.GetDefaultValues(user, "Please logout first")
if user != "" {
return c.Render(http.StatusBadRequest, "login", data)
}
username := c.FormValue("username")
password := c.FormValue("password")
if helpers.EmptyUserPass(username, password) {
data["message"] = "Parameters can't be empty"
c.Render(http.StatusBadRequest, "login", data)
}
profile_id, err := handler.Service.LoginCheck(username, password)
if err != nil {
data["message"] = "Incorrect username or password"
c.Render(http.StatusUnauthorized, "login", data)
}
c.SetCookie(&http.Cookie{
Name: globals.UserKey,
Value: profile_id,
MaxAge: 3600,
Path: "/",
Domain: "",
HttpOnly: true,
})
logger.Info("LoginPost: redirect to index")
return c.Redirect(http.StatusMovedPermanently, "/")
}
func (handler *AuthHandler) LogoutPage(c *echo.Context) error {
cookie, _ := c.Cookie(globals.UserKey)
user := cookie.Value
logger := c.Logger()
logger.Info("LogoutPage", globals.UserKey, user)
if user == "" {
logger.Info("Invalid session token")
return c.Redirect(http.StatusMovedPermanently, "/")
}
c.SetCookie(&http.Cookie{
Name: globals.UserKey,
Value: "",
MaxAge: -1, // Deletes the cookie immediately
Path: "/",
Domain: "",
HttpOnly: true,
})
logger.Info("LogoutPage: redirect to index page")
return c.Redirect(http.StatusMovedPermanently, "/")
}
+33
View File
@@ -0,0 +1,33 @@
package handlers
import (
"kontor-api-echo/internal/globals"
"net/http"
"time"
"github.com/labstack/echo/v5"
)
func Health(c *echo.Context) error {
return c.JSON(200, map[string]string{
"ok": "true",
"status": "healthy",
"version": globals.Version,
"time": time.Now().UTC().Format(time.RFC3339),
})
}
func Index(c *echo.Context) error {
logger := c.Logger()
cookie, err := c.Cookie(globals.UserKey)
user := ""
if err != nil {
logger.Info("error reading cookie", "error", err.Error())
} else {
user = cookie.Value
}
logger.Info("IndexGetHandler", globals.UserKey, user)
data := globals.GetDefaultValues(user, "This is an index page...")
return c.Render(http.StatusOK, "home", data)
}
+30
View File
@@ -0,0 +1,30 @@
package handlers
import (
"kontor-api-echo/internal/models"
"kontor-api-echo/internal/repository"
"kontor-api-echo/internal/service"
"net/http"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
type MailHandler struct {
Service *service.MailService
}
func NewMailHandler(db *bun.DB) *MailHandler {
handler := &MailHandler{Service: service.NewMailService(repository.NewMailRepository(db))}
return handler
}
func (handler *MailHandler) List(c *echo.Context) {
mails, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]any{"error": err.Error()})
return
}
dtos := models.ConvertMailsToDtos(mails)
c.JSON(http.StatusOK, dtos)
}
@@ -0,0 +1,30 @@
package handlers
import (
"kontor-api-echo/internal/models"
"kontor-api-echo/internal/repository"
"kontor-api-echo/internal/service"
"net/http"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
type MailAccountHandler struct {
Service *service.MailAccountService
}
func NewMailAccountHandler(db *bun.DB) *MailAccountHandler {
handler := &MailAccountHandler{Service: service.NewMailAccountService(repository.NewMailAccountRepository(db))}
return handler
}
func (handler *MailAccountHandler) List(c *echo.Context) {
mailaccounts, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]any{"error": err.Error()})
return
}
dtos := models.ConvertMailAccountsToDtos(mailaccounts)
c.JSON(http.StatusOK, dtos)
}
+13 -15
View File
@@ -67,35 +67,35 @@ func (handler *MediaActorHandler) Delete(c *echo.Context) {
c.JSON(http.StatusOK, map[string]string{"ok": "true"})
}
func (handler *MediaActorHandler) ListPage(c *echo.Context) {
func (handler *MediaActorHandler) ListPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
actors, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return err
}
data := globals.GetDefaultValues(user, "MediaActors...")
data["actors"] = actors
c.Render(http.StatusOK, "media_actors", data)
return nil
}
func (handler *MediaActorHandler) EditPage(c *echo.Context) {
func (handler *MediaActorHandler) EditPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
//logger := slog.Get(c)
actor, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
data := globals.GetDefaultValues(user, "MediaActors...")
data["actor"] = actor
c.Render(http.StatusOK, "actor_detail", data)
return c.Render(http.StatusOK, "actor_detail", data)
}
func (handler *MediaActorHandler) UpdatePage(c *echo.Context) {
func (handler *MediaActorHandler) UpdatePage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -103,8 +103,7 @@ func (handler *MediaActorHandler) UpdatePage(c *echo.Context) {
actor, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaActorHandler#UpdatePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
var form models.MediaActorForm
if err := c.Bind(&form); err != nil {
@@ -113,15 +112,14 @@ func (handler *MediaActorHandler) UpdatePage(c *echo.Context) {
data["actor"] = actor
data["Form"] = form
data["Errors"] = err.Error()
c.Render(http.StatusBadRequest, "actor_detail", data)
return
return c.Render(http.StatusBadRequest, "actor_detail", data)
}
logger.Info("MediaActorHandler#UpdatePage", "actor", actor)
//handler.Service.Update(c.Request.Context(), form.ConvertFormToMediaLofiDTO())
c.Redirect(http.StatusFound, globals.MediaActorLink)
return c.Redirect(http.StatusFound, globals.MediaActorLink)
}
func (handler *MediaActorHandler) DeletePage(c *echo.Context) {
func (handler *MediaActorHandler) DeletePage(c *echo.Context) error {
//session := sessions.Default(c)
//user := session.Get(globals.UserKey)
id := c.Param("id")
@@ -129,9 +127,9 @@ func (handler *MediaActorHandler) DeletePage(c *echo.Context) {
//actor, err := handler.Service.Get(c.Request.Context(), id)
// if err != nil {
// logger.Info("MediaActorHandler#DeletePage", "error", err.Error())
// c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
// c.JSON(http.StatusInternalServerError, map[string]any{"error": err.Error()})
// return
// }
logger.Info("MediaActorHandler#DeletePage", "actor", id)
c.Redirect(http.StatusFound, globals.MediaActorLink)
return c.Redirect(http.StatusFound, globals.MediaActorLink)
}
+19 -26
View File
@@ -22,44 +22,41 @@ func NewMediaArticleHandler(db *bun.DB) *MediaArticleHandler {
return mediaFileHandler
}
func (handler *MediaArticleHandler) List(c *echo.Context) {
func (handler *MediaArticleHandler) List(c *echo.Context) error {
logger := c.Logger()
logger.Info("MediaArticleHandler#List")
articles, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
dtos := models.ConvertMediaArticlesToDtos(articles)
c.JSON(http.StatusOK, dtos)
return c.JSON(http.StatusOK, dtos)
}
func (handler *MediaArticleHandler) Delete(c *echo.Context) {
func (handler *MediaArticleHandler) Delete(c *echo.Context) error {
id := c.Param("id")
err := handler.Service.Delete(c.Request().Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not found"})
return
return c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not found"})
}
c.JSON(http.StatusOK, map[string]string{"ok": "true"})
return c.JSON(http.StatusOK, map[string]string{"ok": "true"})
}
func (handler *MediaArticleHandler) ListPage(c *echo.Context) {
func (handler *MediaArticleHandler) ListPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
files, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
data := globals.GetDefaultValues(user, "MediaArticles...")
data["title"] = "Kontor MediaArticles"
data["media_links"] = files
data["MediaLinksUrl"] = globals.MediaArticleLink
c.Render(http.StatusOK, "media_links", data)
return c.Render(http.StatusOK, "media_links", data)
}
func (handler *MediaArticleHandler) EditPage(c *echo.Context) {
func (handler *MediaArticleHandler) EditPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -67,16 +64,15 @@ func (handler *MediaArticleHandler) EditPage(c *echo.Context) {
media_file, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaArticleHandler#EditPage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaArticleHandler#EditPage", "media_file", media_file)
data := globals.GetDefaultValues(user, "MediaArticle Details...")
data["lofi"] = media_file
c.Render(http.StatusOK, "lofi_detail", data)
return c.Render(http.StatusOK, "lofi_detail", data)
}
func (handler *MediaArticleHandler) UpdatePage(c *echo.Context) {
func (handler *MediaArticleHandler) UpdatePage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -84,8 +80,7 @@ func (handler *MediaArticleHandler) UpdatePage(c *echo.Context) {
media_article, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaArticleHandler#UpdatePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
var form models.MediaArticleForm
if err := c.Bind(&form); err != nil {
@@ -93,23 +88,21 @@ func (handler *MediaArticleHandler) UpdatePage(c *echo.Context) {
data := globals.GetDefaultValues(user, "MediaArticle Details...")
data["Form"] = form
data["Errors"] = err.Error()
c.Render(http.StatusBadRequest, "lofi_detail", data)
return
return c.Render(http.StatusBadRequest, "lofi_detail", data)
}
logger.Info("MediaArticleHandler#UpdatePage", "media_article", media_article)
//handler.Service.Update(c.Request.Context(), form.ConvertFormToMediaFileDTO())
c.Redirect(http.StatusFound, globals.MediaArticleLink)
return c.Redirect(http.StatusFound, globals.MediaArticleLink)
}
func (handler *MediaArticleHandler) DeletePage(c *echo.Context) {
func (handler *MediaArticleHandler) DeletePage(c *echo.Context) error {
id := c.Param("id")
logger := c.Logger()
err := handler.Service.Delete(c.Request().Context(), id)
if err != nil {
logger.Info("MediaArticleHandler#DeletePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaArticleHandler#DeletePage", "lofi", id)
c.Redirect(http.StatusFound, globals.MediaArticleLink)
return c.Redirect(http.StatusFound, globals.MediaArticleLink)
}
+23 -29
View File
@@ -22,59 +22,57 @@ func NewMediaFileHandler(db *bun.DB) *MediaFileHandler {
return mediaFileHandler
}
func (handler *MediaFileHandler) List(c *echo.Context) {
func (handler *MediaFileHandler) List(c *echo.Context) error {
logger := c.Logger()
logger.Info("MediaFileHandler#List")
files, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return err
}
dtos := models.ConvertMediaFilesToDtos(files)
c.JSON(http.StatusOK, dtos)
return c.JSON(http.StatusOK, dtos)
}
func (handler *MediaFileHandler) Add(c *echo.Context) {
func (handler *MediaFileHandler) Add(c *echo.Context) error {
var input models.MediaFileAdd
if err := c.Bind(&input); err != nil {
c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
return
return err
}
lofi, err := handler.Service.Add(c.Request().Context(), input.Url)
if err != nil {
c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not added"})
return
return err
}
c.JSON(http.StatusOK, lofi)
return c.JSON(http.StatusOK, lofi)
}
func (handler *MediaFileHandler) Delete(c *echo.Context) {
func (handler *MediaFileHandler) Delete(c *echo.Context) error {
id := c.Param("id")
err := handler.Service.Delete(c.Request().Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not found"})
return
return c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not found"})
}
c.JSON(http.StatusOK, map[string]string{"ok": "true"})
return c.JSON(http.StatusOK, map[string]string{"ok": "true"})
}
func (handler *MediaFileHandler) ListPage(c *echo.Context) {
func (handler *MediaFileHandler) ListPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
files, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
data := globals.GetDefaultValues(user, "MediaFiles...")
data["title"] = "Kontor MediaFiles"
data["media_links"] = files
data["MediaLinksUrl"] = globals.MediaVideoLink
c.Render(http.StatusOK, "media_links", data)
return c.Render(http.StatusOK, "media_links", data)
}
func (handler *MediaFileHandler) EditPage(c *echo.Context) {
func (handler *MediaFileHandler) EditPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -82,16 +80,15 @@ func (handler *MediaFileHandler) EditPage(c *echo.Context) {
media_file, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaLoFiHandler#EditPage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaLoFiHandler#EditPage", "media_file", media_file)
data := globals.GetDefaultValues(user, "MediaFile Details...")
data["lofi"] = media_file
c.Render(http.StatusOK, "lofi_detail", data)
return c.Render(http.StatusOK, "lofi_detail", data)
}
func (handler *MediaFileHandler) UpdatePage(c *echo.Context) {
func (handler *MediaFileHandler) UpdatePage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -99,8 +96,7 @@ func (handler *MediaFileHandler) UpdatePage(c *echo.Context) {
media_file, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaFileHandler#UpdatePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
var form models.MediaFileForm
if err := c.Bind(&form); err != nil {
@@ -108,15 +104,14 @@ func (handler *MediaFileHandler) UpdatePage(c *echo.Context) {
data := globals.GetDefaultValues(user, "MediaFile Details...")
data["Form"] = form
data["Errors"] = err.Error()
c.Render(http.StatusBadRequest, "lofi_detail", data)
return
return c.Render(http.StatusBadRequest, "lofi_detail", data)
}
logger.Info("MediaFileHandler#UpdatePage", "media_file", media_file)
//handler.Service.Update(c.Request.Context(), form.ConvertFormToMediaFileDTO())
c.Redirect(http.StatusFound, globals.MediaFileLink)
return c.Redirect(http.StatusFound, globals.MediaFileLink)
}
func (handler *MediaFileHandler) DeletePage(c *echo.Context) {
func (handler *MediaFileHandler) DeletePage(c *echo.Context) error {
//session := sessions.Default(c)
//user := session.Get(globals.UserKey)
id := c.Param("id")
@@ -124,9 +119,8 @@ func (handler *MediaFileHandler) DeletePage(c *echo.Context) {
err := handler.Service.Delete(c.Request().Context(), id)
if err != nil {
logger.Info("MediaFileHandler#DeletePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaFileHandler#DeletePage", "lofi", id)
c.Redirect(http.StatusFound, globals.MediaFileLink)
return c.Redirect(http.StatusFound, globals.MediaFileLink)
}
+26 -35
View File
@@ -59,33 +59,31 @@ func (handler *MediaLoFiHandler) Add(c *echo.Context) {
c.JSON(http.StatusOK, lofi)
}
func (handler *MediaLoFiHandler) Update(c *echo.Context) {
func (handler *MediaLoFiHandler) Update(c *echo.Context) error {
var input models.MediaLoFiDTO
if err := c.Bind(&input); err != nil {
c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
lofi, err := handler.Service.Update(c.Request().Context(), input)
if err != nil {
logger := c.Logger()
logger.Info("MediaLoFiHandler#Update", "error", err.Error())
c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not updated"})
return
return c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not updated"})
}
c.JSON(http.StatusOK, lofi)
return c.JSON(http.StatusOK, lofi)
}
func (handler *MediaLoFiHandler) Delete(c *echo.Context) {
func (handler *MediaLoFiHandler) Delete(c *echo.Context) error {
id := c.Param("id")
err := handler.Service.Delete(c.Request().Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not found"})
return
return c.JSON(http.StatusNotFound, map[string]string{"error": "lofi not found"})
}
c.JSON(http.StatusOK, map[string]string{"ok": "true"})
return c.JSON(http.StatusOK, map[string]string{"ok": "true"})
}
func (handler *MediaLoFiHandler) ListPage(c *echo.Context) {
func (handler *MediaLoFiHandler) ListPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
logger := c.Logger()
@@ -113,8 +111,7 @@ func (handler *MediaLoFiHandler) ListPage(c *echo.Context) {
lofis, err := handler.Service.List(c.Request().Context())
if err != nil {
logger.Info("MediaLoFiHandler#ListPage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaLoFiHandler#ListPage", "lofis count", len(lofis))
data := globals.GetDefaultValues(user, "MediaFiles...")
@@ -122,10 +119,10 @@ func (handler *MediaLoFiHandler) ListPage(c *echo.Context) {
data["isReview"] = review
data["isDownload"] = download
data["MediaLinksUrl"] = globals.MediaLofiLink
c.Render(http.StatusOK, "media_links", data)
return c.Render(http.StatusOK, "media_links", data)
}
func (handler *MediaLoFiHandler) EditPage(c *echo.Context) {
func (handler *MediaLoFiHandler) EditPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -146,25 +143,23 @@ func (handler *MediaLoFiHandler) EditPage(c *echo.Context) {
lofi, err = handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaLoFiHandler#EditPage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
}
logger.Info("MediaLoFiHandler#EditPage", "lofi", lofi)
data := globals.GetDefaultValues(user, "MediaLofis...")
data["media"] = lofi
data["MediaLinksUrl"] = globals.MediaLofiLink
c.Render(http.StatusOK, "media_detail", data)
return c.Render(http.StatusOK, "media_detail", data)
}
func (handler *MediaLoFiHandler) ChangeReviewPage(c *echo.Context) {
func (handler *MediaLoFiHandler) ChangeReviewPage(c *echo.Context) error {
id := c.Param("id")
logger := c.Logger()
lofi, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaLoFiHandler#ChangeReviewPage", "error", err.Error())
c.Redirect(http.StatusNotFound, globals.MediaLofiLink)
return
return c.Redirect(http.StatusNotFound, globals.MediaLofiLink)
}
lofi.Review = !lofi.Review
handler.Service.Update(c.Request().Context(), lofi.ConvertMediaLoFiToDTO())
@@ -174,17 +169,16 @@ func (handler *MediaLoFiHandler) ChangeReviewPage(c *echo.Context) {
lofis, err := handler.Service.List(c.Request().Context())
if err != nil {
logger.Info("MediaLoFiHandler#ChangeReviewPage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaLoFiHandler#ListPage", "lofis count", len(lofis))
data := globals.GetDefaultValues(user, "MediaLofis...")
data["lofis"] = lofis
//c.HTML(http.StatusOK, "lofi", data)
c.Redirect(http.StatusCreated, globals.MediaLofiLink)
//c.Render(http.StatusOK, "lofi", data)
return c.Redirect(http.StatusCreated, globals.MediaLofiLink)
}
func (handler *MediaLoFiHandler) UpdatePage(c *echo.Context) {
func (handler *MediaLoFiHandler) UpdatePage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -192,25 +186,23 @@ func (handler *MediaLoFiHandler) UpdatePage(c *echo.Context) {
lofi, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaLoFiHandler#UpdatePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
var form models.MediaLoFiForm
if err := c.Bind(&form); err != nil {
logger.Info("MediaLoFiHandler#UpdatePage", "error", err.Error())
c.Render(http.StatusBadRequest, "lofi_detail", map[string]any{
return c.Render(http.StatusBadRequest, "lofi_detail", map[string]any{
"Form": form,
"Errors": err.Error(),
"user": user,
})
return
}
logger.Info("MediaLoFiHandler#UpdatePage", "lofi", lofi)
//handler.Service.Update(c.Request.Context(), form.ConvertFormToMediaLofiDTO())
c.Redirect(http.StatusFound, globals.MediaLofiLink)
return c.Redirect(http.StatusFound, globals.MediaLofiLink)
}
func (handler *MediaLoFiHandler) DeletePage(c *echo.Context) {
func (handler *MediaLoFiHandler) DeletePage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -218,11 +210,10 @@ func (handler *MediaLoFiHandler) DeletePage(c *echo.Context) {
lofi, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaLoFiHandler#DeletePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaLoFiHandler#DeletePage", "lofi", id)
c.Render(http.StatusOK, "lofi_delete", map[string]any{
return c.Render(http.StatusOK, "lofi_delete", map[string]any{
"title": "Kontor MediaFiles",
"version": "0.4.0",
"message": "MediaFiles...",
+31 -42
View File
@@ -22,68 +22,62 @@ func NewMediaMusicHandler(db *bun.DB) *MediaMusicHandler {
return mediaMusicHandler
}
func (handler *MediaMusicHandler) List(c *echo.Context) {
func (handler *MediaMusicHandler) List(c *echo.Context) error {
musics, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
dtos := models.ConvertMediaMusicsToDtos(musics)
c.JSON(http.StatusOK, dtos)
return c.JSON(http.StatusOK, dtos)
}
func (handler *MediaMusicHandler) Get(c *echo.Context) {
func (handler *MediaMusicHandler) Get(c *echo.Context) error {
id := c.Param("id")
music, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, map[string]string{"error": "MediaMusic not found"})
return
return c.JSON(http.StatusNotFound, map[string]string{"error": "MediaMusic not found"})
}
c.JSON(http.StatusOK, music)
return c.JSON(http.StatusOK, music)
}
func (handler *MediaMusicHandler) Add(c *echo.Context) {
func (handler *MediaMusicHandler) Add(c *echo.Context) error {
var input models.MediaLofiAdd
if err := c.Bind(&input); err != nil {
c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
music, err := handler.Service.Add(c.Request().Context(), input.Url)
if err != nil {
c.JSON(http.StatusNotFound, map[string]string{"error": "MediaMusic not added"})
return
return c.JSON(http.StatusNotFound, map[string]string{"error": "MediaMusic not added"})
}
c.JSON(http.StatusOK, music)
return c.JSON(http.StatusOK, music)
}
func (handler *MediaMusicHandler) Update(c *echo.Context) {
func (handler *MediaMusicHandler) Update(c *echo.Context) error {
var input models.MediaMusicDTO
if err := c.Bind(&input); err != nil {
c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
music, err := handler.Service.Update(c.Request().Context(), input)
if err != nil {
logger := c.Logger()
logger.Info("MediaMusicHandler#Update", "error", err.Error())
c.JSON(http.StatusNotFound, map[string]string{"error": "MediaMusic not updated"})
return
return c.JSON(http.StatusNotFound, map[string]string{"error": "MediaMusic not updated"})
}
c.JSON(http.StatusOK, music)
return c.JSON(http.StatusOK, music)
}
func (handler *MediaMusicHandler) Delete(c *echo.Context) {
func (handler *MediaMusicHandler) Delete(c *echo.Context) error {
id := c.Param("id")
err := handler.Service.Delete(c.Request().Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, map[string]string{"error": "MediaMusic not found"})
return
return c.JSON(http.StatusNotFound, map[string]string{"error": "MediaMusic not found"})
}
c.JSON(http.StatusOK, map[string]string{"ok": "true"})
return c.JSON(http.StatusOK, map[string]string{"ok": "true"})
}
func (handler *MediaMusicHandler) ListPage(c *echo.Context) {
func (handler *MediaMusicHandler) ListPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
logger := c.Logger()
@@ -111,8 +105,7 @@ func (handler *MediaMusicHandler) ListPage(c *echo.Context) {
musics, err := handler.Service.List(c.Request().Context())
if err != nil {
logger.Info("MediaMusicHandler#ListPage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaMusicHandler#ListPage", "MediaMusic count", len(musics))
data := globals.GetDefaultValues(user, "MediaMusic...")
@@ -120,10 +113,10 @@ func (handler *MediaMusicHandler) ListPage(c *echo.Context) {
data["MediaLinksUrl"] = globals.MediaMusicLink
data["isReview"] = review
data["isDownload"] = download
c.Render(http.StatusOK, "media_links", data)
return c.Render(http.StatusOK, "media_links", data)
}
func (handler *MediaMusicHandler) EditPage(c *echo.Context) {
func (handler *MediaMusicHandler) EditPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -131,16 +124,15 @@ func (handler *MediaMusicHandler) EditPage(c *echo.Context) {
music, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaMusicHandler#EditPage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaMusicHandler#EditPage", "music", music)
data := globals.GetDefaultValues(user, "MediaMusic...")
data["lofi"] = music
c.Render(http.StatusOK, "lofi_detail", data)
return c.Render(http.StatusOK, "lofi_detail", data)
}
func (handler *MediaMusicHandler) UpdatePage(c *echo.Context) {
func (handler *MediaMusicHandler) UpdatePage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -148,8 +140,7 @@ func (handler *MediaMusicHandler) UpdatePage(c *echo.Context) {
music, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaMusicHandler#UpdatePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
var form models.MediaMusicForm
if err := c.Bind(&form); err != nil {
@@ -157,15 +148,14 @@ func (handler *MediaMusicHandler) UpdatePage(c *echo.Context) {
data := globals.GetDefaultValues(user, "MediaMusic...")
data["Form"] = form
data["Errors"] = err.Error()
c.Render(http.StatusBadRequest, "lofi_detail", data)
return
return c.Render(http.StatusBadRequest, "lofi_detail", data)
}
logger.Info("MediaMusicHandler#UpdatePage", "music", music)
//handler.Service.Update(c.Request.Context(), form.ConvertFormToMediaLofiDTO())
c.Redirect(http.StatusFound, globals.MediaMusicLink)
return c.Redirect(http.StatusFound, globals.MediaMusicLink)
}
func (handler *MediaMusicHandler) DeletePage(c *echo.Context) {
func (handler *MediaMusicHandler) DeletePage(c *echo.Context) error {
// session := sessions.Default(c)
// user := session.Get(globals.UserKey)
id := c.Param("id")
@@ -173,9 +163,8 @@ func (handler *MediaMusicHandler) DeletePage(c *echo.Context) {
err := handler.Service.Delete(c.Request().Context(), id)
if err != nil {
logger.Info("MediaMusicHandler#DeletePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaMusicHandler#DeletePage", "music", id)
c.Redirect(http.StatusFound, globals.MediaMusicLink)
return c.Redirect(http.StatusFound, globals.MediaMusicLink)
}
+16 -22
View File
@@ -20,36 +20,34 @@ func NewMediaVideoHandler(db *bun.DB) *MediaVideoHandler {
return handler
}
func (handler *MediaVideoHandler) List(c *echo.Context) {
func (handler *MediaVideoHandler) List(c *echo.Context) error {
logger := c.Logger()
logger.Info("MediaVideoHandler#List")
videos, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
dtos := models.ConvertMediaVideosToDtos(videos)
c.JSON(http.StatusOK, dtos)
return c.JSON(http.StatusOK, dtos)
}
func (handler *MediaVideoHandler) ListPage(c *echo.Context) {
func (handler *MediaVideoHandler) ListPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
logger := c.Logger()
videos, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaVideoHandler#ListPage", "videos count", len(videos))
data := globals.GetDefaultValues(user, "MediaVideos...")
data["title"] = "Kontor MediaVideos"
data["media_links"] = videos
data["MediaLinksUrl"] = globals.MediaVideoLink
c.Render(http.StatusOK, "media_links", data)
return c.Render(http.StatusOK, "media_links", data)
}
func (handler *MediaVideoHandler) EditPage(c *echo.Context) {
func (handler *MediaVideoHandler) EditPage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -57,16 +55,15 @@ func (handler *MediaVideoHandler) EditPage(c *echo.Context) {
video, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaVideoHandler#EditPage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaVideoHandler#EditPage", "lofi", video)
data := globals.GetDefaultValues(user, "MediaLofis...")
data["lofi"] = video
c.Render(http.StatusOK, "lofi_detail", data)
return c.Render(http.StatusOK, "lofi_detail", data)
}
func (handler *MediaVideoHandler) UpdatePage(c *echo.Context) {
func (handler *MediaVideoHandler) UpdatePage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -74,25 +71,23 @@ func (handler *MediaVideoHandler) UpdatePage(c *echo.Context) {
video, err := handler.Service.Get(c.Request().Context(), id)
if err != nil {
logger.Info("MediaVideoHandler#UpdatePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
var form models.MediaVideoForm
if err := c.Bind(&form); err != nil {
logger.Info("MediaVideoHandler#UpdatePage", "error", err.Error())
c.Render(http.StatusBadRequest, "lofi_detail", map[string]any{
return c.Render(http.StatusBadRequest, "lofi_detail", map[string]any{
"Form": form,
"Errors": err.Error(),
"user": user,
})
return
}
logger.Info("MediaVideoHandler#UpdatePage", "lofi", video)
//handler.Service.Update(c.Request.Context(), form.ConvertFormToMediaLofiDTO())
c.Redirect(http.StatusFound, globals.MediaVideoLink)
return c.Redirect(http.StatusFound, globals.MediaVideoLink)
}
func (handler *MediaVideoHandler) DeletePage(c *echo.Context) {
func (handler *MediaVideoHandler) DeletePage(c *echo.Context) error {
cookie, err := c.Cookie(globals.UserKey)
user := cookie.Value
id := c.Param("id")
@@ -100,11 +95,10 @@ func (handler *MediaVideoHandler) DeletePage(c *echo.Context) {
err = handler.Service.Delete(c.Request().Context(), id)
if err != nil {
logger.Info("MediaVideoHandler#DeletePage", "error", err.Error())
c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
logger.Info("MediaVideoHandler#DeletePage", "video", id)
c.Render(http.StatusOK, "lofi_delete", map[string]string{
return c.Render(http.StatusOK, "lofi_delete", map[string]string{
"title": "Kontor MediaFiles",
"version": "0.4.0",
"message": "MediaFiles...",
+30
View File
@@ -0,0 +1,30 @@
package handlers
import (
"kontor-api-echo/internal/models"
"kontor-api-echo/internal/repository"
"kontor-api-echo/internal/service"
"net/http"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
type TokenHandler struct {
Service *service.TokenService
}
func NewTokenHandler(db *bun.DB) *TokenHandler {
handler := &TokenHandler{Service: service.NewTokenService(repository.NewTokenRepository(db))}
return handler
}
func (handler *TokenHandler) List(c *echo.Context) {
tokens, err := handler.Service.List(c.Request().Context())
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]any{"error": err.Error()})
return
}
dtos := models.ConvertTokensToDtos(tokens)
c.JSON(http.StatusOK, dtos)
}
+7
View File
@@ -0,0 +1,7 @@
package helpers
import "strings"
func EmptyUserPass(username, password string) bool {
return strings.Trim(username, " ") == "" || strings.Trim(password, " ") == ""
}
+88
View File
@@ -0,0 +1,88 @@
package helpers
import (
"fmt"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo/v5"
"github.com/spf13/viper"
)
func GenerateToken(profile_id string) (string, error) {
// token_lifespan := viper.GetInt("token_hour_lifespan")
claims := jwt.MapClaims{
"name": "Thomas Peetz",
"admin": true,
"authorized": true,
"sub": profile_id,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// claims["authorized"] = true
// claims["profile_id"] = profile_id
// claims["exp"] = time.Now().Add(time.Hour * time.Duration(token_lifespan)).Unix()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(viper.GetString("api_secret")))
}
func TokenValid(c *echo.Context) error {
logger := c.Logger()
tokenString := ExtractToken(c)
_, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
logger.Info("TokenValid", "Unexpected signing method", token.Header["alg"])
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return []byte(viper.GetString("api_secret")), nil
})
if err != nil {
return err
}
return nil
}
func ExtractToken(c *echo.Context) string {
logger := c.Logger()
token := c.QueryParam("token")
if token != "" {
logger.Info("ExtractToken", "token from query", token)
return token
}
bearerToken := c.Request().Header.Get("Authorization")
if len(strings.Split(bearerToken, " ")) == 2 {
logger.Info("ExtractToken", "token from query", token)
return strings.Split(bearerToken, " ")[1]
}
return ""
}
func ExtractTokenID(c *echo.Context) (string, error) {
tokenString := ExtractToken(c)
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return []byte(viper.GetString("api_secret")), nil
})
if err != nil {
return "", err
}
claims, ok := token.Claims.(jwt.MapClaims)
if ok && token.Valid {
uid, ok := claims["profile_id"].(string)
if !ok {
return "", err
}
return uid, nil
}
return "", nil
}
+69
View File
@@ -0,0 +1,69 @@
package helpers
import (
"encoding/json"
"fmt"
"io/ioutil"
"log/slog"
"net/http"
"net/url"
)
func StripUrl(webLink string, keepParam string) string {
u, err := url.Parse(webLink)
if err != nil {
slog.Info("StripUrl", "error", err)
return webLink
}
values := u.Query()
for key := range values {
if key != keepParam {
values.Del(key)
}
}
u.RawQuery = values.Encode()
return u.String()
}
func GetParam(webLink string, param string) string {
u, err := url.Parse(webLink)
if err != nil {
slog.Info("StripUrl", "error", err)
return webLink
}
values := u.Query()
for key := range values {
if key == param {
return values.Get(key)
}
}
return ""
}
type OEmbedResponse struct {
Title string `json:"title"`
}
func GetVideoTitle(videoUrl string) (string, error) {
videoID := GetParam(videoUrl, "v")
// Construct the oEmbed URL
url := fmt.Sprintf("https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=%s&format=json", videoID)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var data OEmbedResponse
if err := json.Unmarshal(body, &data); err != nil {
return "", err
}
return data.Title, nil
}
+73
View File
@@ -0,0 +1,73 @@
package models
import (
"kontor-api-echo/internal/helpers"
"time"
"github.com/uptrace/bun"
)
type Mail struct {
bun.BaseModel `bun:"table:mail"`
ID string `bun:"id,pk"`
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
DeletedAt *time.Time `bun:"deleted_date,nullzero"`
Version int `bun:"version,default:0"`
Folder string `bun:"folder"`
Subject string `bun:"subject"`
Body string `bun:"body"`
SendAt time.Time `bun:"sent_date"`
ReceivedAt time.Time `bun:"received_date"`
}
type MailDTO struct {
ID string `json:"id" binding:"required"`
CreatedAt helpers.KontorTime `json:"created_date"`
UpdatedAt helpers.KontorTime `json:"last_modified_date"`
DeletedAt *time.Time `json:"deleted_date.omitempty"`
Version int `json:"version"`
Folder string `json:"folder"`
Subject string `json:"subject"`
Body string `json:"body"`
SendAt time.Time `json:"sent_date"`
ReceivedAt time.Time `json:"received_date"`
}
func (dto MailDTO) ConvertDTOToModel() Mail {
model := Mail{
ID: dto.ID,
CreatedAt: dto.CreatedAt.Time,
UpdatedAt: dto.UpdatedAt.Time,
DeletedAt: dto.DeletedAt,
Version: dto.Version,
Folder: dto.Folder,
Subject: dto.Subject,
Body: dto.Body,
SendAt: dto.SendAt,
ReceivedAt: dto.ReceivedAt,
}
return model
}
func ConvertMailsToDtos(mails []Mail) []MailDTO {
dtos := make([]MailDTO, len(mails))
for i, mail := range mails {
dtos[i] = MailDTO{
ID: mail.ID,
CreatedAt: helpers.KontorTime{Time: mail.CreatedAt},
UpdatedAt: helpers.KontorTime{Time: mail.UpdatedAt},
DeletedAt: mail.DeletedAt,
Version: mail.Version,
Folder: mail.Folder,
Subject: mail.Subject,
Body: mail.Body,
SendAt: mail.SendAt,
ReceivedAt: mail.ReceivedAt,
}
}
return dtos
}
type MailDTOList []MailDTO
@@ -0,0 +1,77 @@
package models
import (
"kontor-api-echo/internal/helpers"
"time"
"github.com/uptrace/bun"
)
type MailAccount struct {
bun.BaseModel `bun:"table:mail_account"`
ID string `bun:"id,pk"`
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
DeletedAt *time.Time `bun:"deleted_date,nullzero"`
Version int `bun:"version,default:0"`
Host string `bun:"host"`
Port int `bun:"port"`
Protocol string `bun:"protocol"`
UserName string `bun:"user_name"`
Password string `bun:"password"`
StartTLS bool `bun:"start_tls"`
}
type MailAccountDTO struct {
ID string `json:"id" binding:"required"`
CreatedAt helpers.KontorTime `json:"created_date"`
UpdatedAt helpers.KontorTime `json:"last_modified_date"`
DeletedAt *time.Time `json:"deleted_date,omitempty"`
Version int `json:"version"`
Host string `json:"host"`
Port int `json:"port"`
Protocol string `json:"protocol"`
UserName string `json:"user_name"`
Password string `json:"password"`
StartTLS bool `json:"start_tls"`
}
func (dto MailAccountDTO) ConvertDTOToModel() MailAccount {
model := MailAccount{
ID: dto.ID,
CreatedAt: dto.CreatedAt.Time,
UpdatedAt: dto.UpdatedAt.Time,
DeletedAt: dto.DeletedAt,
Version: dto.Version,
Host: dto.Host,
Port: dto.Port,
Protocol: dto.Protocol,
UserName: dto.UserName,
Password: dto.Password,
StartTLS: dto.StartTLS,
}
return model
}
func ConvertMailAccountsToDtos(mailaccounts []MailAccount) []MailAccountDTO {
dtos := make([]MailAccountDTO, len(mailaccounts))
for i, mailaccount := range mailaccounts {
dtos[i] = MailAccountDTO{
ID: mailaccount.ID,
CreatedAt: helpers.KontorTime{Time: mailaccount.CreatedAt},
UpdatedAt: helpers.KontorTime{Time: mailaccount.UpdatedAt},
DeletedAt: mailaccount.DeletedAt,
Version: mailaccount.Version,
Host: mailaccount.Host,
Port: mailaccount.Port,
Protocol: mailaccount.Protocol,
UserName: mailaccount.UserName,
Password: mailaccount.Password,
StartTLS: mailaccount.StartTLS,
}
}
return dtos
}
type MailAccountDTOList []MailAccountDTO
+30 -21
View File
@@ -4,7 +4,9 @@ import (
"context"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
@@ -19,41 +21,48 @@ func NewAssignmentRepository(db *bun.DB) *AssignmentRepository {
func (repository *AssignmentRepository) FindAll(context context.Context) ([]models.Assignment, error) {
var assignments []models.Assignment
result := repository.db.Find(&assignments)
slog.Info("AssignmentRepository#FindAll", "count", result.RowsAffected)
err := repository.db.NewSelect().Model(&assignments).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("AssignmentRepository#FindAll", "count", len(assignments))
return assignments, nil
}
func (repository *AssignmentRepository) GetById(context context.Context, id string) (models.Assignment, error) {
func (repository *AssignmentRepository) GetById(context context.Context, id string) (*models.Assignment, error) {
var assignment = models.Assignment{ID: id}
slog.Info("AssignmentRepository#GetById", "id", id)
result := repository.db.First(&assignment)
if result.Error != nil {
slog.Info("AssignmentRepository#GetById", "error", result.Error.Error())
err := repository.db.NewSelect().Model(&assignment).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return assignment, result.Error
return &assignment, err
}
func (repository *AssignmentRepository) Import(assignments models.AssignmentDTOList) {
func (repository *AssignmentRepository) Import(dtos models.AssignmentDTOList) {
slog.Info("AssignmentRepository#Import")
for index, assignment := range assignments {
slog.Info("importing", "index", index, "profile", assignment)
model := assignment.ConvertDTOToModel()
existing_item := models.Assignment{ID: assignment.ID}
result_existing := repository.db.First(&existing_item)
if result_existing.Error != nil {
for index, dto := range dtos {
slog.Info("importing", "index", index, "assignment", dto)
model := dto.ConvertDTOToModel()
existing_item := models.Assignment{ID: dto.ID}
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
result := repository.db.Create(&model)
if result.Error != nil {
slog.Info("error during import", "error", result.Error.Error())
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
result := repository.db.Save(&model)
if result.Error != nil {
slog.Info("error during updating", "error", result.Error.Error())
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
+70
View File
@@ -0,0 +1,70 @@
package repository
import (
"context"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
type MailRepository struct {
db *bun.DB
}
func NewMailRepository(db *bun.DB) *MailRepository {
repository := &MailRepository{db: db}
return repository
}
func (repository *MailRepository) FindAll(context context.Context) ([]models.Mail, error) {
var models []models.Mail
err := repository.db.NewSelect().Model(&models).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("MailRepository#FindAll", "count", len(models))
return models, nil
}
func (repository *MailRepository) GetById(context context.Context, id string) (*models.Mail, error) {
var model = models.Mail{ID: id}
slog.Info("MailRepository#GetById", "id", id)
err := repository.db.NewSelect().Model(&model).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return &model, err
}
func (repository *MailRepository) Import(dtos models.MailDTOList) {
slog.Info("MailRepository#Import")
for index, dto := range dtos {
slog.Info("importing", "index", index, "profile", dto)
model := dto.ConvertDTOToModel()
existing_item := models.Mail{ID: dto.ID}
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
}
@@ -0,0 +1,70 @@
package repository
import (
"context"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
type MailAccountRepository struct {
db *bun.DB
}
func (repository *MailAccountRepository) Import(dtos models.MailAccountDTOList) {
slog.Info("MailAccountRepository#Import")
for index, dto := range dtos {
slog.Info("importing", "index", index, "mailaccount", dto)
model := dto.ConvertDTOToModel()
existing_item := models.MailAccount{ID: dto.ID}
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
}
func NewMailAccountRepository(db *bun.DB) *MailAccountRepository {
repository := &MailAccountRepository{db: db}
return repository
}
func (repository *MailAccountRepository) FindAll(context context.Context) ([]models.MailAccount, error) {
var models []models.MailAccount
err := repository.db.NewSelect().Model(&models).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("MailRepository#FindAll", "count", len(models))
return models, nil
}
func (repository *MailAccountRepository) GetById(context context.Context, id string) (*models.MailAccount, error) {
var model = models.MailAccount{ID: id}
slog.Info("MailAccountRepository#GetById", "id", id)
err := repository.db.NewSelect().Model(&model).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return &model, err
}
+27 -17
View File
@@ -5,7 +5,9 @@ import (
"fmt"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
@@ -19,18 +21,21 @@ func (repository *MediaActorRepository) Import(dtos models.MediaActorDTOList) {
slog.Info("importing", "index", index, "video", dto)
model := dto.ConvertDTOToModel()
existing_item := models.MediaActor{ID: dto.ID}
result_existing := repository.db.First(&existing_item)
if result_existing.Error != nil {
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
result := repository.db.Create(&model)
if result.Error != nil {
slog.Info("error during import", "error", result.Error.Error())
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
result := repository.db.Save(&model)
if result.Error != nil {
slog.Info("error during updating", "error", result.Error.Error())
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
@@ -42,22 +47,27 @@ func NewMediaActorRepository(db *bun.DB) *MediaActorRepository {
}
func (repository *MediaActorRepository) FindAll(context context.Context) ([]models.MediaActor, error) {
var actors []models.MediaActor
var models []models.MediaActor
result := repository.db.Find(&actors)
slog.Info("MediaActorRepository#FindAll", "count", result.RowsAffected)
err := repository.db.NewSelect().Model(&models).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("MailRepository#FindAll", "count", len(models))
return actors, nil
return models, nil
}
func (repository *MediaActorRepository) GetById(context context.Context, id string) (*models.MediaActor, error) {
var actor = models.MediaActor{ID: id}
var model = models.MediaActor{ID: id}
slog.Info("MediaActorRepository#GetById", "id", id)
result := repository.db.First(&actor)
if result.Error != nil {
slog.Info("SportRepository#GetById", "error", result.Error.Error())
err := repository.db.NewSelect().Model(&model).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return &actor, result.Error
return &model, err
}
func (repository *MediaActorRepository) Add(context context.Context, url string) (*models.MediaActor, error) {
@@ -5,7 +5,9 @@ import (
"fmt"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
@@ -19,18 +21,21 @@ func (repository *MediaActorFileRepository) Import(dtos models.MediaActorFileDTO
slog.Info("importing", "index", index, "video", dto)
model := dto.ConvertDTOToModel()
existing_item := models.MediaActorFile{ID: dto.ID}
result_existing := repository.db.First(&existing_item)
if result_existing.Error != nil {
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
result := repository.db.Create(&model)
if result.Error != nil {
slog.Info("error during import", "error", result.Error.Error())
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
result := repository.db.Save(&model)
if result.Error != nil {
slog.Info("error during updating", "error", result.Error.Error())
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
@@ -42,23 +47,27 @@ func NewMediaActorFileRepository(db *bun.DB) *MediaActorFileRepository {
}
func (repository *MediaActorFileRepository) FindAll(context context.Context) ([]models.MediaActorFile, error) {
var actor_files []models.MediaActorFile
var models []models.MediaActorFile
result := repository.db.Find(&actor_files)
slog.Info("MediaActorFileRepository#FindAll", "count", result.RowsAffected)
err := repository.db.NewSelect().Model(&models).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("MailRepository#FindAll", "count", len(models))
return actor_files, nil
return models, nil
}
func (repository *MediaActorFileRepository) GetById(context context.Context, id string) (*models.MediaActorFile, error) {
var actorfile = models.MediaActorFile{ID: id}
var model = models.MediaActorFile{ID: id}
slog.Info("MediaActorFileRepository#GetById", "id", id)
result := repository.db.First(&actorfile)
if result.Error != nil {
slog.Info("MediaActorFileRepository#GetById", "error", result.Error.Error())
return nil, result.Error
err := repository.db.NewSelect().Model(&model).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return &actorfile, result.Error
return &model, err
}
func (repository *MediaActorFileRepository) Add(context context.Context, media_actor_id, media_file_id string) (*models.MediaActorFile, error) {
+27 -18
View File
@@ -5,7 +5,9 @@ import (
"fmt"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
@@ -19,18 +21,21 @@ func (repository *MediaFileRepository) Import(dtos models.MediaFileDTOList) {
slog.Info("importing", "index", index, "video", dto)
model := dto.ConvertDTOToModel()
existing_item := models.MediaFile{ID: dto.ID}
result_existing := repository.db.First(&existing_item)
if result_existing.Error != nil {
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
result := repository.db.Create(&model)
if result.Error != nil {
slog.Info("error during import", "error", result.Error.Error())
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
result := repository.db.Save(&model)
if result.Error != nil {
slog.Info("error during updating", "error", result.Error.Error())
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
@@ -42,23 +47,27 @@ func NewMediaFileRepository(db *bun.DB) *MediaFileRepository {
}
func (repository *MediaFileRepository) FindAll(context context.Context) ([]models.MediaFile, error) {
var files []models.MediaFile
var models []models.MediaFile
result := repository.db.Find(&files)
slog.Info("MediaFileRepository#FindAll", "count", result.RowsAffected)
err := repository.db.NewSelect().Model(&models).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("MailRepository#FindAll", "count", len(models))
return files, nil
return models, nil
}
func (repository *MediaFileRepository) GetById(context context.Context, id string) (*models.MediaFile, error) {
var file = models.MediaFile{ID: id}
var model = models.MediaFile{ID: id}
slog.Info("MediaFileRepository#GetById", "id", id)
result := repository.db.First(&file)
if result.Error != nil {
slog.Info("MediaFileRepository#GetById", "error", result.Error.Error())
return nil, result.Error
err := repository.db.NewSelect().Model(&model).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return &file, result.Error
return &model, err
}
func (repository *MediaFileRepository) Add(context context.Context, url string) (*models.MediaFile, error) {
+39 -41
View File
@@ -4,7 +4,6 @@ import (
"context"
"database/sql"
"errors"
"fmt"
"kontor-api-echo/internal/helpers"
"kontor-api-echo/internal/models"
"log/slog"
@@ -25,18 +24,21 @@ func (repository *MediaLoFiRepository) Import(dtos models.MediaLoFiDTOList) {
slog.Info("importing", "index", index, "video", dto)
model := dto.ConvertDTOToModel()
existing_item := models.MediaLoFi{ID: dto.ID}
result_existing := repository.db.First(&existing_item)
if result_existing.Error != nil {
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
result := repository.db.Create(&model)
if result.Error != nil {
slog.Info("error during import", "error", result.Error.Error())
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
result := repository.db.Save(&model)
if result.Error != nil {
slog.Info("error during updating", "error", result.Error.Error())
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
@@ -48,41 +50,37 @@ func NewMediaLoFiRepository(db *bun.DB) *MediaLoFiRepository {
}
func (repository *MediaLoFiRepository) FindAll(context context.Context) ([]models.MediaLoFi, error) {
var lofis []models.MediaLoFi
var models []models.MediaLoFi
err := repository.db.NewSelect().Model(&lofis).Scan(context)
err := repository.db.NewSelect().Model(&models).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("MediaLoFiRepository#FindAll", "count", len(lofis))
slog.Info("MailRepository#FindAll", "count", len(models))
return lofis, nil
return models, nil
}
func (repository *MediaLoFiRepository) GetById(context context.Context, id string) (*models.MediaLoFi, error) {
var lofi = models.MediaLoFi{ID: id}
var model = models.MediaLoFi{ID: id}
slog.Info("MediaLoFiRepository#GetById", "id", id)
err := repository.db.NewSelect().Model(&lofi).Where("id = ?", id).Scan(context)
err := repository.db.NewSelect().Model(&model).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("MediaLoFiRepository#GetById", "error", err.Error())
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return &lofi, err
return &model, err
}
func (repository *MediaLoFiRepository) Add(context context.Context, url string) (*models.MediaLoFi, error) {
slog.Info("MediaLoFiRepository#Add", "url", url)
webLink := helpers.StripUrl(url, "v")
var lofi models.MediaLoFi
search_result := repository.db.Where("url = ?", webLink).First(&lofi)
if search_result.RowsAffected > 0 {
slog.Info("search for existing url", "result", search_result, "lofi", lofi)
return &lofi, nil
}
if errors.Is(search_result.Error, sql.ErrNoRows) {
var model models.MediaLoFi
err := repository.db.NewSelect().Model(&model).Where("url = ?", url).Scan(context)
if errors.Is(err, sql.ErrNoRows) {
slog.Info("MediaLoFiRepository#Add URL not found", "url", webLink)
lofi = models.MediaLoFi{
lofi := models.MediaLoFi{
ID: uuid.NewString(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
@@ -94,32 +92,32 @@ func (repository *MediaLoFiRepository) Add(context context.Context, url string)
Title: "",
FileName: "",
}
add_result := repository.db.Create(&lofi)
if add_result.Error != nil {
slog.Info("add new MediaLofi", "error", add_result.Error)
return nil, add_result.Error
_, add_error := repository.db.NewInsert().Model(&model).Exec(context)
if add_error != nil {
slog.Info("add new MediaLofi", "error", add_error)
return nil, add_error
}
slog.Info("add new MediaLofi", "lofi", lofi)
return &lofi, nil
} else {
slog.Info("search for existing url", "lofi", model)
return &model, nil
}
if search_result.Error != nil {
return nil, search_result.Error
}
return nil, fmt.Errorf("MediaLoFiRepository#Add unknown error")
}
func (repository *MediaLoFiRepository) Update(context context.Context, update models.MediaLoFiDTO) (*models.MediaLoFi, error) {
slog.Info("MediaLoFiRepository#Update", "lofi", update)
var lofi = models.MediaLoFi{ID: update.ID}
var model = models.MediaLoFi{ID: update.ID}
model.Update(update)
slog.Info("MediaLoFiRepository#Update", "id", update.ID)
result := repository.db.First(&lofi)
if result.Error != nil {
slog.Info("MediaLoFiRepository#Update", "error", result.Error.Error())
return nil, result.Error
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", update.ID).
Exec(context)
if err != nil {
slog.Info("MediaLoFiRepository#Update", "error", err.Error())
}
lofi.Update(update)
repository.db.Save(&lofi)
return &lofi, nil
return &model, nil
}
func (repository *MediaLoFiRepository) Delete(context context.Context, id string) error {
+48 -44
View File
@@ -4,13 +4,13 @@ import (
"context"
"database/sql"
"errors"
"fmt"
"kontor-api-echo/internal/helpers"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/google/uuid"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
@@ -24,18 +24,21 @@ func (repository *MediaMusicRepository) Import(dtos models.MediaMusicDTOList) {
slog.Info("importing", "index", index, "video", dto)
model := dto.ConvertDTOToModel()
existing_item := models.MediaMusic{ID: dto.ID}
result_existing := repository.db.First(&existing_item)
if result_existing.Error != nil {
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
result := repository.db.Create(&model)
if result.Error != nil {
slog.Info("error during import", "error", result.Error.Error())
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
result := repository.db.Save(&model)
if result.Error != nil {
slog.Info("error during updating", "error", result.Error.Error())
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
@@ -47,36 +50,37 @@ func NewMediaMusicRepository(db *bun.DB) *MediaMusicRepository {
}
func (repository *MediaMusicRepository) FindAll(context context.Context) ([]models.MediaMusic, error) {
var musics []models.MediaMusic
var models []models.MediaMusic
result := repository.db.Find(&musics)
slog.Info("MediaMusicRepository#FindAll", "count", result.RowsAffected)
err := repository.db.NewSelect().Model(&models).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("MailRepository#FindAll", "count", len(models))
return musics, nil
return models, nil
}
func (repository *MediaMusicRepository) GetById(context context.Context, id string) (*models.MediaMusic, error) {
var music = models.MediaMusic{ID: id}
var model = models.MediaMusic{ID: id}
slog.Info("MediaMusicRepository#GetById", "id", id)
result := repository.db.First(&music)
if result.Error != nil {
slog.Info("MediaMusicRepository#GetById", "error", result.Error.Error())
err := repository.db.NewSelect().Model(&model).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return &music, result.Error
return &model, err
}
func (repository *MediaMusicRepository) Add(context context.Context, url string) (*models.MediaMusic, error) {
slog.Info("MediaMusicRepository#Add", "url", url)
webLink := helpers.StripUrl(url, "v")
var music models.MediaMusic
search_result := repository.db.Where("url = ?", webLink).First(&music)
if search_result.RowsAffected > 0 {
slog.Info("search for existing url", "result", search_result, "music", music)
return &music, nil
}
if errors.Is(search_result.Error, sql.ErrNoRows) {
var model models.MediaMusic
err := repository.db.NewSelect().Model(&model).Where("url = ?", url).Scan(context)
if errors.Is(err, sql.ErrNoRows) {
slog.Info("MediaMusicRepository#Add URL not found", "url", webLink)
music = models.MediaMusic{
music := models.MediaMusic{
ID: uuid.NewString(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
@@ -88,32 +92,32 @@ func (repository *MediaMusicRepository) Add(context context.Context, url string)
Title: "",
FileName: "",
}
add_result := repository.db.Create(&music)
if add_result.Error != nil {
slog.Info("add new MediaMusic", "error", add_result.Error)
return nil, add_result.Error
_, add_error := repository.db.NewInsert().Model(&model).Exec(context)
if add_error != nil {
slog.Info("add new MediaMusic", "error", add_error)
return nil, add_error
}
slog.Info("add new MediaMusic", "music", music)
slog.Info("add new MediaMusic", "lofi", music)
return &music, nil
} else {
slog.Info("search for existing url", "lofi", model)
return &model, nil
}
if search_result.Error != nil {
return nil, search_result.Error
}
return nil, fmt.Errorf("MediaMusicRepository#Add unknown error")
}
func (repository *MediaMusicRepository) Update(context context.Context, update models.MediaMusicDTO) (*models.MediaMusic, error) {
slog.Info("MediaMusicRepository#Update", "music", update)
var music = models.MediaMusic{ID: update.ID}
slog.Info("MediaMusicRepository#Update", "id", update.ID)
result := repository.db.First(&music)
if result.Error != nil {
slog.Info("MediaMusicRepository#Update", "error", result.Error.Error())
return nil, result.Error
var model = models.MediaMusic{ID: update.ID}
model.Update(update)
slog.Info("MediaLoFiRepository#Update", "id", update.ID)
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", update.ID).
Exec(context)
if err != nil {
slog.Info("MediaLoFiRepository#Update", "error", err.Error())
}
music.Update(update)
repository.db.Save(&music)
return &music, nil
return &model, nil
}
func (repository *MediaMusicRepository) Delete(context context.Context, id string) error {
+35 -36
View File
@@ -2,10 +2,14 @@ package repository
import (
"context"
"database/sql"
"errors"
"kontor-api-echo/internal/helpers"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/google/uuid"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
@@ -68,42 +72,37 @@ func (repository *MediaVideoRepository) GetById(context context.Context, id stri
return &video, nil
}
// func (repository *MediaVideoRepository) Add(context context.Context, url string) (*models.MediaVideo, error) {
// slog.Info("MediaVideoRepository#Add", "url", url)
// webLink := helpers.StripUrl(url, "v")
// var video models.MediaVideo
// search_result := repository.db.Where("url = ?", webLink).First(&video)
// if search_result.RowsAffected > 0 {
// slog.Info("search for existing url", "result", search_result, "video", video)
// return &video, nil
// }
// if errors.Is(search_result.Error, sql.ErrNoRows) {
// slog.Info("MediaVideoRepository#Add URL not found", "url", webLink)
// video = models.MediaVideo{
// ID: uuid.NewString(),
// CreatedAt: time.Now(),
// UpdatedAt: time.Now(),
// DeletedAt: nil,
// Version: 0,
// WebLink: webLink,
// ShouldDownload: false,
// Review: true,
// Title: "",
// FileName: "",
// }
// add_result := repository.db.Create(&video)
// if add_result.Error != nil {
// slog.Info("add new MediaVideo", "error", add_result.Error)
// return nil, add_result.Error
// }
// slog.Info("add new MediaVideo", "music", video)
// return &video, nil
// }
// if search_result.Error != nil {
// return nil, search_result.Error
// }
// return nil, fmt.Errorf("MediaVideoRepository#Add unknown error")
// }
func (repository *MediaVideoRepository) Add(context context.Context, url string) (*models.MediaVideo, error) {
slog.Info("MediaVideoRepository#Add", "url", url)
webLink := helpers.StripUrl(url, "v")
var model models.MediaVideo
err := repository.db.NewSelect().Model(&model).Where("url = ?", url).Scan(context)
if errors.Is(err, sql.ErrNoRows) {
slog.Info("MediaMusicRepository#Add URL not found", "url", webLink)
video := models.MediaVideo{
ID: uuid.NewString(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
DeletedAt: nil,
Version: 0,
WebLink: webLink,
ShouldDownload: false,
Review: true,
Title: "",
FileName: "",
}
_, add_error := repository.db.NewInsert().Model(&model).Exec(context)
if add_error != nil {
slog.Info("add new MediaMusic", "error", add_error)
return nil, add_error
}
slog.Info("add new MediaMusic", "lofi", video)
return &video, nil
} else {
slog.Info("search for existing url", "lofi", model)
return &model, nil
}
}
func (repository *MediaVideoRepository) Update(context context.Context, update models.MediaVideoDTO) (*models.MediaVideo, error) {
slog.Info("MediaVideoRepository#Update", "video", update)
+2 -1
View File
@@ -31,9 +31,10 @@ func (repository *ProfileRepository) FindAll(context context.Context) ([]models.
return profiles, nil
}
func (repository *ProfileRepository) FindProfileByEmail(context context.Context, email string) (*models.Profile, error) {
func (repository *ProfileRepository) FindProfileByEmail(email string) (*models.Profile, error) {
var profile models.Profile
slog.Info("MediaArticleRepository#GetById", "email", email)
context := context.Background()
err := repository.db.NewSelect().Model(&profile).Where("email = ?", email).Scan(context)
if err != nil {
+70
View File
@@ -0,0 +1,70 @@
package repository
import (
"context"
"kontor-api-echo/internal/models"
"log/slog"
"time"
"github.com/labstack/echo/v5"
"github.com/uptrace/bun"
)
type TokenRepository struct {
db *bun.DB
}
func (repository *TokenRepository) Import(dtos models.TokenDTOList) {
slog.Info("TokenRepository#Import")
for index, dto := range dtos {
slog.Info("importing", "index", index, "video", dto)
model := dto.ConvertDTOToModel()
existing_item := models.Token{ID: dto.ID}
err := repository.db.NewSelect().Model(&existing_item).Where("id = ?", dto.ID).Scan(context.Background())
if err != nil {
slog.Info("check existing id: not found - inserting")
_, err := repository.db.NewInsert().Model(&model).Exec(context.Background())
if err != nil {
slog.Info("error during import", "error", err.Error())
}
} else {
slog.Info("check existing id: found - updating")
_, err := repository.db.NewUpdate().Model(&model).
Set("updated_at = ? ", time.Now).
Where("id = ?", model.ID).
Exec(context.Background())
if err != nil {
slog.Info("error during updating", "error", err.Error())
}
}
}
}
func NewTokenRepository(db *bun.DB) *TokenRepository {
repository := &TokenRepository{db: db}
return repository
}
func (repository *TokenRepository) FindAll(context context.Context) ([]models.Token, error) {
var models []models.Token
err := repository.db.NewSelect().Model(&models).Scan(context)
if err != nil {
slog.Info(err.Error())
return nil, echo.ErrInternalServerError
}
slog.Info("MailRepository#FindAll", "count", len(models))
return models, nil
}
func (repository *TokenRepository) GetById(context context.Context, id string) (*models.Token, error) {
var model = models.Token{ID: id}
slog.Info("TokenRepository#GetById", "id", id)
err := repository.db.NewSelect().Model(&model).Where("id = ?", id).Scan(context)
if err != nil {
slog.Info("AssignmentRepository#GetById", "error", err.Error())
return nil, err
}
return &model, err
}
+130 -10
View File
@@ -1,34 +1,154 @@
package router
import (
"html/template"
"io"
"kontor-api-echo/assets"
"kontor-api-echo/internal/config"
"kontor-api-echo/internal/handlers"
"kontor-api-echo/templates"
"log/slog"
"os"
echojwt "github.com/labstack/echo-jwt/v5"
"github.com/labstack/echo/v5"
"github.com/labstack/echo/v5/middleware"
)
type Template struct {
templates *template.Template
}
func (t *Template) Render(c *echo.Context, w io.Writer, name string, data any) error {
return t.templates.ExecuteTemplate(w, name, data)
}
func New(admin *handlers.AdminHandler, media *handlers.MediaHandler) *echo.Echo {
e := echo.New()
skipper := func(c *echo.Context) bool {
// Skip health check endpoint
return c.Request().URL.Path == "/health"
}
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
e.Use(middleware.Recover())
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
LogStatus: true,
LogURI: true,
LogStatus: true,
HandleError: true,
Skipper: skipper,
Skipper: func(c *echo.Context) bool {
// Skip health check endpoint
return c.Path() == "/health"
},
LogValuesFunc: func(c *echo.Context, v middleware.RequestLoggerValues) error {
if v.Error == nil {
slog.Info("REQUEST", "uri", v.URI, "status", v.Status)
} else {
slog.Info("REQUEST-ERROR", "uri", v.URI, "status", v.Status, "err", v.Error.Error())
if v.Error != nil {
logger.Error("request failed", "uri", v.URI, "status", v.Status, "error", v.Error)
return nil
}
logger.Info("request", "uri", v.URI, "status", v.Status)
return nil
},
}))
e.StaticFS("/public", echo.MustSubFS(assets.StaticFiles, "assets"))
templ := template.Must(template.New("").ParseFS(templates.TemplateFiles, "layouts/*.html", "pages/*.html"))
t := &Template{
templates: templ,
}
e.Renderer = t
e.GET("/health", handlers.Health)
e.GET("/", handlers.Index)
e.GET("/login", admin.AuthHandler.LoginPage)
e.POST("/login", admin.AuthHandler.LoginPost)
e.GET("/logout", admin.AuthHandler.LogoutPage)
mediaGroup := e.Group("/media")
//mediaGroup.Use(middleware.AuthRequired())
mediaGroup.GET("/actors", media.MediaActorHandler.ListPage)
mediaGroup.GET("/actors/:id", media.MediaActorHandler.EditPage)
mediaGroup.POST("/actors/:id", media.MediaActorHandler.UpdatePage)
mediaGroup.GET("/articles", media.MediaArticleHandler.ListPage)
mediaGroup.GET("/files", media.MediaFileHandler.ListPage)
mediaGroup.GET("/files/edit/:id", media.MediaFileHandler.EditPage)
mediaGroup.POST("/files/edit/:id", media.MediaFileHandler.UpdatePage)
mediaGroup.GET("/files/delete/:id", media.MediaFileHandler.DeletePage)
mediaGroup.GET("/videos", media.MediaVideoHandler.ListPage)
mediaGroup.GET("/videos/edit/:id", media.MediaVideoHandler.EditPage)
mediaGroup.POST("/videos/edit/:id", media.MediaVideoHandler.UpdatePage)
mediaGroup.GET("/videos/delete/:id", media.MediaVideoHandler.DeletePage)
mediaGroup.GET("/lofis", media.MediaLoFiHandler.ListPage)
mediaGroup.GET("/lofis/edit", media.MediaLoFiHandler.EditPage)
mediaGroup.GET("/lofis/edit/:id", media.MediaLoFiHandler.EditPage)
mediaGroup.POST("/lofis/edit/:id", media.MediaLoFiHandler.UpdatePage)
mediaGroup.GET("/lofis/review/:id", media.MediaLoFiHandler.ChangeReviewPage)
mediaGroup.GET("/lofis/delete/:id", media.MediaLoFiHandler.DeletePage)
mediaGroup.GET("/musics", media.MediaMusicHandler.ListPage)
mediaGroup.GET("/musics/edit/:id", media.MediaMusicHandler.EditPage)
mediaGroup.POST("/musics/edit/:id", media.MediaMusicHandler.UpdatePage)
mediaGroup.GET("/musics/delete/:id", media.MediaMusicHandler.DeletePage)
api := e.Group("/api")
api.POST("/login", admin.AuthHandler.Login)
v1 := api.Group("/v1")
{
secured := v1.Group("")
secured.Use(echojwt.WithConfig(echojwt.Config{SigningKey: config.ServerConfig.ApiSecret}))
//secured.Use(middleware.JWT(admin.Secret))
// secured.GET("admin/mailaccounts", admin.MailAccountHandler.List)
// secured.GET("user/profiles", admin.ProfileHandler.List)
// secured.GET("user/permissions", admin.PermissionHandler.List)
// secured.GET("user/assignments", admin.AssignmentHandler.List)
// secured.GET("user/mails", admin.MailHandler.List)
// secured.GET("user/tokens", admin.TokenHandler.List)
// secured.GET("tysc/sports", tysc.SportHandler.List)
// secured.GET("tysc/sports/:id", tysc.SportHandler.Get)
// secured.GET("tysc/players", tysc.PlayerHandler.List)
// secured.GET("tysc/teams", tysc.TeamHandler.List)
// secured.GET("tysc/positions", tysc.PositionHandler.List)
// secured.GET("tysc/roosters", tysc.RoosterHandler.List)
// secured.GET("tysc/vendors", tysc.VendorHandler.List)
// secured.GET("tysc/cardsets", tysc.CardSetHandler.List)
// secured.GET("tysc/cards", tysc.CardHandler.List)
// secured.GET("bookshelf/articles", bookshelf.ArticleHandler.List)
// secured.GET("bookshelf/publishers", bookshelf.BookshelfPublisherHandler.List)
// secured.GET("bookshelf/books", bookshelf.BookHandler.List)
// secured.GET("bookshelf/authors", bookshelf.AuthorHandler.List)
// secured.GET("bookshelf/articleauthors", bookshelf.ArticleAuthorHandler.List)
// secured.GET("bookshelf/bookauthors", bookshelf.BookAuthorHandler.List)
// secured.GET("comics/comics", comics.ComicHandler.List)
// secured.GET("comics/publishers", comics.PublisherHandler.List)
// secured.GET("comics/artists", comics.ArtistHandler.List)
// secured.GET("comics/issues", comics.IssueHandler.List)
// secured.GET("comics/tradepaperbacks", comics.TradePaperbackHandler.List)
// secured.GET("comics/worktypes", comics.WorkTypeHandler.List)
// secured.GET("comics/volumes", comics.VolumeHandler.List)
// secured.GET("comics/storyarcs", comics.StoryArcHandler.List)
// secured.GET("comics/comicworks", comics.ComicworkHandler.List)
// secured.GET("comics/issueworks", comics.IssueworkHandler.List)
mediaGroup := secured.Group("media")
mediaGroup.GET("/files", media.MediaFileHandler.List)
mediaGroup.POST("/files", media.MediaFileHandler.Add)
// mediaGroup.GET("/videos", media.MediaVideoHandler.List)
// mediaGroup.GET("/lofis", media.MediaLoFiHandler.List)
// mediaGroup.POST("/lofis", media.MediaLoFiHandler.Add)
// mediaGroup.GET("/lofis/:id", media.MediaLoFiHandler.Get)
// mediaGroup.POST("/lofis/:id", media.MediaLoFiHandler.Update)
// mediaGroup.DELETE("/lofis/:id", media.MediaLoFiHandler.Delete)
// mediaGroup.POST("/musics", media.MediaMusicHandler.Add)
// mediaGroup.GET("/musics/:id", media.MediaMusicHandler.Get)
// mediaGroup.POST("/musics/:id", media.MediaMusicHandler.Update)
// mediaGroup.DELETE("/musics/:id", media.MediaMusicHandler.Delete)
// mediaGroup.GET("/actors", media.MediaActorHandler.List)
// mediaGroup.POST("/actors", media.MediaActorHandler.Add)
// mediaGroup.GET("/actors/:id", media.MediaActorHandler.Get)
// mediaGroup.DELETE("/actors/:id", media.MediaActorHandler.Delete)
// mediaGroup.GET("/actorfiles", media.MediaActorFileHandler.List)
// mediaGroup.POST("/actorfiles", media.MediaActorFileHandler.Add)
// mediaGroup.DELETE("/actorfiles", media.MediaActorFileHandler.Delete)
}
return e
}
+1 -1
View File
@@ -21,7 +21,7 @@ func (service *AssignmentService) List(context context.Context) ([]models.Assign
return service.repository.FindAll(context)
}
func (service *AssignmentService) Get(context context.Context, id string) (models.Assignment, error) {
func (service *AssignmentService) Get(context context.Context, id string) (*models.Assignment, error) {
slog.Info("AssignmentService#Get")
return service.repository.GetById(context, id)
}
+47
View File
@@ -0,0 +1,47 @@
package service
import (
"kontor-api-echo/internal/helpers"
"kontor-api-echo/internal/repository"
"golang.org/x/crypto/bcrypt"
)
type AuthService struct {
repository *repository.ProfileRepository
}
func NewAuthService(repository *repository.ProfileRepository) *AuthService {
service := &AuthService{repository: repository}
return service
}
func VerifyPassword(password, hashedPassword string) error {
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}
func (service *AuthService) LoginCheck(email string, password string) (string, error) {
var err error
profile, err := service.repository.FindProfileByEmail(email)
if err != nil {
return "", err
}
err = VerifyPassword(password, profile.Password)
if err != nil && err == bcrypt.ErrMismatchedHashAndPassword {
return "", err
}
token, err := helpers.GenerateToken(profile.ID)
if err != nil {
return "", err
}
return token, nil
}
+27
View File
@@ -0,0 +1,27 @@
package service
import (
"context"
"kontor-api-echo/internal/models"
"kontor-api-echo/internal/repository"
"log/slog"
)
type MailService struct {
repository *repository.MailRepository
}
func NewMailService(repo *repository.MailRepository) *MailService {
service := &MailService{repository: repo}
return service
}
func (service *MailService) List(context context.Context) ([]models.Mail, error) {
slog.Info("MailService#List")
return service.repository.FindAll(context)
}
func (service *MailService) Get(context context.Context, id string) (*models.Mail, error) {
slog.Info("MailService#Get")
return service.repository.GetById(context, id)
}
@@ -0,0 +1,27 @@
package service
import (
"context"
"kontor-api-echo/internal/models"
"kontor-api-echo/internal/repository"
"log/slog"
)
type MailAccountService struct {
repository *repository.MailAccountRepository
}
func NewMailAccountService(repo *repository.MailAccountRepository) *MailAccountService {
service := &MailAccountService{repository: repo}
return service
}
func (service *MailAccountService) List(context context.Context) ([]models.MailAccount, error) {
slog.Info("MailAccountService#List")
return service.repository.FindAll(context)
}
func (service *MailAccountService) Get(context context.Context, id string) (*models.MailAccount, error) {
slog.Info("MailAccountService#Get")
return service.repository.GetById(context, id)
}
+27
View File
@@ -0,0 +1,27 @@
package service
import (
"context"
"kontor-api-echo/internal/models"
"kontor-api-echo/internal/repository"
"log/slog"
)
type TokenService struct {
repository *repository.TokenRepository
}
func NewTokenService(repo *repository.TokenRepository) *TokenService {
service := &TokenService{repository: repo}
return service
}
func (service *TokenService) List(context context.Context) ([]models.Token, error) {
slog.Info("TokenService#List")
return service.repository.FindAll(context)
}
func (service *TokenService) Get(context context.Context, id string) (*models.Token, error) {
slog.Info("TokenService#Get")
return service.repository.GetById(context, id)
}
-17
View File
@@ -1,17 +0,0 @@
package handler
import (
"net/http"
"github.com/labstack/echo/v4"
)
type Status struct {
Status string `json:"status"`
}
func GetHealth(c echo.Context) error {
status := new(Status)
status.Status = "ok"
return c.JSON(http.StatusOK, status)
}
-67
View File
@@ -1,67 +0,0 @@
package handler
import (
"context"
"fmt"
"kontor-api-echo/pkg/schema"
"log"
"net/http"
"time"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/uptrace/bun"
)
func SetupMediaRoutes(api *echo.Group) {
media := api.Group("/media")
media.GET("/files", GetAllFiles)
media.POST("/files", AddFile)
}
func GetAllFiles(c echo.Context) error {
var files []schema.MediaFile
var err error
var db *bun.DB
ctx := context.Background()
db, err = schema.GetDatabase()
if err != nil {
log.Fatal(err)
}
err = db.NewSelect().Model(&files).Relation("MediaActorFiles").Scan(ctx)
if err != nil {
log.Fatal(err)
return echo.ErrInternalServerError
}
return c.JSON(http.StatusOK, files)
}
type Link struct {
URL string `json:"url"`
}
func AddFile(c echo.Context) error {
var err error
var db *bun.DB
ctx := context.Background()
link := new(Link)
if err = c.Bind(link); err != nil {
return err
}
log.Printf("URL %s has been sent", link)
db, err = schema.GetDatabase()
if err != nil {
log.Fatal(err)
}
id := uuid.NewString()
timestamp := time.Now()
mediafile := &schema.MediaFile{ID: id, CreatedAt: timestamp, UpdatedAt: timestamp, WebLink: fmt.Sprintf("%s", link), Version: 1, ShouldDownload: true, Review: true}
_, err = db.NewInsert().Model(mediafile).Exec(ctx)
if err != nil {
return err
}
return c.String(http.StatusCreated, "Link created")
}
@@ -0,0 +1,9 @@
{{ define "footer" }}
<footer class="mt-auto py-3 bg-light fixed-bottom mt-auto">
<div class="container text-center">
<span class="text-muted">{{ .version }}</span>
</div>
</footer>
<!-- Bootstrap JS einbinden -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{{ end }}
+13
View File
@@ -0,0 +1,13 @@
{{ define "head" }}
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<link rel="stylesheet" href="/public/style/main.css" />
<title>{{ .title }}</title>
</head>
{{ end }}
+24
View File
@@ -0,0 +1,24 @@
{{ define "main" }}
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="/public/style/main.css" />
<title>{{ .title }}</title>
</head>
<body>
<div class="header"></div>
{{ template "navbar" . }}
{{ template "content" . }}
<div class="footer">{{ .version }}</div>
</body>
</html>
{{ end }}
+88
View File
@@ -0,0 +1,88 @@
{{ define "navbar" }}
<!-- 1. Kopfzeile & Navigation -->
<header>
<nav class="navbar navbar-expand-lg navbar-dark fixed-top" style="background-color:darkgrey;">
<div class="container-fluid">
<a class="navbar-brand" href="#">Kontor</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarComicsMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Comics</a>
<div class="dropdown-menu" aria-labelledby="navbarComicsMenuLink">
<a class="dropdown-item" href="/comics/comics">Comics</a>
<a class="dropdown-item" href="/comics/publishers">Publisher</a>
<a class="dropdown-item" href="/comics/artists">Artist</a>
<a class="dropdown-item" href="/comics/issues">Issue</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarWebMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Media</a>
<div class="dropdown-menu" aria-labelledby="navbarWebMenuLink">
<a class="dropdown-item" href="{{.media_file_link}}">Media Files</a>
<a class="dropdown-item" href="{{.media_actor_link}}">Media Actors</a>
<a class="dropdown-item" href="{{.media_lofi_link}}">Media Lofi</a>
<a class="dropdown-item" href="{{.media_music_link}}">Media Music</a>
<a class="dropdown-item" href="{{.media_video_link}}">Media Videos</a>
<a class="dropdown-item" href="{{.media_article_link}}">Artikel</a>
</div>
</li>
{{ if .user }}
<li class="nav-item">
<a class="nav-link" href="/logout">Logout</a>
</li>
{{ else }}
<li class="nav-item">
<a class="nav-link" href="/login">Login</a>
</li>
{{ end }}
</ul>
</div>
</div>
</nav>
</header>
<!-- <nav class="navbar fixed-top navbar-expand-lg navbar-dark" style="background-color: darkgrey;">
<a class="navbar-brand" href="/">Kontor</a>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-item nav-link" href="/">Kontor</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarComicsMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Comics</a>
<div class="dropdown-menu" aria-labelledby="navbarComicsMenuLink">
<a class="dropdown-item" href="/comics/comics">Comics</a>
<a class="dropdown-item" href="/comics/publishers">Publisher</a>
<a class="dropdown-item" href="/comics/artists">Artist</a>
<a class="dropdown-item" href="/comics/issues">Issue</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarWebMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Media</a>
<div class="dropdown-menu" aria-labelledby="navbarWebMenuLink">
<a class="dropdown-item" href="/web/media-files">Media Files</a>
<a class="dropdown-item" href="/web/actors">Media Actors</a>
<a class="dropdown-item" href="/web/media-lofis">Lofi</a>
<a class="dropdown-item" href="/web/articles">Artikel</a>
</div>
</li>
{{ if .user }}
<li class="nav-item">
<a class="nav-link" href="/logout">Logout</a>
</li>
{{ else }}
<li class="nav-item">
<a class="nav-link" href="/login">Login</a>
</li>
{{ end }}
<form class="form-inline">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</ul>
</nav> -->
{{ end }}
+59
View File
@@ -0,0 +1,59 @@
{{ define "comics" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<div class="row">
<form class="form-inline" action="/comic/comics">
<input type="text" placeholder="Search" name="query">
<label>
<input type="checkbox" name="completed" {{ if .Completed }}checked{{ end }}>Completed
</label>
<label>
<input type="checkbox" name="order" {{ if .CurrentOrder }}checked{{ end }}>Order
</label>
<button type="submit">Search</button>
<div class="pill-nav"><a href="/comic/comic/add">Add Comic</a></div>
</form>
</div>
<div class="row">
<div class="maincolumn">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col">Publisher</th>
<th scope="col">Completed</th>
<th colspan="2">Actions</th>
</tr>
</thead>
<tbody>
{{ range .comics }}
<tr>
<th scope="row"><a href="/comic/comics/{{.ID}}">{{.Title}}</a></th>
{{ if .Publisher }}
<td><a href="/comic/publishers/{{.Publisher.ID}}">{{.Publisher.Name}}</a></td>
{{ else }}
<td>Publisher not set</td>
{{ end }}
<td>
{{ if .Completed }}
<img src="/public/icons/tick.png" alt="true" width="24" height="24">
{{ else }}
<img src="/public/icons/cross.png" alt="true" width="24" height="24">
{{ end }}
</td>
<td><a href="/comic/comics/edit/{{.ID}}" class="btn btn-outline-primary btn-sm active" role="button" aria-pressed="true">Edit</a></td>
<td><a href="/comic/comics/delete/{{.ID}}" class="btn btn-outline-danger btn-sm active" role="button" aria-pressed="true">Delete</a></td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>
{{ template "footer" . }}
</body>
</html>
{{ end }}
+27
View File
@@ -0,0 +1,27 @@
{{ define "home" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body class="d-flex flex-column h-100">
{{ template "navbar" . }}
<main class="flex-shrink-0">
<div class="container my-4">
<div class="row">
<div class="col-md-8">
<h1>Hauptinhalt</h1>
<p>{{ .message }}</p>
</div>
<div class="col-md-4">
<aside class="bg-light p-3">
<h3>Seitenbereich</h3>
<p>Optionale Sidebar oder Zusatzinformationen.</p>
</aside>
</div>
</div>
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
+69
View File
@@ -0,0 +1,69 @@
{{ define "lofi" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<div class="col-md-8">
<h1>Media Lofi</h1>
<p>Hier steht der wesentliche Inhalt der Seite.</p>
</div>
<div class="col-md-4">
<aside class="bg-light p-3">
<h3>Seitenbereich</h3>
<p>Optionale Sidebar oder Zusatzinformationen.</p>
</aside>
</div>
</div>
<div class="row">
<form class="form-inline" action="/web/media-lofis">
<input type="text" placeholder="Search" name="query">
<label>
<input type="checkbox" name="review" {{if .isReview}}checked{{end}}>Review
</label>
<label>
<input type="checkbox" name="should_download" {{if .isDownload}}checked{{end}}>Download
</label>
<button type="submit">Search</button>
<div class="pill-nav"><a href="/web/media-lofis/add">Add Lofi</a></div>
</form>
</div>
<div class="row">
<table class="table table-hover">
<thead><tr>
<th scope="col">Title</th>
<th scope="col">Weblink</th>
<th scope="col">Review</th>
<th colspan="2">Actions</th>
</tr></thead>
<tbody>
{{ range .lofis }}
<tr>
<th scope="row"><a href="/web/media-lofis/{{.ID}}/edit">{{.Title}}</a></th>
{{ if .WebLink }}
<td><a href="{{.WebLink}}">{{.WebLink}}</a></td>
{{ else }}
<td>WebLink not set</td>
{{ end }}
<td>
{{ if .Review }}
<a href="/web/media-lofis/{{.ID}}/change-review"><img src="/public/icons/tick.png" alt="true" width="24" height="24"></a>
{{ else }}
<a href="/web/media-lofis/{{.ID}}/change-review"><img src="/public/icons/cross.png" alt="true" width="24" height="24"></a>
{{ end }}
</td>
<td><a href="/web/media-lofis/{{.ID}}/edit" class="btn btn-outline-primary btn-sm active" role="button" aria-pressed="true">Edit</a></td>
<td><a href="/web/media-lofis/{{.ID}}/delete" class="btn btn-outline-danger btn-sm active" role="button" aria-pressed="true">Delete</a></td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
@@ -0,0 +1,54 @@
{{ define "lofi_detail" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<div class="col-md-8">
<h1>Media Lofi</h1>
<p>Hier steht der wesentliche Inhalt der Seite.</p>
</div>
<div class="col-md-4">
<aside class="bg-light p-3">
<h3>Seitenbereich</h3>
<p>Optionale Sidebar oder Zusatzinformationen.</p>
</aside>
</div>
</div>
<div class="row">
{{ if .Errors }}
<div class="notification is-danger">{{ .Errors }}</div>
{{ end }}
</div>
<div class="row">
<div class="col-md-8">
<form action="/web/media-lofis/{{.lofi.ID}}/edit" method="POST">
<div class="form-group row">
<label for="inputWeblink" class="col-sm-2 -col-form-label">URL</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputWeblink" name="weblink" value="{{.lofi.WebLink}}"/>
</div>
</div>
<div class="form-group row">
<label for="inputTitle" class="col-sm-2 -col-form-label">Title</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputTitle" name="title" value="{{.lofi.Title}}"/>
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<button class="btn btn-primary" type="submit">Update</button>
<button class="btn" type="button" onclick="/web/media-lofis">Cancel</button>
</div>
</div>
</form>
</div>
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
+36
View File
@@ -0,0 +1,36 @@
{{ define "login" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<div class="content">
<form action="{{ .login }}" method="post">
<div class="container" style="border: 1px solid lightgray; border-radius: 3px; padding: 15px;">
{{ if .content }}
<div class="notification is-danger">{{ .message }}</div>
{{ end}}
<div class="field">
<label class="label">Username</label>
<div class="control">
<input class="input" type="text" placeholder="Enter Username" name="username" required/>
</div>
</div>
<div class="field">
<label class="label">Passwort</label>
<div class="control">
<input class="input" type="password" placeholder="Enter Password" name="password" required/>
</div>
</div>
<div class="field">
<button class="button is-link">Submit</button>
</div>
</div>
</form>
</div>
{{ template "footer" . }}
</body>
</html>
{{ end }}
@@ -0,0 +1,54 @@
{{ define "actor_detail" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<div class="col-md-8">
<h1>Media Actor</h1>
<p>Hier steht der wesentliche Inhalt der Seite.</p>
</div>
<div class="col-md-4">
<aside class="bg-light p-3">
<h3>Seitenbereich</h3>
<p>Optionale Sidebar oder Zusatzinformationen.</p>
</aside>
</div>
</div>
<div class="row">
{{ if .Errors }}
<div class="notification is-danger">{{ .Errors }}</div>
{{ end }}
</div>
<div class="row">
<div class="col-md-8">
<form action="/web/actors/{{.actor.ID}}/edit" method="POST">
<div class="form-group row">
<label for="inputName" class="col-sm-2 -col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputName" name="name" value="{{.actor.Name}}"/>
</div>
</div>
<div class="form-group row">
<label for="inputWeblink" class="col-sm-2 -col-form-label">Link</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputWeblink" name="weblink" value="{{.actor.WebLink}}"/>
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<button class="btn btn-primary" type="submit">Update</button>
<button class="btn" type="button" onclick="/web/actors">Cancel</button>
</div>
</div>
</form>
</div>
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
@@ -0,0 +1,41 @@
{{ define "media_actors" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<div class="col-md-8">
<h1>Media Actors</h1>
<p>Hier steht der wesentliche Inhalt der Seite.</p>
</div>
<div class="col-md-4">
<aside class="bg-light p-3">
<h3>Seitenbereich</h3>
<p>Optionale Sidebar oder Zusatzinformationen.</p>
</aside>
</div>
</div>
<div class="row">
<form class="form-inline" action="{{ .MediaLinksUrl }}">
<input type="text" placeholder="Search" name="query">
<button type="submit">Search</button>
<div class="pill-nav"><a href="{{ .MediaLinksUrl }}/edit">Add Lofi</a></div>
</form>
</div>
<div class="row">
{{ range .actors }}
<div class="card shadow p-3 mb-2 bg-body rounded" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">{{.Name}}</h5>
<a href="{{ $.media_actor_link }}/{{.ID}}" class="btn btn-primary">Read more</a>
</div>
</div>
{{ end }}
</div>
</main>
</body>
</html>
{{ end }}
@@ -0,0 +1,54 @@
{{ define "media_detail" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<div class="col-md-8">
<h1>{{.message}}</h1>
<p>Hier steht der wesentliche Inhalt der Seite.</p>
</div>
<div class="col-md-4">
<aside class="bg-light p-3">
<h3>Seitenbereich</h3>
<p>Optionale Sidebar oder Zusatzinformationen.</p>
</aside>
</div>
</div>
<div class="row">
{{ if .Errors }}
<div class="notification is-danger">{{ .Errors }}</div>
{{ end }}
</div>
<div class="row">
<div class="col-md-8">
<form action="{{ .MediaLinksUrl }}/edit/{{.media.ID}}" method="POST">
<div class="form-group row">
<label for="inputWeblink" class="col-sm-2 -col-form-label">URL</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputWeblink" name="weblink" value="{{.media.WebLink}}"/>
</div>
</div>
<div class="form-group row">
<label for="inputTitle" class="col-sm-2 -col-form-label">Title</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputTitle" name="title" value="{{.media.Title}}"/>
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<button class="btn btn-primary" type="submit">Update</button>
<button class="btn" type="button" onclick="window.history.back()">Cancel</button>
</div>
</div>
</form>
</div>
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
@@ -0,0 +1,71 @@
{{ define "media_links" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<form class="form-inline" action="{{ .MediaLinksUrl }}">
<input type="text" placeholder="Search" name="query">
<label>
<input type="checkbox" name="review" {{ if .Review }}checked{{ end }}>Review
</label>
<label>
<input type="checkbox" name="download" {{ if .ShouldDownload }}checked{{ end }}>Download
</label>
<button type="submit">Search</button>
<div class="pill-nav"><a href="{{ .MediaLinksUrl }}/edit">Add {{.title}}</a></div>
</form>
</div>
<div class="row">
<div class="maincolumn">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col">Weblink</th>
<th scope="col">Review</th>
<th scope="col">Download</th>
<th colspan="2">Actions</th>
</tr>
</thead>
<tbody>
{{ range .media_links }}
<tr>
<th scope="row"><a href="{{ $.MediaLinksUrl }}/edit/{{.ID}}">{{.Title}}</a></th>
{{ if .WebLink }}
<td><a href="{{.WebLink}}">{{.WebLink}}</a></td>
{{ else }}
<td>no link</td>
{{ end }}
<td>
{{ if .Review }}
<img src="/public/icons/tick.png" alt="true" width="24" height="24">
{{ else }}
<img src="/public/icons/cross.png" alt="true" width="24" height="24">
{{ end }}
</td>
<td>
{{ if .ShouldDownload }}
<img src="/public/icons/tick.png" alt="true" width="24" height="24">
{{ else }}
<img src="/public/icons/cross.png" alt="true" width="24" height="24">
{{ end }}
</td>
<td><a href="{{ $.MediaLinksUrl }}/edit/{{.ID}}" class="btn btn-outline-primary btn-sm active" role="button"
aria-pressed="true">Edit</a></td>
<td><a href="{ $.MediaLinksUrl }/delete/{{.ID}}" class="btn btn-outline-danger btn-sm active"
role="button" aria-pressed="true">Delete</a></td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
@@ -0,0 +1,71 @@
{{ define "mediafiles" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<form class="form-inline" action="/web/files">
<input type="text" placeholder="Search" name="query">
<label>
<input type="checkbox" name="review" {{ if .Review }}checked{{ end }}>Review
</label>
<label>
<input type="checkbox" name="download" {{ if .ShouldDownload }}checked{{ end }}>Download
</label>
<button type="submit">Search</button>
<div class="pill-nav"><a href="/web/files/add">Add MediaFile</a></div>
</form>
</div>
<div class="row">
<div class="maincolumn">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col">Weblink</th>
<th scope="col">Review</th>
<th scope="col">Download</th>
<th colspan="2">Actions</th>
</tr>
</thead>
<tbody>
{{ range .mediafiles }}
<tr>
<th scope="row"><a href="/web/files/{{.ID}}">{{.Title}}</a></th>
{{ if .WebLink }}
<td><a href="{{.WebLink}}">{{.WebLink}}</a></td>
{{ else }}
<td>no link</td>
{{ end }}
<td>
{{ if .Review }}
<img src="/public/icons/tick.png" alt="true" width="24" height="24">
{{ else }}
<img src="/public/icons/cross.png" alt="true" width="24" height="24">
{{ end }}
</td>
<td>
{{ if .ShouldDownload }}
<img src="/public/icons/tick.png" alt="true" width="24" height="24">
{{ else }}
<img src="/public/icons/cross.png" alt="true" width="24" height="24">
{{ end }}
</td>
<td><a href="/web/files/edit/{{.ID}}" class="btn btn-outline-primary btn-sm active" role="button"
aria-pressed="true">Edit</a></td>
<td><a href="/web/files/delete/{{.ID}}" class="btn btn-outline-danger btn-sm active"
role="button" aria-pressed="true">Delete</a></td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
@@ -0,0 +1,71 @@
{{ define "mediavideos" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<form class="form-inline" action="/web/media-videos">
<input type="text" placeholder="Search" name="query">
<label>
<input type="checkbox" name="review" {{ if .Review }}checked{{ end }}>Review
</label>
<label>
<input type="checkbox" name="download" {{ if .ShouldDownload }}checked{{ end }}>Download
</label>
<button type="submit">Search</button>
<div class="pill-nav"><a href="/web/media-videos/add">Add MediaFile</a></div>
</form>
</div>
<div class="row">
<div class="maincolumn">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col">Weblink</th>
<th scope="col">Review</th>
<th scope="col">Download</th>
<th colspan="2">Actions</th>
</tr>
</thead>
<tbody>
{{ range .mediavideos }}
<tr>
<th scope="row"><a href="/web/media-videos/{{.ID}}">{{.Title}}</a></th>
{{ if .WebLink }}
<td><a href="{{.WebLink}}">{{.WebLink}}</a></td>
{{ else }}
<td>no link</td>
{{ end }}
<td>
{{ if .Review }}
<img src="/public/icons/tick.png" alt="true" width="24" height="24">
{{ else }}
<img src="/public/icons/cross.png" alt="true" width="24" height="24">
{{ end }}
</td>
<td>
{{ if .ShouldDownload }}
<img src="/public/icons/tick.png" alt="true" width="24" height="24">
{{ else }}
<img src="/public/icons/cross.png" alt="true" width="24" height="24">
{{ end }}
</td>
<td><a href="/web/media-videos/{{.ID}}" class="btn btn-outline-primary btn-sm active" role="button"
aria-pressed="true">Edit</a></td>
<td><a href="/web/media-videos/{{.ID}}/delete" class="btn btn-outline-danger btn-sm active"
role="button" aria-pressed="true">Delete</a></td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
@@ -0,0 +1,42 @@
{{ define "publishers" }}
<!doctype html>
<html lang="de">
{{ template "head" . }}
<body>
{{ template "navbar" . }}
<main>
<div class="row">
<div class="col-md-8">
<h1>Comic Publishers</h1>
<p>Hier steht der wesentliche Inhalt der Seite.</p>
</div>
<div class="col-md-4">
<aside class="bg-light p-3">
<h3>Seitenbereich</h3>
<p>Optionale Sidebar oder Zusatzinformationen.</p>
</aside>
</div>
</div>
<div class="row">
<form class="form-inline" action="/comic/publishers">
<input type="text" placeholder="Search" name="query">
<button type="submit">Search</button>
<div class="pill-nav"><a href="/comic/publishers/add">Add Lofi</a></div>
</form>
</div>
<div class="row">
{{ range .publishers }}
<div class="card shadow p-3 mb-2 bg-body rounded" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">{{.Name}}</h5>
<a href="/comic/publishers/{{.ID}}" class="btn btn-primary">Read more</a>
</div>
</div>
{{ end }}
</div>
</main>
{{ template "footer" . }}
</body>
</html>
{{ end }}
+8
View File
@@ -0,0 +1,8 @@
package templates
import (
"embed"
)
//go:embed layouts/* pages/*
var TemplateFiles embed.FS