This commit is contained in:
@@ -10,7 +10,7 @@ RUN go mod download
|
|||||||
# Copy the source code
|
# Copy the source code
|
||||||
COPY . .
|
COPY . .
|
||||||
# Build the Go application (strip debug info for smaller size)
|
# 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" -o kontor-gin cmd/kontor-gin/main.go
|
||||||
|
|
||||||
# ---------- Stage 2: Final ----------
|
# ---------- Stage 2: Final ----------
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
@@ -19,7 +19,9 @@ WORKDIR /app
|
|||||||
# Install runtime dependencies you actually need
|
# Install runtime dependencies you actually need
|
||||||
RUN apk add --no-cache ca-certificates tzdata curl
|
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-gin /app/kontor-gin
|
||||||
|
COPY --from=builder /app/templates /app/templates
|
||||||
|
COPY --from=builder /app/assets /app/assets
|
||||||
ENV DB_HOST=postgres DB_USER=kontor DB_PASSWORD=kontor DB_NAME=kontor DB_PORT=5432 GIN_MODE=release
|
ENV DB_HOST=postgres DB_USER=kontor DB_PASSWORD=kontor DB_NAME=kontor DB_PORT=5432 GIN_MODE=release
|
||||||
# Set the entrypoint command
|
# Set the entrypoint command
|
||||||
ENTRYPOINT ["/app/kontor"]
|
ENTRYPOINT ["/app/kontor-gin"]
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "kontor-api-gin/pkg/handler"
|
||||||
|
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"kontor-api-gin/pkg/middleware"
|
||||||
|
"kontor-api-gin/pkg/routes"
|
||||||
|
"kontor-api-gin/pkg/schema"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/sessions/cookie"
|
||||||
|
gin_slog "github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
slog.Info("Kontor started")
|
||||||
|
|
||||||
|
// Set Gin mode based on environment
|
||||||
|
// Production mode disables debug output
|
||||||
|
if os.Getenv("GIN_MODE") == "release" {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to database
|
||||||
|
schema.Connect()
|
||||||
|
|
||||||
|
// Run migrations
|
||||||
|
schema.RunMigrations()
|
||||||
|
|
||||||
|
// Create Gin router with default middleware
|
||||||
|
// Default includes Logger and Recovery middleware
|
||||||
|
// router := gin.Default()
|
||||||
|
// Create a new router without default mmiddleware
|
||||||
|
router := gin.New()
|
||||||
|
// Add recovery middleware
|
||||||
|
router.Use(gin.Recovery())
|
||||||
|
router.Use(gin_slog.SetLogger(
|
||||||
|
gin_slog.WithSkipPath([]string{"/health", "/public/style/main.css"}),
|
||||||
|
gin_slog.WithDefaultLevel(slog.LevelInfo),
|
||||||
|
gin_slog.WithClientErrorLevel(slog.LevelInfo),
|
||||||
|
gin_slog.WithServerErrorLevel(slog.LevelInfo),
|
||||||
|
))
|
||||||
|
|
||||||
|
router.LoadHTMLGlob("templates/**/*.html")
|
||||||
|
router.Static("/public", "./assets")
|
||||||
|
|
||||||
|
router.Use(sessions.Sessions("session", cookie.NewStore(globals.Secret)))
|
||||||
|
|
||||||
|
// Setup api routes
|
||||||
|
api := routes.SetupApiRoutes(router)
|
||||||
|
api.Use(middleware.CorsMiddleware)
|
||||||
|
routes.SetupMediaRoutes(api)
|
||||||
|
routes.SetupComicRoutes(api)
|
||||||
|
|
||||||
|
// Setup public Webapp routes
|
||||||
|
routes.PublicRoutes(router)
|
||||||
|
|
||||||
|
// Setup protected Webapp routes
|
||||||
|
protected := router.Group("/auth")
|
||||||
|
protected.Use(middleware.AuthRequired)
|
||||||
|
routes.ProtectedRoutes(router)
|
||||||
|
|
||||||
|
comics := router.Group("/comics")
|
||||||
|
comics.Use(middleware.AuthRequired)
|
||||||
|
routes.ComicRoutes(router)
|
||||||
|
|
||||||
|
media := router.Group("/media")
|
||||||
|
media.Use(middleware.AuthRequired)
|
||||||
|
routes.MediaRoutes(router)
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
port := os.Getenv("PORT")
|
||||||
|
if port == "" {
|
||||||
|
port = "8550"
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("Server starting", "port", port)
|
||||||
|
if err := router.Run(":" + port); err != nil {
|
||||||
|
log.Fatal("Failed to start server:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("Kontor finished")
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ func main() {
|
|||||||
gin_slog.WithServerErrorLevel(slog.LevelInfo),
|
gin_slog.WithServerErrorLevel(slog.LevelInfo),
|
||||||
))
|
))
|
||||||
|
|
||||||
templ := template.Must(template.New("").ParseFS(templates.TemplateFiles, "html/*.tmpl"))
|
templ := template.Must(template.New("").ParseFS(templates.TemplateFiles, "layouts/*.html", "pages/*.html"))
|
||||||
router.SetHTMLTemplate(templ)
|
router.SetHTMLTemplate(templ)
|
||||||
|
|
||||||
router.StaticFS("/public", http.FS(assets.StaticFiles))
|
router.StaticFS("/public", http.FS(assets.StaticFiles))
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ func LoginGetHandler(c *gin.Context) {
|
|||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
user := session.Get(globals.UserKey)
|
user := session.Get(globals.UserKey)
|
||||||
logger := slog.Get(c)
|
logger := slog.Get(c)
|
||||||
logger.Info("LoginGetHandler", "user", user)
|
logger.Info("LoginGetHandler", globals.UserKey, user)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
c.HTML(http.StatusBadRequest, "login.tmpl", gin.H{
|
c.HTML(http.StatusBadRequest, "login.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"login": globals.LoginUrl,
|
"login": globals.LoginUrl,
|
||||||
@@ -26,7 +26,7 @@ func LoginGetHandler(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.HTML(http.StatusOK, "login.tmpl", gin.H{
|
c.HTML(http.StatusOK, "login.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"login": globals.LoginUrl,
|
"login": globals.LoginUrl,
|
||||||
@@ -41,7 +41,7 @@ func LoginPostHandler(c *gin.Context) {
|
|||||||
logger := slog.Get(c)
|
logger := slog.Get(c)
|
||||||
logger.Info("LoginPostHandler", "user", user)
|
logger.Info("LoginPostHandler", "user", user)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
c.HTML(http.StatusBadRequest, "login.tmpl", gin.H{
|
c.HTML(http.StatusBadRequest, "login.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"login": globals.LoginUrl,
|
"login": globals.LoginUrl,
|
||||||
@@ -54,7 +54,7 @@ func LoginPostHandler(c *gin.Context) {
|
|||||||
password := c.PostForm("password")
|
password := c.PostForm("password")
|
||||||
|
|
||||||
if helpers.EmptyUserPass(username, password) {
|
if helpers.EmptyUserPass(username, password) {
|
||||||
c.HTML(http.StatusBadRequest, "login.tmpl", gin.H{
|
c.HTML(http.StatusBadRequest, "login.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"login": globals.LoginUrl,
|
"login": globals.LoginUrl,
|
||||||
@@ -63,7 +63,7 @@ func LoginPostHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !helpers.CheckUserPass(username, password) {
|
if !helpers.CheckUserPass(username, password) {
|
||||||
c.HTML(http.StatusUnauthorized, "login.tmpl", gin.H{
|
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"login": globals.LoginUrl,
|
"login": globals.LoginUrl,
|
||||||
@@ -73,7 +73,7 @@ func LoginPostHandler(c *gin.Context) {
|
|||||||
|
|
||||||
session.Set(globals.UserKey, username)
|
session.Set(globals.UserKey, username)
|
||||||
if err := session.Save(); err != nil {
|
if err := session.Save(); err != nil {
|
||||||
c.HTML(http.StatusInternalServerError, "login.tmpl", gin.H{
|
c.HTML(http.StatusInternalServerError, "login.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"login": globals.LoginUrl,
|
"login": globals.LoginUrl,
|
||||||
@@ -90,7 +90,7 @@ func LogoutGetHandler(c *gin.Context) {
|
|||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
user := session.Get(globals.UserKey)
|
user := session.Get(globals.UserKey)
|
||||||
logger := slog.Get(c)
|
logger := slog.Get(c)
|
||||||
logger.Info("LogoutGetHandler", "user", user)
|
logger.Info("LogoutGetHandler", globals.UserKey, user)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
logger.Info("Invalid session token")
|
logger.Info("Invalid session token")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ func IndexGetHandler(c *gin.Context) {
|
|||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
user := session.Get(globals.UserKey)
|
user := session.Get(globals.UserKey)
|
||||||
logger := slog.Get(c)
|
logger := slog.Get(c)
|
||||||
logger.Info("IndexGetHandler", "user", user)
|
logger.Info("IndexGetHandler", globals.UserKey, user)
|
||||||
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
c.HTML(http.StatusOK, "home.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"content": "This is an index page...",
|
"content": "This is an index page...",
|
||||||
|
"login": globals.LoginUrl,
|
||||||
"logout": globals.LogoutUrl,
|
"logout": globals.LogoutUrl,
|
||||||
"user": user,
|
"user": user,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,25 +30,24 @@ func ListMediaLoFis(c *gin.Context) {
|
|||||||
result := schema.DB.Find(&lofis)
|
result := schema.DB.Find(&lofis)
|
||||||
log.Printf("found %d MediaLoFis", result.RowsAffected)
|
log.Printf("found %d MediaLoFis", result.RowsAffected)
|
||||||
c.JSON(http.StatusOK, lofis)
|
c.JSON(http.StatusOK, lofis)
|
||||||
// c.JSON(http.StatusOK, gin.H{
|
|
||||||
// "message": "All MediaLoFis",
|
|
||||||
// "lofis": lofis,
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListMediaActorsGetHandler(c *gin.Context) {
|
func ListMediaActorsGetHandler(c *gin.Context) {
|
||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
user := session.Get(globals.UserKey)
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
|
||||||
var actors []schema.MediaActor
|
var actors []schema.MediaActor
|
||||||
|
|
||||||
result := schema.DB.Find(&actors)
|
result := schema.DB.Find(&actors)
|
||||||
log.Printf("found %d MediaActors", result.RowsAffected)
|
logger.Info("ListMediaActorsGetHandler", "count", result.RowsAffected)
|
||||||
c.HTML(http.StatusOK, "media_actor.tmpl", gin.H{
|
c.HTML(http.StatusOK, "mediaactors.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"content": "Media Actors...",
|
"content": "Media Actors...",
|
||||||
"actors": actors,
|
"actors": actors,
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"logout": globals.LogoutUrl,
|
||||||
"user": user,
|
"user": user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -61,12 +60,14 @@ func ListMediaFilesGetHandler(c *gin.Context) {
|
|||||||
var files []schema.MediaFile
|
var files []schema.MediaFile
|
||||||
|
|
||||||
result := schema.DB.Find(&files)
|
result := schema.DB.Find(&files)
|
||||||
logger.Info("found MediaFiles", "count", result.RowsAffected)
|
logger.Info("ListMediaFilesGetHandler", "count", result.RowsAffected)
|
||||||
c.HTML(http.StatusOK, "media_files.tmpl", gin.H{
|
c.HTML(http.StatusOK, "mediafiles.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"content": "Media Files...",
|
"content": "Media Files...",
|
||||||
"files": files,
|
"files": files,
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"logout": globals.LogoutUrl,
|
||||||
"user": user,
|
"user": user,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -80,14 +81,14 @@ func ListMediaLoFisGetHandler(c *gin.Context) {
|
|||||||
var lofis []schema.MediaLoFi
|
var lofis []schema.MediaLoFi
|
||||||
|
|
||||||
result := schema.DB.Find(&lofis)
|
result := schema.DB.Find(&lofis)
|
||||||
logger.Info("found MediaActors", "count", result.RowsAffected)
|
logger.Info("ListMediaLoFisGetHandler", "count", result.RowsAffected)
|
||||||
c.HTML(http.StatusOK, "media_lofi.tmpl", gin.H{
|
c.HTML(http.StatusOK, "media_lofi.html", gin.H{
|
||||||
"title": "Kontor Gin",
|
"title": "Kontor Gin",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"content": "Media LoFis...",
|
"content": "Media LoFis...",
|
||||||
"lofis": lofis,
|
"lofis": lofis,
|
||||||
|
"login": globals.LoginUrl,
|
||||||
"logout": globals.LogoutUrl,
|
"logout": globals.LogoutUrl,
|
||||||
"user": user,
|
"user": user,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ const UserKey = "user"
|
|||||||
|
|
||||||
const Version = "0.4.0"
|
const Version = "0.4.0"
|
||||||
|
|
||||||
const LoginUrl = "/login"
|
const LoginUrl = "/auth/login"
|
||||||
|
|
||||||
const LogoutUrl = "/exit"
|
const LogoutUrl = "/auth/logout"
|
||||||
|
|||||||
@@ -3,21 +3,32 @@ package routes
|
|||||||
import (
|
import (
|
||||||
"kontor-api-gin/pkg/controllers"
|
"kontor-api-gin/pkg/controllers"
|
||||||
"kontor-api-gin/pkg/globals"
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"kontor-api-gin/pkg/middleware"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PublicRoutes(g *gin.RouterGroup) {
|
func PublicRoutes(g *gin.Engine) {
|
||||||
g.GET("/health", controllers.HealthGetHandler)
|
g.GET("/health", controllers.HealthGetHandler)
|
||||||
g.GET(globals.LoginUrl, controllers.LoginGetHandler)
|
g.GET(globals.LoginUrl, controllers.LoginGetHandler)
|
||||||
g.POST(globals.LoginUrl, controllers.LoginPostHandler)
|
g.POST(globals.LoginUrl, controllers.LoginPostHandler)
|
||||||
g.GET("/comic/comics", controllers.ListComicsGetHandler)
|
g.GET("/", controllers.IndexGetHandler)
|
||||||
g.GET("", controllers.IndexGetHandler)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProtectedRoutes(g *gin.RouterGroup) {
|
func ComicRoutes(g *gin.Engine) {
|
||||||
g.GET(globals.LogoutUrl, controllers.LogoutGetHandler)
|
g.GET("/comic/comics", controllers.ListComicsGetHandler)
|
||||||
g.GET("/media/actors", controllers.ListMediaActorsGetHandler)
|
}
|
||||||
// g.GET("/media/files", controllers.ListMediaFilesGetHandler)
|
|
||||||
g.GET("/media/lofis", controllers.ListMediaLoFisGetHandler)
|
func MediaRoutes(g *gin.Engine) {
|
||||||
|
media := g.Group("/media")
|
||||||
|
media.Use(middleware.AuthRequired)
|
||||||
|
media.GET("/actors", controllers.ListMediaActorsGetHandler)
|
||||||
|
media.GET("/files", controllers.ListMediaFilesGetHandler)
|
||||||
|
media.GET("/lofis", controllers.ListMediaLoFisGetHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProtectedRoutes(g *gin.Engine) {
|
||||||
|
auth := g.Group("/auth")
|
||||||
|
auth.Use(middleware.AuthRequired)
|
||||||
|
auth.GET("/logout", controllers.LogoutGetHandler)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="de-DE">
|
|
||||||
{{ template "header.tmpl" . }}
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="header"></div>
|
|
||||||
{{ template "navbar.tmpl" . }}
|
|
||||||
<div class="content">{{ .content }}</div>
|
|
||||||
<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 .order }}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="/tick.png" alt="true" width="24" height="24">
|
|
||||||
{{ else }}
|
|
||||||
<img src="/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.tmpl" . }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{{ define "footer.tmpl" }}
|
|
||||||
<div class="footer">{{ .version }}</div>
|
|
||||||
{{end}}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{{ 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 }}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="de-DE">
|
|
||||||
{{ template "header.tmpl" . }}
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="header"></div>
|
|
||||||
{{ template "navbar.tmpl" . }}
|
|
||||||
<div class="content">{{ .content }}</div>
|
|
||||||
|
|
||||||
{{ template "footer.tmpl" . }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="de-DE">
|
|
||||||
{{ template "header.tmpl" . }}
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="header"></div>
|
|
||||||
{{ template "navbar.tmpl" . }}
|
|
||||||
<div class="content">{{ .content }}</div>
|
|
||||||
|
|
||||||
{{ template "footer.tmpl" . }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="de-DE">
|
|
||||||
{{ template "header.tmpl" . }}
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="header"></div>
|
|
||||||
{{ template "navbar.tmpl" . }}
|
|
||||||
<div class="content">{{ .content }}</div>
|
|
||||||
|
|
||||||
{{ template "footer.tmpl" . }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -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 }}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
{{ define "navbar.tmpl" }}
|
{{ define "navbar" }}
|
||||||
<div class="topnav">
|
<div class="topnav">
|
||||||
<a href="/">Kontor</a>
|
<a href="/">Kontor</a>
|
||||||
<a href="/comic/comics">Comics</a>
|
<a href="/comic/comics">Comics</a>
|
||||||
<a href="/media/files">Media</a>
|
<a href="/media/files">Media</a>
|
||||||
<a href="/media/lofis">Lofi</a>
|
<a href="/media/lofis">Lofi</a>
|
||||||
{{ if .user }}
|
{{ if .user }}
|
||||||
<a style="float:right" href={{ .logout }}>Logout</a></li>
|
<a style="float:right" href="{{ .logout }}">Logout</a></li>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<a style="float:right" href="/login">Login</a></li>
|
<a style="float:right" href="{{ .login }}">Login</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{{ define "comics.html" }}
|
||||||
|
{{ template "main" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<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 .order }}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="/tick.png" alt="true" width="24" height="24">
|
||||||
|
{{ else }}
|
||||||
|
<img src="/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>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{{ define "home.html" }}
|
||||||
|
{{ template "main" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<h1>{{ .content }}</h1>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{{ define "media_lofi.html" }}
|
||||||
|
{{ template "main" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<div class="row">
|
||||||
|
<form class="form-inline" action="/media/lofi">
|
||||||
|
<input type="text" placeholder="Search" name="query">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="review" {{ if .Review }}checked{{ end }}>Completed
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="should_download" {{ if .ShouldDownload }}checked{{ end }}>Order
|
||||||
|
</label>
|
||||||
|
<button type="submit">Search</button>
|
||||||
|
<div class="pill-nav"><a href="/media/lofi/add">Add Lofi</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="/media/lofi/{{.ID}}">{{.Title}}</a></th>
|
||||||
|
{{ if .WebLink }}
|
||||||
|
<td><a href="{{.WebLink}}">{{.Title}}</a></td>
|
||||||
|
{{ else }}
|
||||||
|
<td>WebLink not set</td>
|
||||||
|
{{ end }}
|
||||||
|
<td>
|
||||||
|
{{ if .Review }}
|
||||||
|
<img src="/tick.png" alt="true" width="24" height="24">
|
||||||
|
{{ else }}
|
||||||
|
<img src="/cross.png" alt="true" width="24" height="24">
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
<td><a href="/media/lofi/edit/{{.ID}}" class="btn btn-outline-primary btn-sm active" role="button" aria-pressed="true">Edit</a></td>
|
||||||
|
<td><a href="/media/lofi/delete/{{.ID}}" class="btn btn-outline-danger btn-sm active" role="button" aria-pressed="true">Delete</a></td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
<!doctype html>
|
{{ define "login.html" }}
|
||||||
<html lang="de-DE">
|
{{ template "main" . }}
|
||||||
{{ template "header.tmpl" . }}
|
{{ end }}
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="header"></div>
|
|
||||||
{{ template "navbar.tmpl" . }}
|
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<form action={{ .login }} method="post">
|
<form action={{ .login }} method="post">
|
||||||
|
|
||||||
@@ -34,7 +31,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
{{ template "footer.tmpl" . }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{{ define "home.html" }}
|
||||||
|
{{ template "main" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<h1>{{ .content }}</h1>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{{ define "mediafiles.html" }}
|
||||||
|
{{ template "main" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<h1>{{ .content }}</h1>
|
||||||
|
{{ end }}
|
||||||
Reference in New Issue
Block a user