Compare commits
19 Commits
v0.3.0
..
c848758821
| Author | SHA1 | Date | |
|---|---|---|---|
| c848758821 | |||
| 2d6fa7b7ee | |||
| f0f1819a1f | |||
| 131296c644 | |||
| ba83d70764 | |||
| a0ad4e5c26 | |||
| 6b5167a5c0 | |||
| 044da5353a | |||
| f2f4a0aaf6 | |||
| 652aa3005b | |||
| ad09386a7a | |||
| 8f5a384e89 | |||
| c1d913e36b | |||
| 8a5bc7873a | |||
| 0ad1969614 | |||
| d80aefe6b2 | |||
| 652378e7de | |||
| 57b942a9ca | |||
| bda453a92a |
@@ -11,3 +11,4 @@ kontor-robyn.bak
|
|||||||
kontor-robyn.bak2
|
kontor-robyn.bak2
|
||||||
kontor-data/.gradle
|
kontor-data/.gradle
|
||||||
kontor-data/build/
|
kontor-data/build/
|
||||||
|
/kontor-api/kontor-api.code-workspace
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ NEXUS_URL=https://nexus.thpeetz.de
|
|||||||
MAVEN_REPO=maven-snapshots
|
MAVEN_REPO=maven-snapshots
|
||||||
GROUP_ID=de.thpeetz
|
GROUP_ID=de.thpeetz
|
||||||
ARTIFACT_ID=kontor-spring
|
ARTIFACT_ID=kontor-spring
|
||||||
VERSION=0.3.0
|
VERSION=0.4.0
|
||||||
FILE_EXTENSION=jar
|
FILE_EXTENSION=jar
|
||||||
|
|
||||||
download_url=$(curl -X GET "${NEXUS_URL}/service/rest/v1/search/assets?repository=${MAVEN_REPO}&maven.groupId=${GROUP_ID}&maven.artifactId=${ARTIFACT_ID}&maven.baseVersion=${VERSION}&maven.extension=${FILE_EXTENSION}" -H "accept: application/json" | jq -rc '.items | .[].downloadUrl' | sort | tail -n 1)
|
download_url=$(curl -X GET "${NEXUS_URL}/service/rest/v1/search/assets?repository=${MAVEN_REPO}&maven.groupId=${GROUP_ID}&maven.artifactId=${ARTIFACT_ID}&maven.baseVersion=${VERSION}&maven.extension=${FILE_EXTENSION}" -H "accept: application/json" | jq -rc '.items | .[].downloadUrl' | sort | tail -n 1)
|
||||||
|
|||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# script/cibuild:
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
source version
|
||||||
|
|
||||||
|
echo " => Baue Image localhost/kontor-angular:$kontorVersion"
|
||||||
|
buildah build -t kontor-angular:$kontorVersion kontor-angular
|
||||||
|
|
||||||
@@ -14,9 +14,6 @@ ENV PATH="/root/.local/bin:${PATH}"
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY ./pyproject.toml .
|
COPY ./pyproject.toml .
|
||||||
#RUN --mount=type=bind,source=/home/tpeetz/projects/kontor/kontor-model,target=/container/kontor-model uv add /container/kontor-model
|
|
||||||
#COPY ../kontor-model/ /container
|
|
||||||
RUN uv add /container/kontor-model
|
|
||||||
RUN uv sync
|
RUN uv sync
|
||||||
|
|
||||||
# ------------------------------- Production Stage ------------------------------ ##
|
# ------------------------------- Production Stage ------------------------------ ##
|
||||||
|
|||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# script/cibuild:
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
source version
|
||||||
|
|
||||||
|
echo " => Baue Image localhost/kontor-api:$kontorVersion"
|
||||||
|
buildah build -t kontor-api:$kontorVersion kontor-api
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "kontor-api"
|
name = "kontor-api"
|
||||||
version = "0.1.0"
|
version = "0.4.0"
|
||||||
description = "Add your description here"
|
description = "Add your description here"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
@@ -32,5 +32,13 @@ dependencies = [
|
|||||||
"kontor-model",
|
"kontor-model",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.uv.sources]
|
[[tool.uv.index]]
|
||||||
kontor-model = { path = "../kontor-model", editable = true }
|
name = "nexus"
|
||||||
|
url = "https://nexus.thpeetz.de/repository/pypi-group/simple"
|
||||||
|
publish-url = "https://nexus.thpeetz.de/repository/pypi-internal/"
|
||||||
|
default = true
|
||||||
|
|
||||||
|
|
||||||
|
[[tool.uv.index]]
|
||||||
|
name = "nexus-proxy"
|
||||||
|
url = "https://nexus.thpeetz.de/repository/pypi-proxy/simple"
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, status, HTTPException
|
||||||
|
|
||||||
from kontor_model.db.models.media import MediaLofi
|
from kontor_model.db.models.media import MediaLofi
|
||||||
from src.db.session import SessionDep
|
from src.db.session import SessionDep
|
||||||
from kontor_model.schema.media.lofi import MediaLofiResponse, lofi_to_response
|
from src.core.log_conf import logger
|
||||||
|
from kontor_model.schema.media.lofi import (
|
||||||
|
MediaLofiModel,
|
||||||
|
MediaLofiResponse,
|
||||||
|
lofi_to_response,
|
||||||
|
lofi_to_model
|
||||||
|
)
|
||||||
|
from kontor_model.db.repository.media.lofi import (
|
||||||
|
import_medialofi,
|
||||||
|
delete_medialofi
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@@ -40,3 +50,42 @@ def get_lofi(lofi_id: str, db: SessionDep) -> MediaLofiResponse:
|
|||||||
raise HTTPException(status_code=404, detail="MediaLofi could not be found")
|
raise HTTPException(status_code=404, detail="MediaLofi could not be found")
|
||||||
response = lofi_to_response(lofi)
|
response = lofi_to_response(lofi)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@router.delete("/lofi/{lofi_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
|
def delete_lofi(lofi_id: str, db: SessionDep):
|
||||||
|
"""
|
||||||
|
Delete MediaFile by given id.
|
||||||
|
"""
|
||||||
|
lofi = db.get(MediaLofi, lofi_id)
|
||||||
|
if not lofi:
|
||||||
|
raise HTTPException(status_code=404, detail="MediaLofi could not be found")
|
||||||
|
logger.info("delete MediaLofi: %s", lofi_id)
|
||||||
|
delete_medialofi(db=db, lofi_id=lofi.id)
|
||||||
|
|
||||||
|
@router.put("/lofi/{lofi_id}", response_model=MediaLofiResponse)
|
||||||
|
def update_lofi(lofi_id: str, db: SessionDep, info: MediaLofiResponse) -> MediaLofiResponse:
|
||||||
|
"""
|
||||||
|
Update MediaLofi with given id and data.
|
||||||
|
"""
|
||||||
|
media_lofi = db.get(MediaLofi, lofi_id)
|
||||||
|
if not media_lofi:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="MediaLofi could not be found")
|
||||||
|
lofi_to_model(info, media_lofi)
|
||||||
|
db.add(media_lofi)
|
||||||
|
db.commit()
|
||||||
|
media_lofi = db.get(MediaLofi, lofi_id)
|
||||||
|
if not media_lofi:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="MediaLofi could not be found")
|
||||||
|
response = lofi_to_response(media_lofi)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/lofi", status_code=status.HTTP_201_CREATED)
|
||||||
|
def add_lofi(new_lofi: MediaLofiModel, db: SessionDep) -> MediaLofiResponse:
|
||||||
|
logger.info("add medialofi %s", new_lofi)
|
||||||
|
try:
|
||||||
|
medialofi: MediaLofi = import_medialofi(db, new_lofi)
|
||||||
|
except:
|
||||||
|
raise HTTPException(status_code=409, detail="MediaLofi duplicate")
|
||||||
|
response = lofi_to_response(medialofi)
|
||||||
|
return response
|
||||||
|
|||||||
@@ -9,17 +9,21 @@ load_dotenv(dotenv_path=env_path)
|
|||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
PROJECT_NAME: str = "Kontor"
|
PROJECT_NAME: str = "Kontor"
|
||||||
PROJECT_VERSION: str = "0.3.0"
|
PROJECT_VERSION: str = "0.4.0"
|
||||||
|
|
||||||
DB_USER: str = os.getenv("DB_USER", "kontor")
|
DB_USER: str = os.getenv("DB_USER", "kontor")
|
||||||
DB_PASSWORD: str = os.getenv("DB_PASSWORD", "kontor")
|
DB_PASSWORD: str = os.getenv("DB_PASSWORD", "kontor")
|
||||||
DB_SERVER: str = os.getenv("DB_SERVER", "postgres")
|
DB_SERVER: str = os.getenv("DB_SERVER", "postgres")
|
||||||
DB_PORT: int = int(os.getenv("DB_PORT", 5432))
|
DB_PORT: int = int(os.getenv("DB_PORT", 5432))
|
||||||
DB_DBNAME: str = os.getenv("DB_DBNAME", "kontor")
|
DB_DBNAME: str = os.getenv("DB_DBNAME", "kontor")
|
||||||
DATABASE_URL: str = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}"
|
DATABASE_URL: str = (
|
||||||
SECRET_KEY: str = os.getenv("SECRET_KEY", "J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc=")
|
f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}"
|
||||||
|
)
|
||||||
|
SECRET_KEY: str = os.getenv(
|
||||||
|
"SECRET_KEY", "J6GOtcwC2NJI1l0VkHu20PacPFGTxpirBxWwynoHjsc="
|
||||||
|
)
|
||||||
ALGORITHM = "HS256"
|
ALGORITHM = "HS256"
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # one week in mins
|
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # one week in mins
|
||||||
|
|
||||||
settings = Settings()
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|||||||
Generated
+1007
-596
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
# This file was generated by the Gradle 'init' task.
|
# This file was generated by the Gradle 'init' task.
|
||||||
# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
|
# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
|
||||||
description='Kontor Documentation'
|
description='Kontor Documentation'
|
||||||
version=0.3.0
|
version=0.4.0-SNAPSHOT
|
||||||
group=de.thpeetz
|
group=de.thpeetz
|
||||||
org.gradle.configuration-cache=true
|
org.gradle.configuration-cache=true
|
||||||
|
|
||||||
|
|||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# script/cibuild:
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
source version
|
||||||
|
|
||||||
|
echo " => Baue Image localhost/kontor-echo:$kontorVersion"
|
||||||
|
buildah build -t kontor-echo:$kontorVersion kontor-echo
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# ---------- Stage 1: Build ----------
|
# ---------- Stage 1: Build ----------
|
||||||
FROM docker.io/library/golang:1.25-alpine AS builder
|
FROM docker.io/library/golang:1.26-alpine AS builder
|
||||||
# Set the working directory
|
# Set the working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
# Ensure a portable, static-ish binary
|
# Ensure a portable, static-ish binary
|
||||||
@@ -20,5 +20,7 @@ WORKDIR /app
|
|||||||
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 /app/kontor
|
||||||
|
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"]
|
||||||
|
|||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# script/cibuild:
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
source version
|
||||||
|
|
||||||
|
echo " => Baue Image localhost/kontor-fiber:$kontorVersion"
|
||||||
|
buildah build -t kontor-fiber:$kontorVersion kontor-fiber
|
||||||
|
|
||||||
@@ -3,11 +3,15 @@ package main
|
|||||||
import (
|
import (
|
||||||
"kontor-api-fiber/pkg/handler"
|
"kontor-api-fiber/pkg/handler"
|
||||||
"kontor-api-fiber/pkg/schema"
|
"kontor-api-fiber/pkg/schema"
|
||||||
|
"kontor-api-fiber/pkg/web"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
jwtware "github.com/gofiber/contrib/jwt"
|
jwtware "github.com/gofiber/contrib/v3/jwt"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v3"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
"github.com/gofiber/fiber/v3/middleware/logger"
|
||||||
|
"github.com/gofiber/fiber/v3/middleware/static"
|
||||||
|
"github.com/gofiber/template/html/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -16,24 +20,39 @@ func main() {
|
|||||||
if err := schema.Connect(); err != nil {
|
if err := schema.Connect(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
// Create a new engine
|
||||||
|
engine := html.New("./views", ".html")
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New(fiber.Config{
|
||||||
|
Views: engine,
|
||||||
// app.Use(jwtware.New(jwtware.Config{
|
ViewsLayout: "layouts/main",
|
||||||
// SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
})
|
||||||
// }))
|
|
||||||
|
|
||||||
//app.Use(logger.New())
|
//app.Use(logger.New())
|
||||||
|
|
||||||
app.Get("/health", handler.GetHealth)
|
app.Get("/health", handler.GetHealth)
|
||||||
app.Post("/login", handler.Login)
|
app.Post("/login", handler.Login)
|
||||||
|
web.SetupRoutes(app)
|
||||||
|
|
||||||
api := app.Group("/api", logger.New(), jwtware.New(jwtware.Config{
|
//api := app.Group("/api/v1")
|
||||||
|
api := app.Group("/api/v1", logger.New(), jwtware.New(jwtware.Config{
|
||||||
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
|
||||||
}))
|
}))
|
||||||
api.Use(logger.New())
|
api.Use(logger.New())
|
||||||
handler.SetupComicRoutes(api)
|
handler.SetupComicRoutes(api)
|
||||||
handler.SetupMediaRoutes(api)
|
handler.SetupMediaRoutes(api)
|
||||||
|
|
||||||
|
app.Get("/*", static.New("./static", static.Config{
|
||||||
|
Browse: false,
|
||||||
|
MaxAge: 3600,
|
||||||
|
CacheDuration: 10 * time.Second,
|
||||||
|
NotFoundHandler: func(c fiber.Ctx) error {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||||||
|
"error": "asset not found",
|
||||||
|
"path": c.Path(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}))
|
||||||
// Listen on port 8900
|
// Listen on port 8900
|
||||||
app.Listen(":8500")
|
app.Listen(":8500")
|
||||||
|
|
||||||
|
|||||||
+45
-28
@@ -1,42 +1,59 @@
|
|||||||
module kontor-api-fiber
|
module kontor-api-fiber
|
||||||
|
|
||||||
go 1.24.2
|
go 1.26.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gofiber/contrib/jwt v1.1.2
|
||||||
|
github.com/gofiber/fiber/v3 v3.5.0
|
||||||
|
github.com/gofiber/template/html/v3 v3.0.9
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/stretchr/testify v1.12.1
|
||||||
|
github.com/uptrace/bun v1.2.18
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v1.2.18
|
||||||
|
github.com/uptrace/bun/driver/pgdriver v1.2.18
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.18
|
||||||
|
golang.org/x/crypto v0.57.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
|
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.4 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/fatih/color v1.19.0 // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/go-logr/logr v1.4.4 // indirect
|
||||||
github.com/gofiber/contrib/jwt v1.1.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/gofiber/fiber/v2 v2.52.10 // indirect
|
github.com/gofiber/contrib/v3/jwt v1.2.4 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
github.com/gofiber/fiber/v2 v2.52.15 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/gofiber/schema v1.8.7 // indirect
|
||||||
|
github.com/gofiber/template/v2 v2.1.2 // indirect
|
||||||
|
github.com/gofiber/utils/v2 v2.5.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
github.com/klauspost/compress v1.20.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.1 // indirect
|
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-isatty v0.0.24 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-runewidth v0.0.30 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
github.com/molecule-man/go-brrr v1.1.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/tinylib/msgp v1.6.4 // indirect
|
||||||
github.com/stretchr/testify v1.11.1 // indirect
|
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
github.com/uptrace/bun v1.2.16 // indirect
|
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.2.16 // indirect
|
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.2.16 // indirect
|
|
||||||
github.com/uptrace/bun/extra/bundebug v1.2.16 // indirect
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
github.com/valyala/fasthttp v1.74.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
go.opentelemetry.io/otel v1.46.0 // indirect
|
||||||
golang.org/x/crypto v0.45.0 // indirect
|
go.opentelemetry.io/otel/metric v1.46.0 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
go.opentelemetry.io/otel/trace v1.46.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||||
|
golang.org/x/mod v0.41.0 // indirect
|
||||||
|
golang.org/x/net v0.59.0 // indirect
|
||||||
|
golang.org/x/sync v0.23.0 // indirect
|
||||||
|
golang.org/x/sys v0.48.0 // indirect
|
||||||
|
golang.org/x/text v0.42.0 // indirect
|
||||||
|
golang.org/x/tools v0.49.0 // indirect
|
||||||
mellium.im/sasl v0.3.2 // indirect
|
mellium.im/sasl v0.3.2 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
+102
-56
@@ -1,98 +1,144 @@
|
|||||||
github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k=
|
github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k=
|
||||||
github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k=
|
github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k=
|
||||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
github.com/andybalholm/brotli v1.2.2 h1:HzTuoo2ErYQqf5qvcJInB8uvqSVxRttzkFexPWtnceM=
|
||||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
github.com/andybalholm/brotli v1.2.2/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
github.com/andybalholm/brotli v1.2.4 h1:649LN6qF+/7JJYNN3Wdu4Dt5PS6eC/yP/Go3Bb0/AHI=
|
||||||
|
github.com/andybalholm/brotli v1.2.4/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
|
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||||
|
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.9.3 h1:oQBnFATpNdY8gJHTndDDv5Xl4QqNaz51G5LLEPhng3Q=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.9.3/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/gofiber/contrib/jwt v1.1.2 h1:GmWnOqT4A15EkA8IPXwSpvNUXZR4u5SMj+geBmyLAjs=
|
github.com/gofiber/contrib/jwt v1.1.2 h1:GmWnOqT4A15EkA8IPXwSpvNUXZR4u5SMj+geBmyLAjs=
|
||||||
github.com/gofiber/contrib/jwt v1.1.2/go.mod h1:CpIwrkUQ3Q6IP8y9n3f0wP9bOnSKx39EDp2fBVgMFVk=
|
github.com/gofiber/contrib/jwt v1.1.2/go.mod h1:CpIwrkUQ3Q6IP8y9n3f0wP9bOnSKx39EDp2fBVgMFVk=
|
||||||
github.com/gofiber/fiber v1.14.6 h1:QRUPvPmr8ijQuGo1MgupHBn8E+wW0IKqiOvIZPtV70o=
|
github.com/gofiber/contrib/v3/jwt v1.2.4 h1:0cApE/65Nb+AV49zMjun2+vXE1pCdZWLdg1z3IxmrPI=
|
||||||
github.com/gofiber/fiber v1.14.6/go.mod h1:Yw2ekF1YDPreO9V6TMYjynu94xRxZBdaa8X5HhHsjCM=
|
github.com/gofiber/contrib/v3/jwt v1.2.4/go.mod h1:ARfFxE8uxUeV5hzq9qUaSCBWkpYmGpvAL/XU3NBpkRA=
|
||||||
github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
|
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
||||||
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||||
github.com/gofiber/utils v0.0.10/go.mod h1:9J5aHFUIjq0XfknT4+hdSMG6/jzfaAgCu4HEbWDeBlo=
|
github.com/gofiber/fiber/v2 v2.52.15 h1:Cov1uKeVPyu9q0jSrN60W+A8XNX+/WK8J7cy5osHLIk=
|
||||||
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
github.com/gofiber/fiber/v2 v2.52.15/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||||
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
github.com/gofiber/fiber/v3 v3.5.0 h1:dk7TOUH6DXJGtOLsN2XEG+0ZML7cznzHILTVozbNEK8=
|
||||||
|
github.com/gofiber/fiber/v3 v3.5.0/go.mod h1:GOVDTW+gjJvfe0iJyVujbQ1Lnx+JUjFySJRI/9/xX/w=
|
||||||
|
github.com/gofiber/schema v1.8.3 h1:06ZedxIYjngzc0095PYy7uWnFnbRflWFpikvZH61fDc=
|
||||||
|
github.com/gofiber/schema v1.8.3/go.mod h1:jWnnZdhcW1mHyV+VnfRxKJDPNcepJsTZ9RIWxrr32Ng=
|
||||||
|
github.com/gofiber/schema v1.8.7 h1:7cSagr2ObiUVFuitCOqjKMqIhV+oVbkanP4Ge/wHsnI=
|
||||||
|
github.com/gofiber/schema v1.8.7/go.mod h1:fBz4T6uOepH3L+dYlz/yzQ0bFHPnfExkp/B3Qrk2VBI=
|
||||||
|
github.com/gofiber/template/html/v3 v3.0.9 h1:oVgrQIGSe7DcufnD823KwLsUnIgtCDGhznj1kMfNB2I=
|
||||||
|
github.com/gofiber/template/html/v3 v3.0.9/go.mod h1:Nk2HJlZYaCd0oVEwQd8v+1M+gmweub6dEAeS07WhL6o=
|
||||||
|
github.com/gofiber/template/v2 v2.1.2 h1:fZTpavK0Yi1ehJGrL9UXzBrOnowZfPpIZwxfiIftXBs=
|
||||||
|
github.com/gofiber/template/v2 v2.1.2/go.mod h1:h8vOKQkwFUFRNTvRcIvt/OgVUOVqHsaEviWTOAh2JqQ=
|
||||||
|
github.com/gofiber/utils/v2 v2.5.2 h1:bX+iACKsCGm2+dW7jvkYSiWIsBaKqFy5slFSxJSIL/s=
|
||||||
|
github.com/gofiber/utils/v2 v2.5.2/go.mod h1:EaVDjy4JJv0m0lg6x4owJ1GG8j1pMO8XriqCgVns1ZQ=
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/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/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
|
||||||
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
|
|
||||||
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||||
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
||||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
||||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/mattn/go-runewidth v0.0.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/mattn/go-runewidth v0.0.30/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
|
||||||
|
github.com/molecule-man/go-brrr v1.0.1 h1:cEjgx8hgNw6UGdhQ94SPDbPkKuRbkUcxBO3IzbGpA/o=
|
||||||
|
github.com/molecule-man/go-brrr v1.0.1/go.mod h1:7ybW6/7gA3oKY45jOfVNjSJDtrr6ea4tzbsTkjmQDC4=
|
||||||
|
github.com/molecule-man/go-brrr v1.1.0 h1:rLBGSXA393y+5ttCBLkW34CHESop5tahrOFxVv/XDEU=
|
||||||
|
github.com/molecule-man/go-brrr v1.1.0/go.mod h1:7ybW6/7gA3oKY45jOfVNjSJDtrr6ea4tzbsTkjmQDC4=
|
||||||
|
github.com/molecule-man/go-brrr v1.1.1 h1:KYwusQhjtV3smALnhmafZf4OBrh7vP4p8O47DyUmmuY=
|
||||||
|
github.com/molecule-man/go-brrr v1.1.1/go.mod h1:7ybW6/7gA3oKY45jOfVNjSJDtrr6ea4tzbsTkjmQDC4=
|
||||||
|
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||||
|
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/shamaton/msgpack/v3 v3.2.3 h1:0r0aZtrXDFOPpwWJTEOpFq+Ih/JmMLm0pqLN5mSilrw=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/shamaton/msgpack/v3 v3.2.3/go.mod h1:sgBYvEiyz8JR1NC3yGRoPVME9xXovpnh3l/plW1nfRo=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||||
|
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
|
||||||
|
github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc=
|
github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc=
|
||||||
github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM=
|
github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM=
|
||||||
|
github.com/uptrace/bun v1.2.18 h1:3HnRcMfS6OBPMG1eSOzlbFJ/X/AyMEJb7rMxE6VQvDU=
|
||||||
|
github.com/uptrace/bun v1.2.18/go.mod h1:wNltaKJk4JtOt4SG5I5zmA7v0/Mzjh1+/S906Rayd3Y=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI9/Z5tUv3DE1NVL4=
|
github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI9/Z5tUv3DE1NVL4=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo=
|
github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo=
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v1.2.18 h1:IZ6nM2+OYrL8lkEAy7UkSEZvoa3vluTAUlZfPtlRB2k=
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v1.2.18/go.mod h1:Tqdf4QP1okrGYpXfodXvCOK6Ob1OOTwSaoAzCgBB3IU=
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU=
|
github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU=
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0=
|
github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0=
|
||||||
|
github.com/uptrace/bun/driver/pgdriver v1.2.18 h1:Zojuc83ulApocXomBLEcx1DqCZweREafHCjPfyXo88I=
|
||||||
|
github.com/uptrace/bun/driver/pgdriver v1.2.18/go.mod h1:ZRJcARw93nxbQ5WawTrc5EO+F+GygkcYgDLEnT17CcE=
|
||||||
github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0=
|
github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0=
|
||||||
github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo=
|
github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.18 h1:5cgkqdvhpSHIEONazSytm4RWYFneNtcznaWLt6r8m4M=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.2.18/go.mod h1:M+U9YJVJcmk0RrszCb2Q1oskJiJ0LuC44FxDhZLP1ws=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA=
|
github.com/valyala/fasthttp v1.73.0 h1:ocTOORnBWtJ+P8t/6wAjdkchMzdfHmWx2VD/DPbgZ7s=
|
||||||
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
github.com/valyala/fasthttp v1.73.0/go.mod h1:EtXQDHaR+5P18p8wqDRFpUhxr108Ga9mXvVJXHRrN2k=
|
||||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
github.com/valyala/fasthttp v1.74.0 h1:wMS9fnO2QTALozYx5pId2Vi7ZwU/epUkY8i/KPWCHoU=
|
||||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
github.com/valyala/fasthttp v1.74.0/go.mod h1:3ARmLamUcw7ElxVtC8PXaGzQ6VEuvnetlkrwIklQBSE=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||||
|
go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc=
|
||||||
|
go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE=
|
||||||
|
go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o=
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c=
|
||||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI=
|
||||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
|
||||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
||||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||||
|
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||||
|
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||||
|
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||||
mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
|
mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
|
||||||
mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
|
mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import (
|
|||||||
"kontor-api-fiber/pkg/schema"
|
"kontor-api-fiber/pkg/schema"
|
||||||
"kontor-api-fiber/pkg/utils"
|
"kontor-api-fiber/pkg/utils"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v3"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Login(c *fiber.Ctx) error {
|
func Login(c fiber.Ctx) error {
|
||||||
user := c.FormValue("user")
|
user := c.FormValue("user")
|
||||||
pass := c.FormValue("pass")
|
pass := c.FormValue("pass")
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ func Login(c *fiber.Ctx) error {
|
|||||||
return c.JSON(fiber.Map{"token": token})
|
return c.JSON(fiber.Map{"token": token})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Restricted(c *fiber.Ctx) error {
|
func Restricted(c fiber.Ctx) error {
|
||||||
user := c.Locals("user").(*jwt.Token)
|
user := c.Locals("user").(*jwt.Token)
|
||||||
claims := user.Claims.(jwt.MapClaims)
|
claims := user.Claims.(jwt.MapClaims)
|
||||||
name := claims["name"].(string)
|
name := claims["name"].(string)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"kontor-api-fiber/pkg/schema"
|
"kontor-api-fiber/pkg/schema"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v3"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ func SetupComicRoutes(api fiber.Router) {
|
|||||||
comics.Get("/comicworks", GetAllComicWorks)
|
comics.Get("/comicworks", GetAllComicWorks)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllComics(c *fiber.Ctx) error {
|
func GetAllComics(c fiber.Ctx) error {
|
||||||
var comics []schema.Comic
|
var comics []schema.Comic
|
||||||
var err error
|
var err error
|
||||||
var db *bun.DB
|
var db *bun.DB
|
||||||
@@ -36,7 +36,7 @@ func GetAllComics(c *fiber.Ctx) error {
|
|||||||
return c.JSON(comics)
|
return c.JSON(comics)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllPublishers(c *fiber.Ctx) error {
|
func GetAllPublishers(c fiber.Ctx) error {
|
||||||
var publishers []schema.Publisher
|
var publishers []schema.Publisher
|
||||||
var err error
|
var err error
|
||||||
var db *bun.DB
|
var db *bun.DB
|
||||||
@@ -56,7 +56,7 @@ func GetAllPublishers(c *fiber.Ctx) error {
|
|||||||
return c.JSON(publishers)
|
return c.JSON(publishers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllComicWorks(c *fiber.Ctx) error {
|
func GetAllComicWorks(c fiber.Ctx) error {
|
||||||
var comic_works []schema.ComicWork
|
var comic_works []schema.ComicWork
|
||||||
var err error
|
var err error
|
||||||
var db *bun.DB
|
var db *bun.DB
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import "github.com/gofiber/fiber/v2"
|
import "github.com/gofiber/fiber/v3"
|
||||||
|
|
||||||
func GetHealth(c *fiber.Ctx) error {
|
func GetHealth(c fiber.Ctx) error {
|
||||||
return c.Status(200).JSON(&fiber.Map{
|
return c.Status(200).JSON(&fiber.Map{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v3"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
@@ -19,7 +19,7 @@ func SetupMediaRoutes(api fiber.Router) {
|
|||||||
media.Post("/files", AddFile)
|
media.Post("/files", AddFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllFiles(c *fiber.Ctx) error {
|
func GetAllFiles(c fiber.Ctx) error {
|
||||||
var files []schema.MediaFile
|
var files []schema.MediaFile
|
||||||
var err error
|
var err error
|
||||||
var db *bun.DB
|
var db *bun.DB
|
||||||
@@ -38,7 +38,7 @@ func GetAllFiles(c *fiber.Ctx) error {
|
|||||||
return c.JSON(files)
|
return c.JSON(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddFile(c *fiber.Ctx) error {
|
func AddFile(c fiber.Ctx) error {
|
||||||
var err error
|
var err error
|
||||||
var db *bun.DB
|
var db *bun.DB
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
@@ -24,6 +24,20 @@ type MediaFile struct {
|
|||||||
MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_file_id"`
|
MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_file_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MediaLoFi struct {
|
||||||
|
bun.BaseModel `bun:"table:media_lofi"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
ShouldDownload bool `bun:"should_download"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
FileName string `bun:"file_name"`
|
||||||
|
}
|
||||||
|
|
||||||
type MediaActor struct {
|
type MediaActor struct {
|
||||||
bun.BaseModel `bun:"table:media_actor"`
|
bun.BaseModel `bun:"table:media_actor"`
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import "github.com/gofiber/fiber/v3"
|
||||||
|
|
||||||
|
func Mainpage(c fiber.Ctx) error {
|
||||||
|
return c.Render("index", fiber.Map{
|
||||||
|
"Title": "Kontor Fiber",
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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,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 @@
|
|||||||
|
<h1>{{.Title}}</h1>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de-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="/style/main.css" />
|
||||||
|
<title>{{.Title}}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="header">{{template "partials/header" .}}</div>
|
||||||
|
{{template "partials/navbar" .}}
|
||||||
|
<div class="content">{{embed}}</div>
|
||||||
|
<div class="footer">{{template "partials/footer" .}}</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<h2>Footer</h2>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<h2>Header</h2>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="subnav">
|
||||||
|
<a href="/media/files/">MediaFiles</a>
|
||||||
|
<a href="/media/actors/">MediaActors</a>
|
||||||
|
<a href="/media/videos/">MediaVideos</a>
|
||||||
|
<a href="/media/lofi/">MediaLofi</a>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<div class="topnav">
|
||||||
|
<a href="/">Kontor</a>
|
||||||
|
<a href="/comic/comics">Comics</a>
|
||||||
|
<a href="/media/files">Media</a>
|
||||||
|
<a style="float:right" href="/login/">Login</a></li>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# ---------- Stage 1: Build ----------
|
||||||
|
FROM docker.io/library/golang:1.26-alpine AS builder
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
# Ensure a portable, static-ish binary
|
||||||
|
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
|
||||||
|
# Copy and download dependencies
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
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-gin cmd/kontor-gin/main.go
|
||||||
|
|
||||||
|
# ---------- Stage 2: Final ----------
|
||||||
|
FROM alpine:latest
|
||||||
|
# Set the working directory
|
||||||
|
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-gin /app/kontor-gin
|
||||||
|
COPY --from=builder /app/templates /app/templates
|
||||||
|
COPY --from=builder /app/assets /app/assets
|
||||||
|
ENV GIN_MODE=release
|
||||||
|
# Set the entrypoint command
|
||||||
|
ENTRYPOINT ["/app/kontor-gin"]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
FROM docker.io/library/golang:1.26-alpine
|
||||||
|
# Install runtime dependencies you actually need
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata curl git make musl-dev go
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
# Copy the binary and set ownership
|
||||||
|
COPY ./kontor kontor
|
||||||
|
EXPOSE 8550
|
||||||
|
ENV DB_HOST=postgres DB_USER=kontor DB_PASSWORD=kontor DB_NAME=kontor DB_PORT=5432
|
||||||
|
# Set the entrypoint command
|
||||||
|
ENTRYPOINT ["/app/kontor"]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package assets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed style/* icons/*
|
||||||
|
var StaticFiles embed.FS
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 634 B |
@@ -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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# script/cibuild:
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
source version
|
||||||
|
|
||||||
|
echo " => Baue Image localhost/kontor-gin:$kontorVersion"
|
||||||
|
buildah build -t kontor-gin:$kontorVersion kontor-gin
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "kontor-api-gin/pkg/handler"
|
||||||
|
|
||||||
|
"kontor-api-gin/internal/globals"
|
||||||
|
"kontor-api-gin/internal/schema"
|
||||||
|
"kontor-api-gin/pkg/middleware"
|
||||||
|
"kontor-api-gin/pkg/routes"
|
||||||
|
"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"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
slog.Info("Kontor started")
|
||||||
|
|
||||||
|
viper.SetDefault("db.host", "localhost")
|
||||||
|
viper.SetDefault("db.port", 5432)
|
||||||
|
viper.SetDefault("db.name", "kontor")
|
||||||
|
viper.SetDefault("db.user", "kontor")
|
||||||
|
viper.SetDefault("db.password", "kontor")
|
||||||
|
viper.SetDefault("port", 8550)
|
||||||
|
viper.SetDefault("token_hour_lifespan", 1)
|
||||||
|
viper.SetDefault("api_secret", "secret")
|
||||||
|
|
||||||
|
if err := globals.InitConfig(); err != nil {
|
||||||
|
slog.Info("Failed to initialize config", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
api.Use(middleware.JwtRequired)
|
||||||
|
routes.SetupMediaRoutes(api)
|
||||||
|
routes.SetupComicRoutes(api)
|
||||||
|
|
||||||
|
// Setup Webapp routes
|
||||||
|
routes.AuthRoutes(router)
|
||||||
|
routes.ComicRoutes(router)
|
||||||
|
routes.MediaRoutes(router)
|
||||||
|
routes.PublicRoutes(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")
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"kontor-api-gin/internal/config"
|
||||||
|
"kontor-api-gin/internal/globals"
|
||||||
|
"kontor-api-gin/internal/handlers"
|
||||||
|
"kontor-api-gin/internal/repository"
|
||||||
|
"kontor-api-gin/internal/router"
|
||||||
|
"kontor-api-gin/internal/service"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
slog.Info("Kontor started")
|
||||||
|
|
||||||
|
if err := globals.InitConfig(); err != nil {
|
||||||
|
log.Fatalf("Failed to initialize config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsn := fmt.Sprintf(
|
||||||
|
"host=%s user=%s password=%s dbname=%s port=%d sslmode=disable",
|
||||||
|
viper.GetString("db.host"),
|
||||||
|
viper.GetString("db.user"),
|
||||||
|
viper.GetString("db.password"),
|
||||||
|
viper.GetString("db.name"),
|
||||||
|
viper.GetInt("db.port"),
|
||||||
|
)
|
||||||
|
|
||||||
|
db, err := config.NewDB(dsn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := db.AutoMigrate(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
comicRepo := repository.NewComicRepository(db)
|
||||||
|
profileRepo := repository.NewProfileRepository(db)
|
||||||
|
comicService := service.NewComicService(comicRepo)
|
||||||
|
authService := service.NewAuthService(profileRepo, []byte(viper.GetString("api_secret")))
|
||||||
|
|
||||||
|
// Set Gin mode based on environment
|
||||||
|
// Production mode disables debug output
|
||||||
|
if viper.GetString("gin.mode") == "release" {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := router.New(
|
||||||
|
&handlers.ComicHandler{Service: comicService},
|
||||||
|
&handlers.AuthHandler{Service: authService},
|
||||||
|
[]byte(viper.GetString("api_secret")),
|
||||||
|
)
|
||||||
|
// router.Use(sessions.Sessions("session", cookie.NewStore(globals.Secret)))
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: ":" + strconv.Itoa(viper.GetInt("port")),
|
||||||
|
Handler: r,
|
||||||
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
|
WriteTimeout: 30 * time.Second,
|
||||||
|
IdleTimeout: 120 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
slog.Info("server up", "address", srv.Addr)
|
||||||
|
|
||||||
|
quit := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-quit
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
_ = srv.Shutdown(ctx)
|
||||||
|
slog.Info("server stopped")
|
||||||
|
slog.Info("Kontor finished")
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
module kontor-api-gin
|
||||||
|
|
||||||
|
go 1.26.8
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gin-contrib/cors v1.7.2
|
||||||
|
github.com/gin-contrib/sessions v1.0.2
|
||||||
|
github.com/gin-contrib/slog v1.1.0
|
||||||
|
github.com/gin-gonic/gin v1.10.1
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/spf13/cobra v1.10.2
|
||||||
|
github.com/spf13/viper v1.21.0
|
||||||
|
golang.org/x/crypto v0.57.0
|
||||||
|
golang.org/x/time v0.16.0
|
||||||
|
gorm.io/driver/postgres v1.6.3
|
||||||
|
gorm.io/gorm v1.31.2
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||||
|
github.com/bytedance/sonic v1.15.4 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.5.2 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.7 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
|
||||||
|
github.com/gin-contrib/sse v1.1.2 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.6 // 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/inconshreveable/mousetrap v1.1.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
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
||||||
|
github.com/leodido/go-urn v1.5.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.24 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||||
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/stretchr/testify v1.12.1 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.3.2 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||||
|
golang.org/x/arch v0.31.0 // indirect
|
||||||
|
golang.org/x/net v0.59.0 // indirect
|
||||||
|
golang.org/x/sync v0.23.0 // indirect
|
||||||
|
golang.org/x/sys v0.48.0 // indirect
|
||||||
|
golang.org/x/text v0.42.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.12 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
resty.dev/v3 v3.0.0-rc.4 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||||
|
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||||
|
github.com/bytedance/sonic v1.15.4 h1:FgtV/4aBHpla9AxuMpuuzVUpa/Cf3izufkxNmnEzdI8=
|
||||||
|
github.com/bytedance/sonic v1.15.4/go.mod h1:8e51yTPdY8M6t+vvGL1c2Y1xL9i+frEeIAQAEl75NUc=
|
||||||
|
github.com/bytedance/sonic/loader v0.5.2 h1:0QtP1gevc1OZ6/H8Lb9BRZiCXd1Ftjd3OKuj1T1lBIo=
|
||||||
|
github.com/bytedance/sonic/loader v0.5.2/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||||
|
github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI=
|
||||||
|
github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||||
|
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||||
|
github.com/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/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||||
|
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
||||||
|
github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA=
|
||||||
|
github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs=
|
||||||
|
github.com/gin-contrib/slog v1.1.0 h1:K9MVNrETT6r/C3u2Aheer/gxwVeVqrGL0hXlsmv3fm4=
|
||||||
|
github.com/gin-contrib/slog v1.1.0/go.mod h1:PvNXQVXcVOAaaiJR84LV1/xlQHIaXi9ygEXyBkmjdkY=
|
||||||
|
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.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||||
|
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
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=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.11.0 h1:IzBBtyK9AHqf98cctWFifYSci2hgQR/cd56wB4p+ogg=
|
||||||
|
github.com/jackc/pgx/v5 v5.11.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0=
|
||||||
|
github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE=
|
||||||
|
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||||
|
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||||
|
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||||
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||||
|
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||||
|
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.3.2 h1:zkEASHHyEClGeURfgNT9PJZVfAbs9oEX9QXggwWNJbc=
|
||||||
|
github.com/ugorji/go/codec v1.3.2/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||||
|
golang.org/x/arch v0.31.0 h1:22MlEb14/O/EPCYHFxsDdv5TuLD5dMjT5e2QeJw4ULk=
|
||||||
|
golang.org/x/arch v0.31.0/go.mod h1:KcJSod3cqT2dKcjBxqTyGfbumNikqU9p5tHJinPJnuY=
|
||||||
|
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||||
|
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||||
|
golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
|
||||||
|
golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
||||||
|
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||||
|
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||||
|
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||||
|
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||||
|
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||||
|
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||||
|
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
|
||||||
|
golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
|
||||||
|
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||||
|
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/driver/postgres v1.6.3 h1:bAn6O2pUa8LtpWEvL5NFU4+52Tfx8Ut7IVaIacCLcI0=
|
||||||
|
gorm.io/driver/postgres v1.6.3/go.mod h1:0c4fQA44XhOklXDkgtuKqysHCycTa5i9e3EIpDGCwXk=
|
||||||
|
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||||
|
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||||
|
gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo=
|
||||||
|
gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||||
|
resty.dev/v3 v3.0.0-rc.4 h1:n3360yEcFiyrX5Vkc9XG3Y8kMM24alRBxJ9Ev1mLmqs=
|
||||||
|
resty.dev/v3 v3.0.0-rc.4/go.mod h1:NTOerrC/4T7/FE6tXIZGIysXXBdgNqwMZuKtxpea9NM=
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var addLinksCmd = &cobra.Command{
|
||||||
|
Use: "add-links",
|
||||||
|
Short: "Add Links",
|
||||||
|
Long: "Add Links",
|
||||||
|
// Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Info("add Links")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(addLinksCmd)
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"resty.dev/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Login struct {
|
||||||
|
Email string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginResponse struct {
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
var checkCmd = &cobra.Command{
|
||||||
|
Use: "check",
|
||||||
|
Short: "Check Kontor Database",
|
||||||
|
Long: "Check Kontor Database to given file",
|
||||||
|
// Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Info("check kontor")
|
||||||
|
slog.Info("Verbosity level", "level", verbose)
|
||||||
|
var login LoginResponse
|
||||||
|
client := resty.New()
|
||||||
|
res, err := client.R().
|
||||||
|
SetBody(Login{
|
||||||
|
Email: "thomas.peetz@thpeetz.de",
|
||||||
|
Password: "admin",
|
||||||
|
}).
|
||||||
|
SetHeader("Accept", "application/json").
|
||||||
|
SetResult(&login).
|
||||||
|
Post("http://127.0.0.1:8550/api/login")
|
||||||
|
if err != nil {
|
||||||
|
slog.Info("Health", "error", err.Error())
|
||||||
|
}
|
||||||
|
slog.Info("health", "token", login.Token)
|
||||||
|
res, err = client.R().
|
||||||
|
SetAuthToken(login.Token).
|
||||||
|
SetHeader("Accept", "application/json").
|
||||||
|
Get("http://127.0.0.1:8550/api/v1/comics/comics")
|
||||||
|
if err != nil {
|
||||||
|
slog.Info("comics", "error", err.Error())
|
||||||
|
}
|
||||||
|
slog.Info("comics", "res", res)
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
slog.Info("kontor.check_kontor.check_duplicate_links")
|
||||||
|
// check_duplicate_links(first_server, dirs)
|
||||||
|
slog.Info("kontor.check_kontor.check_media_dirs")
|
||||||
|
// check_media_dirs(first_server, dirs)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(checkCmd)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var exportCmd = &cobra.Command{
|
||||||
|
Use: "export",
|
||||||
|
Short: "Export Kontor Database",
|
||||||
|
Long: "Export Kontor Database to given file",
|
||||||
|
// Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Info("export kontor")
|
||||||
|
slog.Info("Verbosity level", "level", verbose)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(exportCmd)
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var importCmd = &cobra.Command{
|
||||||
|
Use: "import",
|
||||||
|
Short: "Import Kontor Database",
|
||||||
|
Long: "Import Kontor Database from given file",
|
||||||
|
// Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Info("import kontor")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(importCmd)
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cfgFile string
|
||||||
|
var verbose int
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "kontor-cli",
|
||||||
|
Short: "A brief description of your application",
|
||||||
|
Long: `A longer description that spans multiple lines and likely contains
|
||||||
|
examples and usage of using your application. For example:
|
||||||
|
|
||||||
|
Cobra is a CLI library for Go that empowers applications.
|
||||||
|
This application is a tool to generate the needed files
|
||||||
|
to quickly create a Cobra application.`,
|
||||||
|
// Uncomment the following line if your bare application
|
||||||
|
// has an action associated with it:
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
cmd.Help()
|
||||||
|
slog.Info("Verbosity level", "level", verbose)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Oops. An error while executing Zero '%s'\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.myapp.yaml)")
|
||||||
|
rootCmd.PersistentFlags().CountVarP(&verbose, "verbose", "v", "increase verbosity (-v, -vv, -vvv)")
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var syncCmd = &cobra.Command{
|
||||||
|
Use: "sync",
|
||||||
|
Short: "Sync Kontor Database",
|
||||||
|
Long: "Sync Kontor Database",
|
||||||
|
// Args: cobra.ExactArgs(2),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
slog.Info("sync kontor")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(syncCmd)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitConfig() error {
|
||||||
|
viper.SetConfigName("kontor-gin")
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
viper.AddConfigPath("$HOME/.config")
|
||||||
|
viper.AddConfigPath("/etc/kontor")
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
|
// Read the config file
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
// Handle the case where config file is not found
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
slog.Info("No config file found, using defaults")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error reading config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("initConfig", "Using config file: ", viper.ConfigFileUsed())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDB(dsn string) (*gorm.DB, error) {
|
||||||
|
// Configure GORM with custom settings
|
||||||
|
// The logger helps with debugging SQL queries during development
|
||||||
|
return gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
})
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("Failed to connect to database:", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Get the underlying SQL database connection for pool configuration
|
||||||
|
// sqlDB, err := db.DB()
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("Failed to get database instance:", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Configure connection pool settings
|
||||||
|
// // MaxIdleConns: number of connections kept open when idle
|
||||||
|
// sqlDB.SetMaxIdleConns(10)
|
||||||
|
// // MaxOpenConns: maximum total connections to the database
|
||||||
|
// sqlDB.SetMaxOpenConns(100)
|
||||||
|
// // ConnMaxLifetime: how long a connection can be reused
|
||||||
|
// sqlDB.SetConnMaxLifetime(time.Hour)
|
||||||
|
|
||||||
|
// slog.Info("Database connected successfully")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package globals
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitConfig() error {
|
||||||
|
viper.SetDefault("db.host", "localhost")
|
||||||
|
viper.SetDefault("db.port", 5432)
|
||||||
|
viper.SetDefault("db.name", "kontor")
|
||||||
|
viper.SetDefault("db.user", "kontor")
|
||||||
|
viper.SetDefault("db.password", "kontor")
|
||||||
|
viper.SetDefault("port", 8550)
|
||||||
|
viper.SetDefault("gin.mode", "release")
|
||||||
|
|
||||||
|
viper.SetConfigName("kontor-gin")
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
viper.AddConfigPath("$HOME/.config")
|
||||||
|
viper.AddConfigPath("/etc/kontor")
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
|
// Read the config file
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
// Handle the case where config file is not found
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
slog.Info("No config file found, using defaults")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error reading config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("initConfig", "Using config file: ", viper.ConfigFileUsed())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package globals
|
||||||
|
|
||||||
|
var Secret = []byte("secret")
|
||||||
|
|
||||||
|
const UserKey = "user"
|
||||||
|
|
||||||
|
const Version = "0.4.0"
|
||||||
|
|
||||||
|
const LoginUrl = "/login"
|
||||||
|
|
||||||
|
const LogoutUrl = "/auth/logout"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/helpers"
|
||||||
|
"kontor-api-gin/internal/service"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthHandler struct {
|
||||||
|
Service *service.AuthService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *AuthHandler) Login(c *gin.Context) {
|
||||||
|
token, err := helpers.GenerateToken("bla")
|
||||||
|
logger := slog.Get(c)
|
||||||
|
if err != nil {
|
||||||
|
logger.Info("Login", "GenerateToken error", err.Error())
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{"token": token})
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/service"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ComicHandler struct {
|
||||||
|
Service *service.ComicService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *ComicHandler) List(c *gin.Context) {
|
||||||
|
comics, err := handler.Service.List(c.Request.Context())
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, comics)
|
||||||
|
}
|
||||||
@@ -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,90 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateToken(profile_id string) (string, error) {
|
||||||
|
|
||||||
|
// token_lifespan := viper.GetInt("token_hour_lifespan")
|
||||||
|
|
||||||
|
claims := jwt.MapClaims{
|
||||||
|
"name": "Thomas Peetz",
|
||||||
|
"admin": true,
|
||||||
|
"authorized": true,
|
||||||
|
"sub": profile_id,
|
||||||
|
"exp": time.Now().Add(time.Hour * 72).Unix(),
|
||||||
|
}
|
||||||
|
// claims["authorized"] = true
|
||||||
|
// claims["profile_id"] = profile_id
|
||||||
|
// claims["exp"] = time.Now().Add(time.Hour * time.Duration(token_lifespan)).Unix()
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|
||||||
|
return token.SignedString([]byte(viper.GetString("api_secret")))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TokenValid(c *gin.Context) error {
|
||||||
|
logger := slog.Get(c)
|
||||||
|
|
||||||
|
tokenString := ExtractToken(c)
|
||||||
|
_, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
logger.Info("TokenValid", "Unexpected signing method", token.Header["alg"])
|
||||||
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
return []byte(viper.GetString("api_secret")), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractToken(c *gin.Context) string {
|
||||||
|
logger := slog.Get(c)
|
||||||
|
|
||||||
|
token := c.Query("token")
|
||||||
|
if token != "" {
|
||||||
|
logger.Info("ExtractToken", "token from query", token)
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
bearerToken := c.Request.Header.Get("Authorization")
|
||||||
|
if len(strings.Split(bearerToken, " ")) == 2 {
|
||||||
|
logger.Info("ExtractToken", "token from query", token)
|
||||||
|
|
||||||
|
return strings.Split(bearerToken, " ")[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractTokenID(c *gin.Context) (string, error) {
|
||||||
|
|
||||||
|
tokenString := ExtractToken(c)
|
||||||
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
return []byte(viper.GetString("api_secret")), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
claims, ok := token.Claims.(jwt.MapClaims)
|
||||||
|
if ok && token.Valid {
|
||||||
|
uid, ok := claims["profile_id"].(string)
|
||||||
|
if !ok {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return uid, nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func JWT(secret []byte) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
h := c.GetHeader("Authorization")
|
||||||
|
if !strings.HasPrefix(h, "Bearer ") {
|
||||||
|
c.AbortWithStatusJSON(http.StatusUnauthorized,
|
||||||
|
gin.H{"error": "missing bearer token"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw := strings.TrimPrefix(h, "Bearer ")
|
||||||
|
token, err := jwt.Parse(raw, func(t *jwt.Token) (any, error) {
|
||||||
|
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, jwt.ErrSignatureInvalid
|
||||||
|
}
|
||||||
|
return secret, nil
|
||||||
|
})
|
||||||
|
if err != nil || !token.Valid {
|
||||||
|
c.AbortWithStatusJSON(http.StatusUnauthorized,
|
||||||
|
gin.H{"error": "invalid token"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
claims := token.Claims.(jwt.MapClaims)
|
||||||
|
c.Set("profile_id", claims["sub"].(string))
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RateLimit(rps int, burst int) gin.HandlerFunc {
|
||||||
|
limiter := rate.NewLimiter(rate.Limit(rps), burst)
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
if !limiter.Allow() {
|
||||||
|
c.AbortWithStatusJSON(429, gin.H{"error": "too many requests"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RequestID() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
id := c.GetHeader("X-Request-ID")
|
||||||
|
if id == "" {
|
||||||
|
id = uuid.NewString()
|
||||||
|
}
|
||||||
|
c.Set("request_id", id)
|
||||||
|
c.Writer.Header().Set("X-Request-ID", id)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Artist struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Name string `gorm:"uniqueIndex" json:"name"`
|
||||||
|
WebLink string `gorm:"column:weblink" json:"weblink"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (artist *Artist) TableName() string {
|
||||||
|
return "artist"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateArtistDTO struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Comic struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Title string `gorm:"uniqueIndex" json:"title"`
|
||||||
|
CurrentOrder bool `gorm:"column:current_order" json:"current_order"`
|
||||||
|
Completed bool `gorm:"column:completed" json:"completed"`
|
||||||
|
WebLink string `gorm:"column:weblink" json:"weblink"`
|
||||||
|
PublisherID *string `gorm:"column:publisher_id" json:"publisher_id"`
|
||||||
|
Issues []Issue `gorm:"foreignKey:ComicID"`
|
||||||
|
StoryArcs []StoryArc `gorm:"foreignKey:ComicID"`
|
||||||
|
TradePaperbacks []TradePaperback `gorm:"foreignKey:ComicID"`
|
||||||
|
Volumes []Volume `gorm:"foreignKey:ComicID"`
|
||||||
|
ComicWorks []ComicWork `gorm:"foreignKey:ComicID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (comic *Comic) TableName() string {
|
||||||
|
return "comic"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateComicDTO struct {
|
||||||
|
Title string `json:"title" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateComicDTO struct {
|
||||||
|
Title *string `json:"title,omitempty" binding:"omitempty"`
|
||||||
|
WebLink *string `json:"weblink,omitempty" binding:"omitempty"`
|
||||||
|
Completed *bool `json:"cimpleted,omitempty" binding:"omitempty"`
|
||||||
|
CurrentOrder *bool `json:"current_order,omitempty" binding:"omitempty"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ComicWork struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
ArtistID *string `gorm:"column:artist_id" json:"artist_id"`
|
||||||
|
Artist *Artist `gorm:"foreignKey:ArtistID"`
|
||||||
|
ComicID *string `gorm:"column:comic_id" json:"comic_id"`
|
||||||
|
Comic *Comic `gorm:"foreignKey:ComicID"`
|
||||||
|
WorkTypeID *string `gorm:"column:work_type_id" json:"work_type_id"`
|
||||||
|
WorkType *WorkType `gorm:"foreignKey:WorkTypeID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (comic_work *ComicWork) TableName() string {
|
||||||
|
return "comic_work"
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Issue struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
InStock bool `gorm:"column:in_stock" json:"in_stock"`
|
||||||
|
IsRead bool `gorm:"column:is_read" json:"is_read"`
|
||||||
|
IssueNumber string `gorm:"column:issue_number" json:"issue_number"`
|
||||||
|
Title string `gorm:"column:title" json:"title"`
|
||||||
|
PublishedOn time.Time `gorm:"column:published_on" json:"published_on"`
|
||||||
|
ComicID *string `gorm:"column:comic_id" json:"comic_id"`
|
||||||
|
Comic *Comic `gorm:"foreignKey:ComicID"`
|
||||||
|
StoryArcID *string `gorm:"column:story_arc_id" json:"story_arc_id"`
|
||||||
|
StoryArc *StoryArc `gorm:"foreignKey:StoryArcID"`
|
||||||
|
VolumeID *string `gorm:"column:volume_id" json:"volume_id"`
|
||||||
|
Volume *Volume `gorm:"foreignKey:VolumeID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (issue *Issue) TableName() string {
|
||||||
|
return "issue"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type IssueWork struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
ArtistID *string `gorm:"column:artist_id" json:"artist_id"`
|
||||||
|
Artist *Artist `gorm:"foreignKey:ArtistID"`
|
||||||
|
IssueID *string `gorm:"column:issue_id" json:"issue_id"`
|
||||||
|
Issue *Issue `gorm:"foreignKey:IssueID"`
|
||||||
|
WorkTypeID *string `gorm:"column:work_type_id" json:"work_type_id"`
|
||||||
|
WorkType *WorkType `gorm:"foreignKey:WorkTypeID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (issue_work *IssueWork) TableName() string {
|
||||||
|
return "issue_work"
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Publisher struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Name string `gorm:"uniqueIndex" json:"name"`
|
||||||
|
WebLink string `gorm:"column:weblink" json:"weblink"`
|
||||||
|
ParentPublisherID *string `gorm:"column:parent_publisher_id" json:"parent_publisher_id"`
|
||||||
|
ParentPublisher *Publisher `gorm:"foreignKey:ParentPublisherID"`
|
||||||
|
Imprints []Publisher `gorm:"foreignKey:ParentPublisherID"`
|
||||||
|
Comics []Comic `gorm:"foreignKey:PublisherID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (publisher *Publisher) TableName() string {
|
||||||
|
return "publisher"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreatePublisherDTO struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
WebLink string `json:"weblink" binding:"required"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type StoryArc struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Name string `gorm:"uniqueIndex" json:"name"`
|
||||||
|
ComicID *string `gorm:"column:comic_id" json:"comic_id"`
|
||||||
|
Comic *Comic `gorm:"foreignKey:ComicID"`
|
||||||
|
VolumeID *string `gorm:"column:volume_id" json:"volume_id"`
|
||||||
|
Volume *Volume `gorm:"foreignKey:VolumeID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (story_arc *StoryArc) TableName() string {
|
||||||
|
return "story_arc"
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type TradePaperback struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Name string `gorm:"uniqueIndex" json:"name"`
|
||||||
|
IssueStart int `gorm:"column:issue_start" json:"issue_start"`
|
||||||
|
IssueEnd int `gorm:"column:issue_end" json:"issue_end"`
|
||||||
|
ComicID *string `gorm:"column:comic_id" json:"comic_id"`
|
||||||
|
Comic *Comic `gorm:"foreignKey:ComicID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (trade_paperback *TradePaperback) TableName() string {
|
||||||
|
return "trade_paperback"
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Volume struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Name string `gorm:"uniqueIndex" json:"name"`
|
||||||
|
ComicID *string `gorm:"column:comic_id" json:"comic_id"`
|
||||||
|
Comic *Comic `gorm:"foreignKey:ComicID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (volume *Volume) TableName() string {
|
||||||
|
return "volume"
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package comics
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type WorkType struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `gorm:"column:created_date" json:"created_date"`
|
||||||
|
UpdatedAt time.Time `gorm:"column:last_modified_date" json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_date" json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Name string `gorm:"uniqueIndex" json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (worktype *WorkType) TableName() string {
|
||||||
|
return "worktype"
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"kontor-api-gin/internal/models/comics"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ComicRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewComicRepository(db *gorm.DB) *ComicRepository {
|
||||||
|
repo := &ComicRepository{db: db}
|
||||||
|
return repo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *ComicRepository) List(context context.Context) ([]comics.Comic, error) {
|
||||||
|
var comics []comics.Comic
|
||||||
|
|
||||||
|
result := repository.db.Find(&comics)
|
||||||
|
slog.Info("ComicRepository#List", "count", result.RowsAffected)
|
||||||
|
|
||||||
|
return comics, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProfileRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProfileRepository(db *gorm.DB) *ProfileRepository {
|
||||||
|
repository := &ProfileRepository{db: db}
|
||||||
|
return repository
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"kontor-api-gin/assets"
|
||||||
|
"kontor-api-gin/internal/handlers"
|
||||||
|
"kontor-api-gin/internal/middleware"
|
||||||
|
"kontor-api-gin/templates"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/cors"
|
||||||
|
gin_slog "github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(comics *handlers.ComicHandler, auth *handlers.AuthHandler, secret []byte) *gin.Engine {
|
||||||
|
router := gin.New()
|
||||||
|
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.Use(cors.New(cors.Config{
|
||||||
|
AllowOrigins: []string{"https://app.example.com"},
|
||||||
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
|
||||||
|
AllowHeaders: []string{"Authorization", "Content-Type"},
|
||||||
|
}))
|
||||||
|
templ := template.Must(template.New("").ParseFS(templates.TemplateFiles, "layouts/*.html", "pages/*.html"))
|
||||||
|
router.SetHTMLTemplate(templ)
|
||||||
|
|
||||||
|
router.StaticFS("/public", http.FS(assets.StaticFiles))
|
||||||
|
|
||||||
|
router.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"ok": true}) })
|
||||||
|
|
||||||
|
api := router.Group("/api")
|
||||||
|
api.POST("/login", auth.Login)
|
||||||
|
|
||||||
|
v1 := api.Group("/v1")
|
||||||
|
{
|
||||||
|
secured := v1.Group("")
|
||||||
|
secured.Use(middleware.JWT(secret))
|
||||||
|
secured.GET("comics/comics", comics.List)
|
||||||
|
}
|
||||||
|
return router
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import "kontor-api-gin/internal/repository"
|
||||||
|
|
||||||
|
type AuthService struct {
|
||||||
|
repository *repository.ProfileRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthService(repository *repository.ProfileRepository, secret []byte) *AuthService {
|
||||||
|
service := &AuthService{repository: repository}
|
||||||
|
return service
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"kontor-api-gin/internal/models/comics"
|
||||||
|
"kontor-api-gin/internal/repository"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ComicService struct {
|
||||||
|
repository *repository.ComicRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewComicService(repo *repository.ComicRepository) *ComicService {
|
||||||
|
service := &ComicService{repository: repo}
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *ComicService) List(context context.Context) ([]comics.Comic, error) {
|
||||||
|
slog.Info("ComicService#List")
|
||||||
|
return service.repository.List(context)
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"kontor-api-gin/pkg/helpers"
|
||||||
|
"kontor-api-gin/pkg/schema"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/slog"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginInput struct {
|
||||||
|
Email string `json:"email" binding:"required"`
|
||||||
|
Password string `json:"password" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoginHandler(c *gin.Context) {
|
||||||
|
var input LoginInput
|
||||||
|
|
||||||
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
profile := schema.Profile{}
|
||||||
|
|
||||||
|
profile.Email = input.Email
|
||||||
|
profile.Password = input.Password
|
||||||
|
|
||||||
|
token, err := schema.LoginCheck(profile.Email, profile.Password)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "username or password is incorrect."})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"token": token})
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoginGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
logger.Info("LoginGetHandler", globals.UserKey, user)
|
||||||
|
if user != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "login", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"message": "Please logout first",
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(http.StatusOK, "login", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"message": "",
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoginPostHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
logger.Info("LoginPostHandler", "user", user)
|
||||||
|
if user != nil {
|
||||||
|
c.HTML(http.StatusBadRequest, "login", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"message": "Please logout first",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.PostForm("username")
|
||||||
|
password := c.PostForm("password")
|
||||||
|
|
||||||
|
if helpers.EmptyUserPass(username, password) {
|
||||||
|
c.HTML(http.StatusBadRequest, "login", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"message": "Parameters can't be empty",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if !helpers.CheckUserPass(username, password) {
|
||||||
|
c.HTML(http.StatusUnauthorized, "login", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"message": "Incorrect username or password",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Set(globals.UserKey, username)
|
||||||
|
if err := session.Save(); err != nil {
|
||||||
|
c.HTML(http.StatusInternalServerError, "login", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"message": "Failed to save session",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("LoginGetHandler: redirect to index")
|
||||||
|
|
||||||
|
c.Redirect(http.StatusMovedPermanently, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogoutGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
logger.Info("LogoutGetHandler", globals.UserKey, user)
|
||||||
|
if user == nil {
|
||||||
|
logger.Info("Invalid session token")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
session.Delete(globals.UserKey)
|
||||||
|
if err := session.Save(); err != nil {
|
||||||
|
logger.Info("Failed to save session", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("LogoutGetHandler: redirect to index page")
|
||||||
|
c.Redirect(http.StatusMovedPermanently, "/")
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"kontor-api-gin/pkg/schema"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListComicsGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
|
||||||
|
var comics []schema.Comic
|
||||||
|
|
||||||
|
result := schema.DB.Find(&comics)
|
||||||
|
logger.Info("ListComicsGetHandler", "count", result.RowsAffected)
|
||||||
|
c.HTML(http.StatusOK, "comics", gin.H{
|
||||||
|
"title": "Kontor Comics",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"message": "Comics...",
|
||||||
|
"comics": comics,
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/schema"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListComics(c *gin.Context) {
|
||||||
|
var comics []schema.Comic
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
// query := schema.DB.Model(&schema.MediaFile{})
|
||||||
|
// result := query.Find(&files)
|
||||||
|
result := schema.DB.Find(&comics)
|
||||||
|
logger.Info("found Comics", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, comics)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListComicPublishers(c *gin.Context) {
|
||||||
|
var publishers []schema.Publisher
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
result := schema.DB.Find(&publishers)
|
||||||
|
logger.Info("found Comic Publishers", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, publishers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListComicArtists(c *gin.Context) {
|
||||||
|
var artists []schema.Artist
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
result := schema.DB.Find(&artists)
|
||||||
|
logger.Info("found Comic Artists", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, artists)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListComicIssues(c *gin.Context) {
|
||||||
|
var issues []schema.Issue
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
result := schema.DB.Find(&issues)
|
||||||
|
logger.Info("found Comic Issues", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, issues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListComicWorkTypes(c *gin.Context) {
|
||||||
|
var worktypes []schema.WorkType
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
result := schema.DB.Find(&worktypes)
|
||||||
|
logger.Info("found Comic WorkTypes", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, worktypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListComicVolumes(c *gin.Context) {
|
||||||
|
var items []schema.Volume
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
result := schema.DB.Find(&items)
|
||||||
|
logger.Info("found Comic Volumes", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListComicStoryArcs(c *gin.Context) {
|
||||||
|
var items []schema.StoryArc
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
result := schema.DB.Find(&items)
|
||||||
|
logger.Info("ListComicStoryArcs", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListComicWorks(c *gin.Context) {
|
||||||
|
var items []schema.ComicWork
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
result := schema.DB.Find(&items)
|
||||||
|
logger.Info("found Comic ComicWorks", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListComicIssueWorks(c *gin.Context) {
|
||||||
|
var items []schema.IssueWork
|
||||||
|
|
||||||
|
logger := slog.Get(c)
|
||||||
|
result := schema.DB.Find(&items)
|
||||||
|
logger.Info("found Comic IssueWorks", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, items)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IndexGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
logger.Info("IndexGetHandler", globals.UserKey, user)
|
||||||
|
c.HTML(http.StatusOK, "home", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"message": "This is an index page...",
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"logout": globals.LogoutUrl,
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HealthGetHandler(c *gin.Context) {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"status": "healthy",
|
||||||
|
"version": globals.Version,
|
||||||
|
"time": time.Now().UTC().Format(time.RFC3339),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/globals"
|
||||||
|
"kontor-api-gin/pkg/schema"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListMediaFiles(c *gin.Context) {
|
||||||
|
var files []schema.MediaFile
|
||||||
|
logger := slog.Get(c)
|
||||||
|
|
||||||
|
// query := schema.DB.Model(&schema.MediaFile{})
|
||||||
|
// result := query.Find(&files)
|
||||||
|
result := schema.DB.Find(&files)
|
||||||
|
logger.Info("ListMediaFiles", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListMediaLoFis(c *gin.Context) {
|
||||||
|
var lofis []schema.MediaLoFi
|
||||||
|
logger := slog.Get(c)
|
||||||
|
|
||||||
|
result := schema.DB.Find(&lofis)
|
||||||
|
logger.Info("ListMediaLoFis", "count", result.RowsAffected)
|
||||||
|
c.JSON(http.StatusOK, 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)
|
||||||
|
logger.Info("ListMediaActorsGetHandler", "count", result.RowsAffected)
|
||||||
|
c.HTML(http.StatusOK, "mediaactors", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"message": "Media Actors...",
|
||||||
|
"actors": actors,
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"logout": globals.LogoutUrl,
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListMediaFilesGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
|
||||||
|
var files []schema.MediaFile
|
||||||
|
|
||||||
|
result := schema.DB.Find(&files)
|
||||||
|
logger.Info("ListMediaFilesGetHandler", "count", result.RowsAffected)
|
||||||
|
c.HTML(http.StatusOK, "mediafiles", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"message": "Media Files...",
|
||||||
|
"files": files,
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"logout": globals.LogoutUrl,
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListMediaLoFisGetHandler(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
|
||||||
|
var lofis []schema.MediaLoFi
|
||||||
|
|
||||||
|
result := schema.DB.Find(&lofis)
|
||||||
|
logger.Info("ListMediaLoFisGetHandler", "count", result.RowsAffected)
|
||||||
|
logger.Info("ListMediaLoFisGetHandler", "medialofi", lofis[0])
|
||||||
|
c.HTML(http.StatusOK, "lofi", gin.H{
|
||||||
|
"title": "Kontor Gin",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"message": "Media LoFis...",
|
||||||
|
"lofis": lofis,
|
||||||
|
"login": globals.LoginUrl,
|
||||||
|
"logout": globals.LogoutUrl,
|
||||||
|
"user": user,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// corsMiddleware adds CORS headers for cross-origin requests
|
||||||
|
func CorsMiddleware(c *gin.Context) {
|
||||||
|
c.Header("Access-Control-Allow-Origin", "*")
|
||||||
|
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
|
||||||
|
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||||
|
c.Header("Access-Control-Allow-Credentials", "true")
|
||||||
|
|
||||||
|
// Handle preflight requests
|
||||||
|
if c.Request.Method == "OPTIONS" {
|
||||||
|
c.AbortWithStatus(http.StatusNoContent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/globals"
|
||||||
|
"kontor-api-gin/internal/helpers"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/slog"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AuthRequired(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(globals.UserKey)
|
||||||
|
logger := slog.Get(c)
|
||||||
|
logger.Info("AuthRequired", globals.UserKey, user)
|
||||||
|
if user == nil {
|
||||||
|
logger.Info("User not logged in")
|
||||||
|
c.Redirect(http.StatusMovedPermanently, globals.LoginUrl)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func JwtRequired(c *gin.Context) {
|
||||||
|
logger := slog.Get(c)
|
||||||
|
err := helpers.TokenValid(c)
|
||||||
|
logger.Info("JwtRequired", "TokenValid", err)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusUnauthorized, "Unauthorized")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/controllers"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupApiRoutes(router *gin.Engine) *gin.RouterGroup {
|
||||||
|
api := router.Group("/api")
|
||||||
|
api.POST("/login", controllers.LoginHandler)
|
||||||
|
v1 := api.Group("/v1")
|
||||||
|
return v1
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/controllers"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupComicRoutes(router *gin.RouterGroup) {
|
||||||
|
comic := router.Group("/comics")
|
||||||
|
|
||||||
|
comics := comic.Group("/comics")
|
||||||
|
comics.GET("", controllers.ListComics)
|
||||||
|
publishers := comic.Group("/publishers")
|
||||||
|
publishers.GET("", controllers.ListComicPublishers)
|
||||||
|
artists := comic.Group("/artists")
|
||||||
|
artists.GET("/", controllers.ListComicArtists)
|
||||||
|
issues := comic.Group("/issues")
|
||||||
|
issues.GET("", controllers.ListComicIssues)
|
||||||
|
worktypes := comic.Group("/worktypes")
|
||||||
|
worktypes.GET("", controllers.ListComicWorkTypes)
|
||||||
|
volumes := comic.Group("/volumes")
|
||||||
|
volumes.GET("", controllers.ListComicVolumes)
|
||||||
|
storyarcs := comic.Group("/storyarcs")
|
||||||
|
storyarcs.GET("", controllers.ListComicStoryArcs)
|
||||||
|
comicworks := comic.Group("/comicworks")
|
||||||
|
comicworks.GET("", controllers.ListComicWorks)
|
||||||
|
issueworks := comic.Group("/issueworks")
|
||||||
|
issueworks.GET("", controllers.ListComicIssueWorks)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/pkg/controllers"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupMediaRoutes(router *gin.RouterGroup) {
|
||||||
|
media := router.Group("/media")
|
||||||
|
|
||||||
|
files := media.Group("/files")
|
||||||
|
files.GET("", controllers.ListMediaFiles)
|
||||||
|
|
||||||
|
lofi := media.Group("/lofi")
|
||||||
|
lofi.GET("/", controllers.ListMediaLoFis)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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.Engine) {
|
||||||
|
g.GET("/health", controllers.HealthGetHandler)
|
||||||
|
g.GET(globals.LoginUrl, controllers.LoginGetHandler)
|
||||||
|
g.POST(globals.LoginUrl, controllers.LoginPostHandler)
|
||||||
|
g.GET("/", controllers.IndexGetHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 AuthRoutes(g *gin.Engine) {
|
||||||
|
auth := g.Group("/auth")
|
||||||
|
auth.Use(middleware.AuthRequired)
|
||||||
|
auth.GET("/logout", controllers.LogoutGetHandler)
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"kontor-api-gin/internal/helpers"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func VerifyPassword(password, hashedPassword string) error {
|
||||||
|
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoginCheck(email string, password string) (string, error) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
profile := Profile{}
|
||||||
|
|
||||||
|
err = DB.Model(Profile{}).Where("email = ?", email).Take(&profile).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = VerifyPassword(password, profile.Password)
|
||||||
|
|
||||||
|
if err != nil && err == bcrypt.ErrMismatchedHashAndPassword {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := helpers.GenerateToken(profile.ID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
First_Name *string `json:"first_name"`
|
||||||
|
Last_Name *string `json:"last_name"`
|
||||||
|
User_Name string `json:"user_name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
|
||||||
|
Assignments []Assignment `gorm:"foreignKey:ProfileID" json:"assignments"`
|
||||||
|
Tokens []Token `gorm:"foreignKey:ProfileID" json:"tokens"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (profile *Profile) TableName() string {
|
||||||
|
return "profile"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (profile *Profile) GetFullName() string {
|
||||||
|
full_name := ""
|
||||||
|
if profile.First_Name != nil {
|
||||||
|
full_name += *profile.First_Name
|
||||||
|
}
|
||||||
|
if profile.Last_Name != nil {
|
||||||
|
full_name += " "
|
||||||
|
full_name += *profile.Last_Name
|
||||||
|
}
|
||||||
|
return full_name
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Token string `gorm:"uniqueIndex" json:"token"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Last_Used_date time.Time `json:"last_used_date"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
ProfileID *string `json:"profile_id"`
|
||||||
|
Profile *Profile `gorm:"foreignKey:ProfileID" json:"profile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (token *Token) TableName() string {
|
||||||
|
return "token"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Permission struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Assignments []Assignment `gorm:"foreignKey:PermissionID" json:"assignments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (permission *Permission) TableName() string {
|
||||||
|
return "permission"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Assignment struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
ProfileID *string `json:"profile_id"`
|
||||||
|
Profile *Profile `gorm:"foreignKey:ProfileID" json:"profile"`
|
||||||
|
PermissionID *string `json:"permission_id"`
|
||||||
|
Permission *Profile `gorm:"foreignKey:PermissionID" json:"permission"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assignment *Assignment) TableName() string {
|
||||||
|
return "assignment"
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Base struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
CreatedAt time.Time `json:"created_date"`
|
||||||
|
UpdatedAt time.Time `json:"last_modified_date"`
|
||||||
|
DeletedAt *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeCreate will set a UUID rather than numeric ID.
|
||||||
|
func (b *Base) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
|
b.ID = uuid.New().String()
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB *gorm.DB
|
||||||
|
|
||||||
|
func Connect() {
|
||||||
|
// Build the connection string from environment variables
|
||||||
|
// This keeps sensitive credentials out of the codebase
|
||||||
|
dsn := fmt.Sprintf(
|
||||||
|
"host=%s user=%s password=%s dbname=%s port=%d sslmode=disable",
|
||||||
|
viper.GetString("db.host"),
|
||||||
|
viper.GetString("db.user"),
|
||||||
|
viper.GetString("db.password"),
|
||||||
|
viper.GetString("db.name"),
|
||||||
|
viper.GetInt("db.port"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure GORM with custom settings
|
||||||
|
// The logger helps with debugging SQL queries during development
|
||||||
|
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to connect to database:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the underlying SQL database connection for pool configuration
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to get database instance:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure connection pool settings
|
||||||
|
// MaxIdleConns: number of connections kept open when idle
|
||||||
|
sqlDB.SetMaxIdleConns(10)
|
||||||
|
// MaxOpenConns: maximum total connections to the database
|
||||||
|
sqlDB.SetMaxOpenConns(100)
|
||||||
|
// ConnMaxLifetime: how long a connection can be reused
|
||||||
|
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||||
|
|
||||||
|
DB = db
|
||||||
|
slog.Info("Database connected successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunMigrations() {
|
||||||
|
// AutoMigrate creates tables, missing columns, and missing indexes
|
||||||
|
// It will NOT delete unused columns to protect your data
|
||||||
|
err := DB.AutoMigrate()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Migration failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("Database migration completed")
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type MediaLoFi struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Url 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 {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
CloudLink string `gorm:"column:cloud_link" 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:"column:url" 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"
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaActor struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
|
||||||
|
MediaActorFiles []MediaActorFile `gorm:"foreignKey:MediaActorID" json:"media_actor_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (media_actor *MediaActor) TableName() string {
|
||||||
|
return "media_actor"
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaActorFile struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
MediaActorID *string `json:"media_actor_id"`
|
||||||
|
MediaActor *MediaActor `gorm:"foreignKey:MediaActorID" json:"media_actor"`
|
||||||
|
MediaFileID *string `json:"media_file_id"`
|
||||||
|
MediaFile *MediaFile `gorm:"foreignKey:MediaFileID" json:"media_file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (media_file *MediaActorFile) TableName() string {
|
||||||
|
return "media_actor_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaArticle struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
Review bool `json:"review"`
|
||||||
|
Title string `gorm:"size:300" json:"title"`
|
||||||
|
Url string `gorm:"uniqueIndex" json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (media_article *MediaArticle) TableName() string {
|
||||||
|
return "media_article"
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaVideo struct {
|
||||||
|
ID string `gorm:"type:string;primary_key;"`
|
||||||
|
Created_Date time.Time `json:"created_date"`
|
||||||
|
Last_Modified_date time.Time `json:"last_modified_date"`
|
||||||
|
Deleted_Date *time.Time `json:"deleted_date"`
|
||||||
|
Version int `gorm:"default:0" json:"version"`
|
||||||
|
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"`
|
||||||
|
Url string `gorm:"uniqueIndex" json:"url"`
|
||||||
|
ShouldDownload bool `json:"should_download"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (media_video *MediaVideo) TableName() string {
|
||||||
|
return "media_video"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{{ define "footer" }}
|
||||||
|
<div class="footer">{{ .version }}</div>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{{ define "head" }}
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/public/style/main.css" />
|
||||||
|
<title>{{ .title }}</title>
|
||||||
|
</head>
|
||||||
|
{{ end }}
|
||||||
@@ -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 }}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{{ define "navbar" }}
|
||||||
|
<div class="container">
|
||||||
|
<nav class="navbar fixed-top navbar-expand-lg navbar-dark" style="background-color: darkgrey;">
|
||||||
|
<a class="navbar-brand" href="/">Kontor</a>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-item nav-link" href="/comic/comics">Comics</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Media</a>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
||||||
|
<a class="dropdown-item" href="/media/files">Media Files</a>
|
||||||
|
<a class="dropdown-item" href="/media/actors">Media Actors</a>
|
||||||
|
<a class="dropdown-item" href="/media/lofis">Lofi</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/media/files">Media</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/media/lofis">Lofis</a>
|
||||||
|
</li>
|
||||||
|
{{ if .user }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ .logout }}">Logout</a>
|
||||||
|
</li>
|
||||||
|
{{ else }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ .login }}">Login</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
<form class="form-inline">
|
||||||
|
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
|
||||||
|
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
|
||||||
|
</form>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{{ define "comics" }}
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
{{ template "head" . }}
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{ template "navbar" . }}
|
||||||
|
<div class="row">
|
||||||
|
<form class="form-inline" action="/comic/comics">
|
||||||
|
<input type="text" placeholder="Search" name="query">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="completed" {{ if .Completed }}checked{{ end }}>Completed
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="order" {{ if .CurrentOrder }}checked{{ end }}>Order
|
||||||
|
</label>
|
||||||
|
<button type="submit">Search</button>
|
||||||
|
<div class="pill-nav"><a href="/comic/comic/add">Add Comic</a></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="maincolumn">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Title</th>
|
||||||
|
<th scope="col">Publisher</th>
|
||||||
|
<th scope="col">Completed</th>
|
||||||
|
<th colspan="2">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range .comics }}
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><a href="/comic/comics/{{.ID}}">{{.Title}}</a></th>
|
||||||
|
{{ if .Publisher }}
|
||||||
|
<td><a href="/comic/publishers/{{.Publisher.ID}}">{{.Publisher.Name}}</a></td>
|
||||||
|
{{ else }}
|
||||||
|
<td>Publisher not set</td>
|
||||||
|
{{ end }}
|
||||||
|
<td>
|
||||||
|
{{ if .Completed }}
|
||||||
|
<img src="/public/icons/tick.png" alt="true" width="24" height="24">
|
||||||
|
{{ else }}
|
||||||
|
<img src="/public/icons/cross.png" alt="true" width="24" height="24">
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
<td><a href="/comic/comics/edit/{{.ID}}" class="btn btn-outline-primary btn-sm active" role="button" aria-pressed="true">Edit</a></td>
|
||||||
|
<td><a href="/comic/comics/delete/{{.ID}}" class="btn btn-outline-danger btn-sm active" role="button" aria-pressed="true">Delete</a></td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ template "footer" . }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user