This commit is contained in:
@@ -21,5 +21,6 @@ RUN apk add --no-cache ca-certificates tzdata curl
|
|||||||
# Copy the binary and set ownership
|
# Copy the binary and set ownership
|
||||||
COPY --from=builder /app/kontor /app/kontor
|
COPY --from=builder /app/kontor /app/kontor
|
||||||
COPY --from=builder /app/views /app/views
|
COPY --from=builder /app/views /app/views
|
||||||
|
COPY --from=builder /app/static /app/static
|
||||||
# Set the entrypoint command
|
# Set the entrypoint command
|
||||||
ENTRYPOINT ["/app/kontor"]
|
ENTRYPOINT ["/app/kontor"]
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func main() {
|
|||||||
|
|
||||||
app.Get("/health", handler.GetHealth)
|
app.Get("/health", handler.GetHealth)
|
||||||
app.Post("/login", handler.Login)
|
app.Post("/login", handler.Login)
|
||||||
app.Get("/", web.Mainpage)
|
web.SetupRoutes(app)
|
||||||
|
|
||||||
//api := app.Group("/api/v1")
|
//api := app.Group("/api/v1")
|
||||||
api := app.Group("/api/v1", logger.New(), jwtware.New(jwtware.Config{
|
api := app.Group("/api/v1", logger.New(), jwtware.New(jwtware.Config{
|
||||||
@@ -54,7 +54,7 @@ func main() {
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
// Listen on port 8900
|
// Listen on port 8900
|
||||||
app.Listen(":8510")
|
app.Listen(":8500")
|
||||||
|
|
||||||
log.Println("Kontor finished")
|
log.Println("Kontor finished")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupRoutes(app *fiber.App) {
|
||||||
|
app.Get("/", Mainpage)
|
||||||
|
app.Get("/comic/comics", Mainpage)
|
||||||
|
app.Get("/media/files", Mainpage)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package assets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed style/*
|
||||||
|
var StaticFiles embed.FS
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial;
|
||||||
|
padding: 10px;
|
||||||
|
background: lightgrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial;
|
||||||
|
padding: 10px;
|
||||||
|
background: lightgrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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,11 +2,19 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
// "kontor-api-gin/pkg/handler"
|
// "kontor-api-gin/pkg/handler"
|
||||||
|
"html/template"
|
||||||
|
"kontor-api-gin/assets"
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"kontor-api-gin/pkg/middleware"
|
||||||
"kontor-api-gin/pkg/routes"
|
"kontor-api-gin/pkg/routes"
|
||||||
"kontor-api-gin/pkg/schema"
|
"kontor-api-gin/pkg/schema"
|
||||||
|
"kontor-api-gin/templates"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/sessions/cookie"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,18 +35,40 @@ func main() {
|
|||||||
|
|
||||||
// Create Gin router with default middleware
|
// Create Gin router with default middleware
|
||||||
// Default includes Logger and Recovery middleware
|
// Default includes Logger and Recovery middleware
|
||||||
router := gin.Default()
|
//router := gin.Default()
|
||||||
|
// Create a new router without default mmiddleware
|
||||||
|
router := gin.New()
|
||||||
|
templ := template.Must(template.New("").ParseFS(templates.TemplateFiles, "html/*.tmpl"))
|
||||||
|
router.SetHTMLTemplate(templ)
|
||||||
|
|
||||||
|
router.StaticFS("/public", http.FS(assets.StaticFiles))
|
||||||
|
|
||||||
// Add CORS middleware for frontend access
|
// Add CORS middleware for frontend access
|
||||||
router.Use(corsMiddleware())
|
// router.Use(middleware.CorsMiddleware)
|
||||||
|
|
||||||
// Setup application routes
|
router.Use(gin.LoggerWithConfig(gin.LoggerConfig{
|
||||||
|
SkipPaths: []string{"/health", "/metrics"},
|
||||||
|
}))
|
||||||
|
// Add recovery middleware
|
||||||
|
router.Use(gin.Recovery())
|
||||||
|
|
||||||
|
router.Use(sessions.Sessions("session", cookie.NewStore(globals.Secret)))
|
||||||
|
// Setup api routes
|
||||||
api := routes.SetupApiRoutes(router)
|
api := routes.SetupApiRoutes(router)
|
||||||
routes.SetupMediaRoutes(api)
|
routes.SetupMediaRoutes(api)
|
||||||
|
|
||||||
|
// Setup public Webapp routes
|
||||||
|
public := router.Group("/")
|
||||||
|
routes.PublicRoutes(public)
|
||||||
|
|
||||||
|
// Setup protected Webapp routes
|
||||||
|
protected := router.Group("/")
|
||||||
|
protected.Use(middleware.AuthRequired)
|
||||||
|
routes.ProtectedRoutes(protected)
|
||||||
|
|
||||||
// Health check endpoint
|
// Health check endpoint
|
||||||
router.GET("/health", func(c *gin.Context) {
|
router.GET("/health", func(c *gin.Context) {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"status": "healthy",
|
"status": "healthy",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -56,19 +86,3 @@ func main() {
|
|||||||
|
|
||||||
log.Println("Kontor finished")
|
log.Println("Kontor finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
// corsMiddleware adds CORS headers for cross-origin requests
|
|
||||||
func corsMiddleware() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS")
|
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
|
||||||
|
|
||||||
if c.Request.Method == "OPTIONS" {
|
|
||||||
c.AbortWithStatus(204)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ require (
|
|||||||
github.com/bytedance/sonic/loader v0.5.2 // indirect
|
github.com/bytedance/sonic/loader v0.5.2 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.7 // indirect
|
github.com/cloudwego/base64x v0.1.7 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
|
||||||
|
github.com/gin-contrib/sessions v1.1.2 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.2 // indirect
|
github.com/gin-contrib/sse v1.1.2 // indirect
|
||||||
github.com/gin-gonic/gin v1.12.0 // indirect
|
github.com/gin-gonic/gin v1.12.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
@@ -16,6 +17,9 @@ require (
|
|||||||
github.com/goccy/go-json v0.10.6 // indirect
|
github.com/goccy/go-json v0.10.6 // indirect
|
||||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
|
github.com/gorilla/sessions v1.4.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/pgx/v5 v5.11.0 // indirect
|
github.com/jackc/pgx/v5 v5.11.0 // indirect
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
|
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
|
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
|
||||||
|
github.com/gin-contrib/sessions v1.1.2 h1:utWRqOHT/kbqxxqF/mtutV9wfGLi1+uu5YuvWUhj/Ow=
|
||||||
|
github.com/gin-contrib/sessions v1.1.2/go.mod h1:qRxYwA/TYJw0wv28oFzs4gY/8G8z69id862SNgX9Gmw=
|
||||||
github.com/gin-contrib/sse v1.1.2 h1:MU2fgl1RrdYTMcgJLtz2kJF+vPg3xrqaaKfUUU18tCo=
|
github.com/gin-contrib/sse v1.1.2 h1:MU2fgl1RrdYTMcgJLtz2kJF+vPg3xrqaaKfUUU18tCo=
|
||||||
github.com/gin-contrib/sse v1.1.2/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
|
github.com/gin-contrib/sse v1.1.2/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
|
||||||
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||||
@@ -27,6 +29,12 @@ github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7Lk
|
|||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
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/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||||
|
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||||
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
|
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||||
|
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"kontor-api-gin/pkg/helpers"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoginGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
if user != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "login.tmpl", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"content": "Please logout first",
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "login.tmpl", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"content": "",
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoginPostHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
if user != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "login.tmpl", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"content": "Please logout first",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.PostForm("username")
|
||||||
|
password := c.PostForm("password")
|
||||||
|
|
||||||
|
if helpers.EmptyUserPass(username, password) {
|
||||||
|
c.HTML(http.StatusBadRequest, "login.tmpl", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"content": "Parameters can't be empty",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if !helpers.CheckUserPass(username, password) {
|
||||||
|
c.HTML(http.StatusUnauthorized, "login.tmpl", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"content": "Incorrect username or password",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Set(globals.UserKey, username)
|
||||||
|
if err := session.Save(); err != nil {
|
||||||
|
c.HTML(http.StatusInternalServerError, "login.tmpl", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"content": "Failed to save session",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(http.StatusMovedPermanently, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogoutGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
log.Printf("logging out user %v", user)
|
||||||
|
if user == nil {
|
||||||
|
log.Println("Invalid session token")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
session.Delete(globals.UserKey)
|
||||||
|
if err := session.Save(); err != nil {
|
||||||
|
log.Printf("Failed to save session: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(http.StatusMovedPermanently, "/")
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IndexGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"content": "This is an index page...",
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"kontor-api-gin/pkg/schema"
|
"kontor-api-gin/pkg/schema"
|
||||||
@@ -20,3 +20,15 @@ func ListMediaFiles(c *gin.Context) {
|
|||||||
"files": files,
|
"files": files,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ListMediaLoFis(c *gin.Context) {
|
||||||
|
var lofis []schema.MediaLoFi
|
||||||
|
|
||||||
|
result := schema.DB.Find(&lofis)
|
||||||
|
log.Printf("found %d MediaLoFis", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, lofis)
|
||||||
|
// c.JSON(http.StatusOK, gin.H{
|
||||||
|
// "message": "All MediaLoFis",
|
||||||
|
// "lofis": lofis,
|
||||||
|
// })
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package globals
|
||||||
|
|
||||||
|
var Secret = []byte("secret")
|
||||||
|
|
||||||
|
const UserKey = "user"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func CheckUserPass(username, password string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func EmptyUserPass(username, password string) bool {
|
||||||
|
return strings.Trim(username, " ") == "" || strings.Trim(password, " ") == ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AuthRequired(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get("user")
|
||||||
|
log.Printf("current user %v", user)
|
||||||
|
if user == nil {
|
||||||
|
log.Println("User not logged in")
|
||||||
|
c.Redirect(http.StatusMovedPermanently, "/auth/login")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// corsMiddleware adds CORS headers for cross-origin requests
|
||||||
|
func CorsMiddleware(c *gin.Context) {
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||||
|
|
||||||
|
if c.Request.Method == "OPTIONS" {
|
||||||
|
c.AbortWithStatus(http.StatusNoContent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"kontor-api-gin/pkg/handlers"
|
"kontor-api-gin/pkg/controllers"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -11,6 +11,11 @@ func SetupMediaRoutes(router *gin.RouterGroup) {
|
|||||||
|
|
||||||
files := media.Group("/files")
|
files := media.Group("/files")
|
||||||
{
|
{
|
||||||
files.GET("", handlers.ListMediaFiles)
|
files.GET("", controllers.ListMediaFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
lofi := media.Group("/lofi")
|
||||||
|
{
|
||||||
|
lofi.GET("", controllers.ListMediaLoFis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/controllers"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PublicRoutes(g *gin.RouterGroup) {
|
||||||
|
g.GET("/auth/login", controllers.LoginGetHandler)
|
||||||
|
g.POST("/auth/login", controllers.LoginPostHandler)
|
||||||
|
g.GET("/", controllers.IndexGetHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtectedRoutes(g *gin.RouterGroup) {
|
||||||
|
g.GET("/media/actors", controllers.IndexGetHandler)
|
||||||
|
g.GET("/comic/comics", controllers.IndexGetHandler)
|
||||||
|
g.GET("/media/files", controllers.IndexGetHandler)
|
||||||
|
g.GET("/media/lofis", controllers.IndexGetHandler)
|
||||||
|
g.GET("/auth/logout", controllers.LogoutGetHandler)
|
||||||
|
}
|
||||||
@@ -1,7 +1,18 @@
|
|||||||
package schema
|
package schema
|
||||||
|
|
||||||
import (
|
type MediaLoFi struct {
|
||||||
)
|
Base
|
||||||
|
|
||||||
|
WebLink string `gorm:"uniqueIndex" json:"url"`
|
||||||
|
Review bool `json:"review"`
|
||||||
|
ShouldDownload bool `json:"should_download"`
|
||||||
|
Title string `gorm:"size:300" json:"title"`
|
||||||
|
FileName string `json:"file_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (media_lofi *MediaLoFi) TableName() string {
|
||||||
|
return "media_lofi"
|
||||||
|
}
|
||||||
|
|
||||||
type MediaFile struct {
|
type MediaFile struct {
|
||||||
Base
|
Base
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{{ define "footer.tmpl" }}
|
||||||
|
<div class="footer">{{ .version }}</div>
|
||||||
|
{{end}}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{{ define "header.tmpl" }}
|
||||||
|
<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>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de-DE">
|
||||||
|
{{ template "header.tmpl" . }}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="header"></div>
|
||||||
|
{{ template "navbar.tmpl" . }}
|
||||||
|
<div class="content">{{ .title }}</div>
|
||||||
|
|
||||||
|
{{ template "footer.tmpl" . }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de-DE">
|
||||||
|
{{ template "header.tmpl" . }}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="header"></div>
|
||||||
|
{{ template "navbar.tmpl" . }}
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<form action="/auth/login" method="post">
|
||||||
|
|
||||||
|
<div class="container" style="border: 1px solid lightgray; border-radius: 3px; padding: 15px;">
|
||||||
|
|
||||||
|
{{ if .content }}
|
||||||
|
<div class="notification is-danger">{{ .content }}</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.tmpl" . }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{{ define "navbar.tmpl" }}
|
||||||
|
<div class="topnav">
|
||||||
|
<a href="/">Kontor</a>
|
||||||
|
<a href="/comic/comics">Comics</a>
|
||||||
|
<a href="/media/files">Media</a>
|
||||||
|
{{ if .user }}
|
||||||
|
<a style="float:right" href="/auth/logout/">Logout</a></li>
|
||||||
|
{{ else }}
|
||||||
|
<a style="float:right" href="/auth/login/">Login</a></li>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed html/*
|
||||||
|
var TemplateFiles embed.FS
|
||||||
+1
-1
@@ -11,7 +11,7 @@ source version
|
|||||||
echo "==> Stopping and removing pod kontor"
|
echo "==> Stopping and removing pod kontor"
|
||||||
if podman pod exists kontor; then
|
if podman pod exists kontor; then
|
||||||
podman pod stop kontor
|
podman pod stop kontor
|
||||||
pause 3
|
#pause 3
|
||||||
podman pod rm kontor
|
podman pod rm kontor
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ ExecStart=/usr/bin/podman run \
|
|||||||
--name kontor-fiber \
|
--name kontor-fiber \
|
||||||
--label io.containers.autoupdate=local \
|
--label io.containers.autoupdate=local \
|
||||||
--label PODMAN_SYSTEMD_UNIT=kontor-fiber.service \
|
--label PODMAN_SYSTEMD_UNIT=kontor-fiber.service \
|
||||||
--health-cmd="curl -f http://kontor-echo:8500/health || exit 1" \
|
--health-cmd="curl -f http://kontor-fiber:8500/health || exit 1" \
|
||||||
--health-interval=1s \
|
--health-interval=1s \
|
||||||
--health-timeout=5s \
|
--health-timeout=5s \
|
||||||
--health-retries=10 \
|
--health-retries=10 \
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# kontor-gin.service
|
||||||
|
# autogenerated by Podman 4.9.3
|
||||||
|
# Sun Apr 19 16:28:14 CEST 2026
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Podman kontor-gin.service
|
||||||
|
Documentation=man:podman-generate-systemd(1)
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
RequiresMountsFor=%t/containers
|
||||||
|
BindsTo=pod-kontor.service
|
||||||
|
After=pod-kontor.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Environment=PODMAN_SYSTEMD_UNIT=%n
|
||||||
|
Restart=on-failure
|
||||||
|
TimeoutStopSec=70
|
||||||
|
ExecStart=/usr/bin/podman run \
|
||||||
|
--cidfile=%t/%n.ctr-id \
|
||||||
|
--cgroups=no-conmon \
|
||||||
|
--rm \
|
||||||
|
--pod-id-file %t/pod-kontor.pod-id \
|
||||||
|
--sdnotify=conmon \
|
||||||
|
-d \
|
||||||
|
--replace \
|
||||||
|
--name kontor-gin \
|
||||||
|
--label io.containers.autoupdate=local \
|
||||||
|
--label PODMAN_SYSTEMD_UNIT=kontor-gin.service \
|
||||||
|
--health-cmd="curl -f http://kontor-gin:8550/health || exit 1" \
|
||||||
|
--health-interval=1s \
|
||||||
|
--health-timeout=5s \
|
||||||
|
--health-retries=10 \
|
||||||
|
localhost/kontor-gin:0.4.0
|
||||||
|
ExecStop=/usr/bin/podman stop \
|
||||||
|
--ignore -t 10 \
|
||||||
|
--cidfile=%t/%n.ctr-id
|
||||||
|
ExecStopPost=/usr/bin/podman rm \
|
||||||
|
-f \
|
||||||
|
--ignore -t 10 \
|
||||||
|
--cidfile=%t/%n.ctr-id
|
||||||
|
Type=notify
|
||||||
|
NotifyAccess=all
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
Reference in New Issue
Block a user