This commit is contained in:
@@ -10,7 +10,7 @@ RUN go mod download
|
||||
# Copy the source code
|
||||
COPY . .
|
||||
# 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 ----------
|
||||
FROM alpine:latest
|
||||
@@ -19,7 +19,9 @@ WORKDIR /app
|
||||
# Install runtime dependencies you actually need
|
||||
RUN apk add --no-cache ca-certificates tzdata curl
|
||||
# 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
|
||||
# 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),
|
||||
))
|
||||
|
||||
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.StaticFS("/public", http.FS(assets.StaticFiles))
|
||||
|
||||
@@ -15,9 +15,9 @@ func LoginGetHandler(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
user := session.Get(globals.UserKey)
|
||||
logger := slog.Get(c)
|
||||
logger.Info("LoginGetHandler", "user", user)
|
||||
logger.Info("LoginGetHandler", globals.UserKey, user)
|
||||
if user != nil {
|
||||
c.HTML(http.StatusBadRequest, "login.tmpl", gin.H{
|
||||
c.HTML(http.StatusBadRequest, "login.html", gin.H{
|
||||
"title": "Kontor Gin",
|
||||
"version": "0.4.0",
|
||||
"login": globals.LoginUrl,
|
||||
@@ -26,7 +26,7 @@ func LoginGetHandler(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "login.tmpl", gin.H{
|
||||
c.HTML(http.StatusOK, "login.html", gin.H{
|
||||
"title": "Kontor Gin",
|
||||
"version": "0.4.0",
|
||||
"login": globals.LoginUrl,
|
||||
@@ -41,7 +41,7 @@ func LoginPostHandler(c *gin.Context) {
|
||||
logger := slog.Get(c)
|
||||
logger.Info("LoginPostHandler", "user", user)
|
||||
if user != nil {
|
||||
c.HTML(http.StatusBadRequest, "login.tmpl", gin.H{
|
||||
c.HTML(http.StatusBadRequest, "login.html", gin.H{
|
||||
"title": "Kontor Gin",
|
||||
"version": "0.4.0",
|
||||
"login": globals.LoginUrl,
|
||||
@@ -54,7 +54,7 @@ func LoginPostHandler(c *gin.Context) {
|
||||
password := c.PostForm("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",
|
||||
"version": "0.4.0",
|
||||
"login": globals.LoginUrl,
|
||||
@@ -63,7 +63,7 @@ func LoginPostHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
if !helpers.CheckUserPass(username, password) {
|
||||
c.HTML(http.StatusUnauthorized, "login.tmpl", gin.H{
|
||||
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
|
||||
"title": "Kontor Gin",
|
||||
"version": "0.4.0",
|
||||
"login": globals.LoginUrl,
|
||||
@@ -73,7 +73,7 @@ func LoginPostHandler(c *gin.Context) {
|
||||
|
||||
session.Set(globals.UserKey, username)
|
||||
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",
|
||||
"version": "0.4.0",
|
||||
"login": globals.LoginUrl,
|
||||
@@ -90,7 +90,7 @@ func LogoutGetHandler(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
user := session.Get(globals.UserKey)
|
||||
logger := slog.Get(c)
|
||||
logger.Info("LogoutGetHandler", "user", user)
|
||||
logger.Info("LogoutGetHandler", globals.UserKey, user)
|
||||
if user == nil {
|
||||
logger.Info("Invalid session token")
|
||||
return
|
||||
|
||||
@@ -13,11 +13,12 @@ func IndexGetHandler(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
user := session.Get(globals.UserKey)
|
||||
logger := slog.Get(c)
|
||||
logger.Info("IndexGetHandler", "user", user)
|
||||
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
||||
logger.Info("IndexGetHandler", globals.UserKey, user)
|
||||
c.HTML(http.StatusOK, "home.html", gin.H{
|
||||
"title": "Kontor Gin",
|
||||
"version": "0.4.0",
|
||||
"content": "This is an index page...",
|
||||
"login": globals.LoginUrl,
|
||||
"logout": globals.LogoutUrl,
|
||||
"user": user,
|
||||
})
|
||||
|
||||
@@ -30,25 +30,24 @@ func ListMediaLoFis(c *gin.Context) {
|
||||
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,
|
||||
// })
|
||||
}
|
||||
|
||||
func ListMediaActorsGetHandler(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
user := session.Get(globals.UserKey)
|
||||
logger := slog.Get(c)
|
||||
|
||||
var actors []schema.MediaActor
|
||||
|
||||
result := schema.DB.Find(&actors)
|
||||
log.Printf("found %d MediaActors", result.RowsAffected)
|
||||
c.HTML(http.StatusOK, "media_actor.tmpl", gin.H{
|
||||
logger.Info("ListMediaActorsGetHandler", "count", result.RowsAffected)
|
||||
c.HTML(http.StatusOK, "mediaactors.html", gin.H{
|
||||
"title": "Kontor Gin",
|
||||
"version": "0.4.0",
|
||||
"content": "Media Actors...",
|
||||
"actors": actors,
|
||||
"login": globals.LoginUrl,
|
||||
"logout": globals.LogoutUrl,
|
||||
"user": user,
|
||||
})
|
||||
}
|
||||
@@ -61,12 +60,14 @@ func ListMediaFilesGetHandler(c *gin.Context) {
|
||||
var files []schema.MediaFile
|
||||
|
||||
result := schema.DB.Find(&files)
|
||||
logger.Info("found MediaFiles", "count", result.RowsAffected)
|
||||
c.HTML(http.StatusOK, "media_files.tmpl", gin.H{
|
||||
logger.Info("ListMediaFilesGetHandler", "count", result.RowsAffected)
|
||||
c.HTML(http.StatusOK, "mediafiles.html", gin.H{
|
||||
"title": "Kontor Gin",
|
||||
"version": "0.4.0",
|
||||
"content": "Media Files...",
|
||||
"files": files,
|
||||
"login": globals.LoginUrl,
|
||||
"logout": globals.LogoutUrl,
|
||||
"user": user,
|
||||
})
|
||||
|
||||
@@ -80,14 +81,14 @@ func ListMediaLoFisGetHandler(c *gin.Context) {
|
||||
var lofis []schema.MediaLoFi
|
||||
|
||||
result := schema.DB.Find(&lofis)
|
||||
logger.Info("found MediaActors", "count", result.RowsAffected)
|
||||
c.HTML(http.StatusOK, "media_lofi.tmpl", gin.H{
|
||||
logger.Info("ListMediaLoFisGetHandler", "count", result.RowsAffected)
|
||||
c.HTML(http.StatusOK, "media_lofi.html", gin.H{
|
||||
"title": "Kontor Gin",
|
||||
"version": "0.4.0",
|
||||
"content": "Media LoFis...",
|
||||
"lofis": lofis,
|
||||
"login": globals.LoginUrl,
|
||||
"logout": globals.LogoutUrl,
|
||||
"user": user,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ const UserKey = "user"
|
||||
|
||||
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 (
|
||||
"kontor-api-gin/pkg/controllers"
|
||||
"kontor-api-gin/pkg/globals"
|
||||
"kontor-api-gin/pkg/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PublicRoutes(g *gin.RouterGroup) {
|
||||
func PublicRoutes(g *gin.Engine) {
|
||||
g.GET("/health", controllers.HealthGetHandler)
|
||||
g.GET(globals.LoginUrl, controllers.LoginGetHandler)
|
||||
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) {
|
||||
g.GET(globals.LogoutUrl, controllers.LogoutGetHandler)
|
||||
g.GET("/media/actors", controllers.ListMediaActorsGetHandler)
|
||||
// g.GET("/media/files", controllers.ListMediaFilesGetHandler)
|
||||
g.GET("/media/lofis", controllers.ListMediaLoFisGetHandler)
|
||||
func ComicRoutes(g *gin.Engine) {
|
||||
g.GET("/comic/comics", controllers.ListComicsGetHandler)
|
||||
}
|
||||
|
||||
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">
|
||||
<a href="/">Kontor</a>
|
||||
<a href="/comic/comics">Comics</a>
|
||||
<a href="/media/files">Media</a>
|
||||
<a href="/media/lofis">Lofi</a>
|
||||
{{ if .user }}
|
||||
<a style="float:right" href={{ .logout }}>Logout</a></li>
|
||||
<a style="float:right" href="{{ .logout }}">Logout</a></li>
|
||||
{{ else }}
|
||||
<a style="float:right" href="/login">Login</a></li>
|
||||
<a style="float:right" href="{{ .login }}">Login</a></li>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ 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>
|
||||
<html lang="de-DE">
|
||||
{{ template "header.tmpl" . }}
|
||||
|
||||
<body>
|
||||
<div class="header"></div>
|
||||
{{ template "navbar.tmpl" . }}
|
||||
{{ define "login.html" }}
|
||||
{{ template "main" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "content" }}
|
||||
<div class="content">
|
||||
<form action={{ .login }} method="post">
|
||||
|
||||
@@ -34,7 +31,4 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{ template "footer.tmpl" . }}
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -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