This commit is contained in:
@@ -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 (
|
||||
// "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/schema"
|
||||
"kontor-api-gin/templates"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -27,18 +35,40 @@ func main() {
|
||||
|
||||
// Create Gin router with default 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
|
||||
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)
|
||||
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
|
||||
router.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "healthy",
|
||||
})
|
||||
})
|
||||
@@ -56,19 +86,3 @@ func main() {
|
||||
|
||||
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/cloudwego/base64x v0.1.7 // 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-gonic/gin v1.12.0 // 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-yaml v1.19.2 // 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/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // 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/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/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/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
|
||||
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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
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/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
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 (
|
||||
"kontor-api-gin/pkg/schema"
|
||||
@@ -20,3 +20,15 @@ func ListMediaFiles(c *gin.Context) {
|
||||
"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
|
||||
|
||||
import (
|
||||
"kontor-api-gin/pkg/handlers"
|
||||
"kontor-api-gin/pkg/controllers"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -11,6 +11,11 @@ func SetupMediaRoutes(router *gin.RouterGroup) {
|
||||
|
||||
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,37 +1,48 @@
|
||||
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 {
|
||||
Base
|
||||
|
||||
CloudLink string `json:"cloud_link"`
|
||||
FileName string `json:"file_name"`
|
||||
Path string `json:"path"`
|
||||
Review bool `json:"review"`
|
||||
Title string `gorm:"size:300" json:"title"`
|
||||
WebLink string `gorm:"uniqueIndex" json:"url"`
|
||||
ShouldDownload bool `json:"should_download"`
|
||||
CloudLink string `json:"cloud_link"`
|
||||
FileName string `json:"file_name"`
|
||||
Path string `json:"path"`
|
||||
Review bool `json:"review"`
|
||||
Title string `gorm:"size:300" json:"title"`
|
||||
WebLink string `gorm:"uniqueIndex" json:"url"`
|
||||
ShouldDownload bool `json:"should_download"`
|
||||
|
||||
MediaActorFiles []MediaActorFile `gorm:"foreignKey:MediaFileID" json:"media_actor_files"`
|
||||
}
|
||||
|
||||
func (media_file *MediaFile) TableName() string {
|
||||
return "media_file"
|
||||
return "media_file"
|
||||
}
|
||||
|
||||
type MediaActor struct {
|
||||
Base
|
||||
|
||||
Name string `json:"name"`
|
||||
WebLink string `gorm:"uniqueIndex" json:"url"`
|
||||
Name string `json:"name"`
|
||||
WebLink string `gorm:"uniqueIndex" json:"url"`
|
||||
|
||||
MediaActorFiles []MediaActorFile `gorm:"foreignKey:MediaActorID" json:"media_actor_files"`
|
||||
}
|
||||
|
||||
func (media_actor *MediaActor) TableName() string {
|
||||
return "media_actor"
|
||||
return "media_actor"
|
||||
}
|
||||
|
||||
type MediaActorFile struct {
|
||||
@@ -44,7 +55,7 @@ type MediaActorFile struct {
|
||||
}
|
||||
|
||||
func (media_file *MediaActorFile) TableName() string {
|
||||
return "media_actor_file"
|
||||
return "media_actor_file"
|
||||
}
|
||||
|
||||
// type MediaArticle struct {
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user