[add] better log page, [add] admin users page, [add] admin nav
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
a65750ae21
commit
f0a2d2cf69
@ -3,7 +3,7 @@ FROM alpine AS certs
|
|||||||
RUN apk update && apk add ca-certificates
|
RUN apk update && apk add ca-certificates
|
||||||
|
|
||||||
# Build Image
|
# Build Image
|
||||||
FROM golang:1.20 AS build
|
FROM golang:1.21 AS build
|
||||||
|
|
||||||
# Copy Source
|
# Copy Source
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
@ -13,7 +13,9 @@ COPY . .
|
|||||||
RUN mkdir -p /opt/antholume
|
RUN mkdir -p /opt/antholume
|
||||||
|
|
||||||
# Compile
|
# Compile
|
||||||
RUN go build -o /opt/antholume/server
|
RUN go build \
|
||||||
|
-ldflags "-X reichard.io/antholume/config.version=`git describe --tags`" \
|
||||||
|
-o /opt/antholume/server
|
||||||
|
|
||||||
# Create Image
|
# Create Image
|
||||||
FROM busybox:1.36
|
FROM busybox:1.36
|
||||||
|
@ -3,7 +3,7 @@ FROM alpine AS certs
|
|||||||
RUN apk update && apk add ca-certificates
|
RUN apk update && apk add ca-certificates
|
||||||
|
|
||||||
# Build Image
|
# Build Image
|
||||||
FROM --platform=$BUILDPLATFORM golang:1.20 AS build
|
FROM --platform=$BUILDPLATFORM golang:1.21 AS build
|
||||||
|
|
||||||
# Create Package Directory
|
# Create Package Directory
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
@ -15,7 +15,9 @@ ARG TARGETARCH
|
|||||||
RUN --mount=target=. \
|
RUN --mount=target=. \
|
||||||
--mount=type=cache,target=/root/.cache/go-build \
|
--mount=type=cache,target=/root/.cache/go-build \
|
||||||
--mount=type=cache,target=/go/pkg \
|
--mount=type=cache,target=/go/pkg \
|
||||||
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /opt/antholume/server
|
GOOS=$TARGETOS GOARCH=$TARGETARCH go build \
|
||||||
|
-ldflags "-X reichard.io/antholume/config.version=`git describe --tags`" \
|
||||||
|
-o /opt/antholume/server
|
||||||
|
|
||||||
# Create Image
|
# Create Image
|
||||||
FROM busybox:1.36
|
FROM busybox:1.36
|
||||||
|
8
Makefile
8
Makefile
@ -3,10 +3,10 @@ build_local: build_tailwind
|
|||||||
rm -r ./build || true
|
rm -r ./build || true
|
||||||
mkdir -p ./build
|
mkdir -p ./build
|
||||||
|
|
||||||
env GOOS=linux GOARCH=amd64 go build -o ./build/server_linux_amd64
|
env GOOS=linux GOARCH=amd64 go build -ldflags "-X reichard.io/antholume/config.version=`git describe --tags`" -o ./build/server_linux_amd64
|
||||||
env GOOS=linux GOARCH=arm64 go build -o ./build/server_linux_arm64
|
env GOOS=linux GOARCH=arm64 go build -ldflags "-X reichard.io/antholume/config.version=`git describe --tags`" -o ./build/server_linux_arm64
|
||||||
env GOOS=darwin GOARCH=arm64 go build -o ./build/server_darwin_arm64
|
env GOOS=darwin GOARCH=arm64 go build -ldflags "-X reichard.io/antholume/config.version=`git describe --tags`" -o ./build/server_darwin_arm64
|
||||||
env GOOS=darwin GOARCH=amd64 go build -o ./build/server_darwin_amd64
|
env GOOS=darwin GOARCH=amd64 go build -ldflags "-X reichard.io/antholume/config.version=`git describe --tags`" -o ./build/server_darwin_amd64
|
||||||
|
|
||||||
docker_build_local: build_tailwind
|
docker_build_local: build_tailwind
|
||||||
docker build -t antholume:latest .
|
docker build -t antholume:latest .
|
||||||
|
@ -112,6 +112,7 @@ func (api *API) registerWebAppRoutes() {
|
|||||||
api.Router.GET("/register", api.appGetRegister)
|
api.Router.GET("/register", api.appGetRegister)
|
||||||
api.Router.GET("/settings", api.authWebAppMiddleware, api.appGetSettings)
|
api.Router.GET("/settings", api.authWebAppMiddleware, api.appGetSettings)
|
||||||
api.Router.GET("/admin/logs", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appGetAdminLogs)
|
api.Router.GET("/admin/logs", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appGetAdminLogs)
|
||||||
|
api.Router.GET("/admin/users", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appGetAdminUsers)
|
||||||
api.Router.GET("/admin", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appGetAdmin)
|
api.Router.GET("/admin", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appGetAdmin)
|
||||||
api.Router.POST("/admin", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appPerformAdminAction)
|
api.Router.POST("/admin", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appPerformAdminAction)
|
||||||
api.Router.POST("/login", api.appAuthFormLogin)
|
api.Router.POST("/login", api.appAuthFormLogin)
|
||||||
@ -184,6 +185,7 @@ func (api *API) generateTemplates() *multitemplate.Renderer {
|
|||||||
"GetUTCOffsets": getUTCOffsets,
|
"GetUTCOffsets": getUTCOffsets,
|
||||||
"NiceSeconds": niceSeconds,
|
"NiceSeconds": niceSeconds,
|
||||||
"dict": dict,
|
"dict": dict,
|
||||||
|
"hasPrefix": strings.HasPrefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load Base
|
// Load Base
|
||||||
|
@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
|
"bufio"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -132,8 +133,7 @@ func (api *API) appDocumentReader(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetDocuments(c *gin.Context) {
|
func (api *API) appGetDocuments(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("documents", c)
|
templateVars, auth := api.getBaseTemplateVars("documents", c)
|
||||||
auth := templateVars["Authorization"].(authData)
|
|
||||||
qParams := bindQueryParams(c, 9)
|
qParams := bindQueryParams(c, 9)
|
||||||
|
|
||||||
var query *string
|
var query *string
|
||||||
@ -184,8 +184,7 @@ func (api *API) appGetDocuments(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetDocument(c *gin.Context) {
|
func (api *API) appGetDocument(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("document", c)
|
templateVars, auth := api.getBaseTemplateVars("document", c)
|
||||||
auth := templateVars["Authorization"].(authData)
|
|
||||||
|
|
||||||
var rDocID requestDocumentID
|
var rDocID requestDocumentID
|
||||||
if err := c.ShouldBindUri(&rDocID); err != nil {
|
if err := c.ShouldBindUri(&rDocID); err != nil {
|
||||||
@ -211,8 +210,7 @@ func (api *API) appGetDocument(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetProgress(c *gin.Context) {
|
func (api *API) appGetProgress(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("progress", c)
|
templateVars, auth := api.getBaseTemplateVars("progress", c)
|
||||||
auth := templateVars["Authorization"].(authData)
|
|
||||||
|
|
||||||
qParams := bindQueryParams(c, 15)
|
qParams := bindQueryParams(c, 15)
|
||||||
|
|
||||||
@ -240,8 +238,7 @@ func (api *API) appGetProgress(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetActivity(c *gin.Context) {
|
func (api *API) appGetActivity(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("activity", c)
|
templateVars, auth := api.getBaseTemplateVars("activity", c)
|
||||||
auth := templateVars["Authorization"].(authData)
|
|
||||||
qParams := bindQueryParams(c, 15)
|
qParams := bindQueryParams(c, 15)
|
||||||
|
|
||||||
activityFilter := database.GetActivityParams{
|
activityFilter := database.GetActivityParams{
|
||||||
@ -268,8 +265,7 @@ func (api *API) appGetActivity(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetHome(c *gin.Context) {
|
func (api *API) appGetHome(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("home", c)
|
templateVars, auth := api.getBaseTemplateVars("home", c)
|
||||||
auth := templateVars["Authorization"].(authData)
|
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
graphData, _ := api.DB.Queries.GetDailyReadStats(api.DB.Ctx, auth.UserName)
|
graphData, _ := api.DB.Queries.GetDailyReadStats(api.DB.Ctx, auth.UserName)
|
||||||
@ -293,8 +289,7 @@ func (api *API) appGetHome(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetSettings(c *gin.Context) {
|
func (api *API) appGetSettings(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("settings", c)
|
templateVars, auth := api.getBaseTemplateVars("settings", c)
|
||||||
auth := templateVars["Authorization"].(authData)
|
|
||||||
|
|
||||||
user, err := api.DB.Queries.GetUser(api.DB.Ctx, auth.UserName)
|
user, err := api.DB.Queries.GetUser(api.DB.Ctx, auth.UserName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -319,11 +314,13 @@ func (api *API) appGetSettings(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetAdmin(c *gin.Context) {
|
func (api *API) appGetAdmin(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("admin", c)
|
templateVars, _ := api.getBaseTemplateVars("admin", c)
|
||||||
c.HTML(http.StatusOK, "page/admin", templateVars)
|
c.HTML(http.StatusOK, "page/admin", templateVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetAdminLogs(c *gin.Context) {
|
func (api *API) appGetAdminLogs(c *gin.Context) {
|
||||||
|
templateVars, _ := api.getBaseTemplateVars("admin-logs", c)
|
||||||
|
|
||||||
// Open Log File
|
// Open Log File
|
||||||
logPath := path.Join(api.Config.ConfigPath, "logs/antholume.log")
|
logPath := path.Join(api.Config.ConfigPath, "logs/antholume.log")
|
||||||
logFile, err := os.Open(logPath)
|
logFile, err := os.Open(logPath)
|
||||||
@ -333,18 +330,38 @@ func (api *API) appGetAdminLogs(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
defer logFile.Close()
|
defer logFile.Close()
|
||||||
|
|
||||||
// Write Log File
|
// Log Lines
|
||||||
c.Stream(func(w io.Writer) bool {
|
var logLines []string
|
||||||
_, err = io.Copy(w, logFile)
|
scanner := bufio.NewScanner(logFile)
|
||||||
if err != nil {
|
for scanner.Scan() {
|
||||||
return true
|
logLines = append(logLines, scanner.Text())
|
||||||
}
|
}
|
||||||
return false
|
templateVars["Data"] = logLines
|
||||||
})
|
|
||||||
|
c.HTML(http.StatusOK, "page/admin-logs", templateVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) appGetAdminUsers(c *gin.Context) {
|
||||||
|
templateVars, _ := api.getBaseTemplateVars("admin-users", c)
|
||||||
|
|
||||||
|
users, err := api.DB.Queries.GetUsers(api.DB.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[appGetAdminUsers] GetUsers DB Error:", err)
|
||||||
|
errorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUsers DB Error: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
templateVars["Data"] = users
|
||||||
|
|
||||||
|
c.HTML(http.StatusOK, "page/admin-users", templateVars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tabs:
|
||||||
|
// - General (Import, Backup & Restore, Version (githash?), Stats?)
|
||||||
|
// - Users
|
||||||
|
// - Metadata
|
||||||
func (api *API) appPerformAdminAction(c *gin.Context) {
|
func (api *API) appPerformAdminAction(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("admin", c)
|
templateVars, _ := api.getBaseTemplateVars("admin", c)
|
||||||
|
|
||||||
var rAdminAction requestAdminAction
|
var rAdminAction requestAdminAction
|
||||||
if err := c.ShouldBind(&rAdminAction); err != nil {
|
if err := c.ShouldBind(&rAdminAction); err != nil {
|
||||||
@ -438,7 +455,7 @@ func (api *API) appPerformAdminAction(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetSearch(c *gin.Context) {
|
func (api *API) appGetSearch(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("search", c)
|
templateVars, _ := api.getBaseTemplateVars("search", c)
|
||||||
|
|
||||||
var sParams searchParams
|
var sParams searchParams
|
||||||
c.BindQuery(&sParams)
|
c.BindQuery(&sParams)
|
||||||
@ -462,7 +479,7 @@ func (api *API) appGetSearch(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appGetLogin(c *gin.Context) {
|
func (api *API) appGetLogin(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("login", c)
|
templateVars, _ := api.getBaseTemplateVars("login", c)
|
||||||
templateVars["RegistrationEnabled"] = api.Config.RegistrationEnabled
|
templateVars["RegistrationEnabled"] = api.Config.RegistrationEnabled
|
||||||
c.HTML(http.StatusOK, "page/login", templateVars)
|
c.HTML(http.StatusOK, "page/login", templateVars)
|
||||||
}
|
}
|
||||||
@ -473,7 +490,7 @@ func (api *API) appGetRegister(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
templateVars := api.getBaseTemplateVars("login", c)
|
templateVars, _ := api.getBaseTemplateVars("login", c)
|
||||||
templateVars["RegistrationEnabled"] = api.Config.RegistrationEnabled
|
templateVars["RegistrationEnabled"] = api.Config.RegistrationEnabled
|
||||||
templateVars["Register"] = true
|
templateVars["Register"] = true
|
||||||
c.HTML(http.StatusOK, "page/login", templateVars)
|
c.HTML(http.StatusOK, "page/login", templateVars)
|
||||||
@ -842,8 +859,7 @@ func (api *API) appIdentifyDocument(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get Template Variables
|
// Get Template Variables
|
||||||
templateVars := api.getBaseTemplateVars("document", c)
|
templateVars, auth := api.getBaseTemplateVars("document", c)
|
||||||
auth := templateVars["Authorization"].(authData)
|
|
||||||
|
|
||||||
// Get Metadata
|
// Get Metadata
|
||||||
metadataResults, err := metadata.SearchMetadata(metadata.GBOOK, metadata.MetadataInfo{
|
metadataResults, err := metadata.SearchMetadata(metadata.GBOOK, metadata.MetadataInfo{
|
||||||
@ -900,12 +916,15 @@ func (api *API) appSaveNewDocument(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render Initial Template
|
// Render Initial Template
|
||||||
templateVars := api.getBaseTemplateVars("search", c)
|
templateVars, _ := api.getBaseTemplateVars("search", c)
|
||||||
c.HTML(http.StatusOK, "page/search", templateVars)
|
c.HTML(http.StatusOK, "page/search", templateVars)
|
||||||
|
|
||||||
// Create Streamer
|
// Create Streamer
|
||||||
stream := api.newStreamer(c)
|
stream := api.newStreamer(c, `
|
||||||
defer stream.close()
|
<div class="absolute top-0 left-0 w-full h-full z-50">
|
||||||
|
<div class="fixed top-0 left-0 bg-black opacity-50 w-screen h-screen"></div>
|
||||||
|
<div id="stream-main" class="relative max-h-[95%] -translate-x-2/4 top-1/2 left-1/2 w-5/6">`)
|
||||||
|
defer stream.close(`</div></div>`)
|
||||||
|
|
||||||
// Stream Helper Function
|
// Stream Helper Function
|
||||||
sendDownloadMessage := func(msg string, args ...map[string]any) {
|
sendDownloadMessage := func(msg string, args ...map[string]any) {
|
||||||
@ -1061,8 +1080,7 @@ func (api *API) appEditSettings(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
templateVars := api.getBaseTemplateVars("settings", c)
|
templateVars, auth := api.getBaseTemplateVars("settings", c)
|
||||||
auth := templateVars["Authorization"].(authData)
|
|
||||||
|
|
||||||
newUserSettings := database.UpdateUserParams{
|
newUserSettings := database.UpdateUserParams{
|
||||||
UserID: auth.UserName,
|
UserID: auth.UserName,
|
||||||
@ -1167,7 +1185,7 @@ func (api *API) getDocumentsWordCount(documents []database.GetDocumentsWithStats
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) getBaseTemplateVars(routeName string, c *gin.Context) gin.H {
|
func (api *API) getBaseTemplateVars(routeName string, c *gin.Context) (gin.H, authData) {
|
||||||
var auth authData
|
var auth authData
|
||||||
if data, _ := c.Get("Authorization"); data != nil {
|
if data, _ := c.Get("Authorization"); data != nil {
|
||||||
auth = data.(authData)
|
auth = data.(authData)
|
||||||
@ -1181,7 +1199,7 @@ func (api *API) getBaseTemplateVars(routeName string, c *gin.Context) gin.H {
|
|||||||
"SearchEnabled": api.Config.SearchEnabled,
|
"SearchEnabled": api.Config.SearchEnabled,
|
||||||
"RegistrationEnabled": api.Config.RegistrationEnabled,
|
"RegistrationEnabled": api.Config.RegistrationEnabled,
|
||||||
},
|
},
|
||||||
}
|
}, auth
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindQueryParams(c *gin.Context, defaultLimit int64) queryParams {
|
func bindQueryParams(c *gin.Context, defaultLimit int64) queryParams {
|
||||||
|
@ -141,7 +141,7 @@ func (api *API) authAdminWebAppMiddleware(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) appAuthFormLogin(c *gin.Context) {
|
func (api *API) appAuthFormLogin(c *gin.Context) {
|
||||||
templateVars := api.getBaseTemplateVars("login", c)
|
templateVars, _ := api.getBaseTemplateVars("login", c)
|
||||||
|
|
||||||
username := strings.TrimSpace(c.PostForm("username"))
|
username := strings.TrimSpace(c.PostForm("username"))
|
||||||
rawPassword := strings.TrimSpace(c.PostForm("password"))
|
rawPassword := strings.TrimSpace(c.PostForm("password"))
|
||||||
@ -179,7 +179,7 @@ func (api *API) appAuthFormRegister(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
templateVars := api.getBaseTemplateVars("login", c)
|
templateVars, _ := api.getBaseTemplateVars("login", c)
|
||||||
templateVars["Register"] = true
|
templateVars["Register"] = true
|
||||||
|
|
||||||
username := strings.TrimSpace(c.PostForm("username"))
|
username := strings.TrimSpace(c.PostForm("username"))
|
||||||
|
@ -17,7 +17,7 @@ type streamer struct {
|
|||||||
completeCh chan struct{}
|
completeCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) newStreamer(c *gin.Context) *streamer {
|
func (api *API) newStreamer(c *gin.Context, data string) *streamer {
|
||||||
stream := &streamer{
|
stream := &streamer{
|
||||||
templates: api.Templates,
|
templates: api.Templates,
|
||||||
writer: c.Writer,
|
writer: c.Writer,
|
||||||
@ -32,10 +32,7 @@ func (api *API) newStreamer(c *gin.Context) *streamer {
|
|||||||
stream.writer.WriteHeader(http.StatusOK)
|
stream.writer.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
// Send Open Element Tags
|
// Send Open Element Tags
|
||||||
stream.write(`
|
stream.write(data)
|
||||||
<div class="absolute top-0 left-0 w-full h-full z-50">
|
|
||||||
<div class="fixed top-0 left-0 bg-black opacity-50 w-screen h-screen"></div>
|
|
||||||
<div id="stream-main" class="relative max-h-[95%] -translate-x-2/4 top-1/2 left-1/2 w-5/6">`)
|
|
||||||
|
|
||||||
// Keep Alive
|
// Keep Alive
|
||||||
go func() {
|
go func() {
|
||||||
@ -70,9 +67,9 @@ func (stream *streamer) send(templateName string, templateVars gin.H) {
|
|||||||
stream.write(buf.String())
|
stream.write(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (stream *streamer) close() {
|
func (stream *streamer) close(data string) {
|
||||||
// Send Close Element Tags
|
// Send Close Element Tags
|
||||||
stream.write(`</div></div>`)
|
stream.write(data)
|
||||||
|
|
||||||
// Close
|
// Close
|
||||||
close(stream.completeCh)
|
close(stream.completeCh)
|
||||||
|
File diff suppressed because one or more lines are too long
@ -43,9 +43,12 @@ func (u UTCFormatter) Format(e *log.Entry) ([]byte, error) {
|
|||||||
return u.Formatter.Format(e)
|
return u.Formatter.Format(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set at runtime
|
||||||
|
var version string = "develop"
|
||||||
|
|
||||||
func Load() *Config {
|
func Load() *Config {
|
||||||
c := &Config{
|
c := &Config{
|
||||||
Version: "0.0.1",
|
Version: version,
|
||||||
DBType: trimLowerString(getEnv("DATABASE_TYPE", "SQLite")),
|
DBType: trimLowerString(getEnv("DATABASE_TYPE", "SQLite")),
|
||||||
DBName: trimLowerString(getEnv("DATABASE_NAME", "antholume")),
|
DBName: trimLowerString(getEnv("DATABASE_NAME", "antholume")),
|
||||||
ConfigPath: getEnv("CONFIG_PATH", "/config"),
|
ConfigPath: getEnv("CONFIG_PATH", "/config"),
|
||||||
|
@ -305,6 +305,9 @@ WHERE id = $user_id LIMIT 1;
|
|||||||
SELECT * FROM user_streaks
|
SELECT * FROM user_streaks
|
||||||
WHERE user_id = $user_id;
|
WHERE user_id = $user_id;
|
||||||
|
|
||||||
|
-- name: GetUsers :many
|
||||||
|
SELECT * FROM users;
|
||||||
|
|
||||||
-- name: GetWPMLeaderboard :many
|
-- name: GetWPMLeaderboard :many
|
||||||
SELECT
|
SELECT
|
||||||
user_id,
|
user_id,
|
||||||
|
@ -1008,6 +1008,39 @@ func (q *Queries) GetUserStreaks(ctx context.Context, userID string) ([]UserStre
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getUsers = `-- name: GetUsers :many
|
||||||
|
SELECT id, pass, admin, time_offset, created_at FROM users
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetUsers(ctx context.Context) ([]User, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getUsers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []User
|
||||||
|
for rows.Next() {
|
||||||
|
var i User
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Pass,
|
||||||
|
&i.Admin,
|
||||||
|
&i.TimeOffset,
|
||||||
|
&i.CreatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getWPMLeaderboard = `-- name: GetWPMLeaderboard :many
|
const getWPMLeaderboard = `-- name: GetWPMLeaderboard :many
|
||||||
SELECT
|
SELECT
|
||||||
user_id,
|
user_id,
|
||||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module reichard.io/antholume
|
module reichard.io/antholume
|
||||||
|
|
||||||
go 1.19
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.8.1
|
github.com/PuerkitoBio/goquery v1.8.1
|
||||||
|
8
go.sum
8
go.sum
@ -43,6 +43,7 @@ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SU
|
|||||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
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.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
@ -63,8 +64,10 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
|||||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||||
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||||
@ -193,6 +196,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -254,7 +258,9 @@ modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
|||||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||||
|
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||||
|
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||||
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
||||||
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||||
@ -268,8 +274,10 @@ modernc.org/sqlite v1.26.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU
|
|||||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||||
|
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
|
||||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||||
|
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
@ -6,7 +6,11 @@ module.exports = {
|
|||||||
"./assets/reader/*.{html,htm,svg,js}",
|
"./assets/reader/*.{html,htm,svg,js}",
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {
|
||||||
|
minWidth: {
|
||||||
|
40: "10rem",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
};
|
};
|
||||||
|
@ -141,28 +141,28 @@
|
|||||||
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "home"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "home"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
||||||
href="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span class="text-left">{{ template "svg/home" (dict "Size" 20) }}</span>
|
{{ template "svg/home" (dict "Size" 20) }}
|
||||||
<span class="mx-4 text-sm font-normal">Home</span>
|
<span class="mx-4 text-sm font-normal">Home</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "documents"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "documents"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
||||||
href="/documents"
|
href="/documents"
|
||||||
>
|
>
|
||||||
<span class="text-left">{{ template "svg/documents" (dict "Size" 20) }}</span>
|
{{ template "svg/documents" (dict "Size" 20) }}
|
||||||
<span class="mx-4 text-sm font-normal">Documents</span>
|
<span class="mx-4 text-sm font-normal">Documents</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "progress"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "progress"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
||||||
href="/progress"
|
href="/progress"
|
||||||
>
|
>
|
||||||
<span class="text-left">{{ template "svg/activity" (dict "Size" 20) }}</span>
|
{{ template "svg/activity" (dict "Size" 20) }}
|
||||||
<span class="mx-4 text-sm font-normal">Progress</span>
|
<span class="mx-4 text-sm font-normal">Progress</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "activity"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "activity"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
||||||
href="/activity"
|
href="/activity"
|
||||||
>
|
>
|
||||||
<span class="text-left">{{ template "svg/activity" (dict "Size" 20) }}</span>
|
{{ template "svg/activity" (dict "Size" 20) }}
|
||||||
<span class="mx-4 text-sm font-normal">Activity</span>
|
<span class="mx-4 text-sm font-normal">Activity</span>
|
||||||
</a>
|
</a>
|
||||||
{{ if .Config.SearchEnabled }}
|
{{ if .Config.SearchEnabled }}
|
||||||
@ -170,12 +170,36 @@
|
|||||||
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "search"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
class="flex items-center justify-start w-full p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if eq .RouteName "search"}}border-purple-500 dark:text-white{{else}}border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}"
|
||||||
href="/search"
|
href="/search"
|
||||||
>
|
>
|
||||||
<span class="text-left">{{ template "svg/search" (dict "Size" 20) }}</span>
|
{{ template "svg/search" (dict "Size" 20) }}
|
||||||
<span class="mx-4 text-sm font-normal">Search</span>
|
<span class="mx-4 text-sm font-normal">Search</span>
|
||||||
</a>
|
</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if .Authorization.IsAdmin }}
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4 p-2 pl-6 my-2 transition-colors duration-200 border-l-4 {{if hasPrefix .RouteName "admin"}}dark:text-white border-purple-500{{else}}border-transparent text-gray-400{{end}}">
|
||||||
|
<a href="/admin" class="flex justify-start w-full {{if not (hasPrefix .RouteName "admin")}}text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}">
|
||||||
|
{{ template "svg/settings" (dict "Size" 20) }}
|
||||||
|
<span class="mx-4 text-sm font-normal">Admin</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{ if hasPrefix .RouteName "admin" }}
|
||||||
|
<a href="/admin" style="padding-left: 1.75em;" class="flex justify-start w-full {{if not (eq .RouteName "admin")}}text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}">
|
||||||
|
<span class="mx-4 text-sm font-normal">General</span>
|
||||||
|
</a>
|
||||||
|
<a href="/admin/users" style="padding-left: 1.75em;" class="flex justify-start w-full {{if not (eq .RouteName "admin-users")}}text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}">
|
||||||
|
<span class="mx-4 text-sm font-normal">Users</span>
|
||||||
|
</a>
|
||||||
|
<a href="/admin/logs" style="padding-left: 1.75em;" class="flex justify-start w-full {{if not (eq .RouteName "admin-logs")}}text-gray-400 hover:text-gray-800 dark:hover:text-gray-100{{end}}">
|
||||||
|
<span class="mx-4 text-sm font-normal">Logs</span>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<a class="flex justify-center items-center p-6 w-full absolute bottom-0" target="_blank" href="https://gitea.va.reichard.io/evan/AnthoLume">
|
{{ end }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<a class="flex flex-col gap-2 justify-center items-center p-6 w-full absolute bottom-0 text-black dark:text-white" target="_blank" href="https://gitea.va.reichard.io/evan/AnthoLume">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="text-black dark:text-white"
|
class="text-black dark:text-white"
|
||||||
@ -211,6 +235,7 @@
|
|||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
<span class="text-xs">{{ .Config.Version }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -231,19 +256,6 @@
|
|||||||
aria-orientation="vertical"
|
aria-orientation="vertical"
|
||||||
aria-labelledby="options-menu"
|
aria-labelledby="options-menu"
|
||||||
>
|
>
|
||||||
|
|
||||||
{{ if .Authorization.IsAdmin }}
|
|
||||||
<a
|
|
||||||
href="/admin"
|
|
||||||
class="block px-4 py-2 text-md text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600"
|
|
||||||
role="menuitem"
|
|
||||||
>
|
|
||||||
<span class="flex flex-col">
|
|
||||||
<span>Administration</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="/settings"
|
href="/settings"
|
||||||
class="block px-4 py-2 text-md text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600"
|
class="block px-4 py-2 text-md text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600"
|
||||||
|
15
templates/pages/admin-logs.html
Normal file
15
templates/pages/admin-logs.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{{template "base" .}} {{define "title"}}Admin - Logs{{end}} {{define
|
||||||
|
"header"}}
|
||||||
|
<a class="whitespace-pre" href="../admin">Admin - Logs</a>
|
||||||
|
{{end}} {{define "content"}}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-col-reverse text-black dark:text-white"
|
||||||
|
style="font-family: monospace"
|
||||||
|
>
|
||||||
|
{{range $log := .Data }}
|
||||||
|
<span class="whitespace-pre">{{ $log }}</span>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{end}}
|
50
templates/pages/admin-users.html
Normal file
50
templates/pages/admin-users.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{{template "base" .}} {{define "title"}}Admin - Users{{end}} {{define "header"}}
|
||||||
|
<a class="whitespace-pre" href="../admin">Admin - Users</a>
|
||||||
|
{{end}} {{define "content"}}
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<div class="inline-block min-w-full overflow-hidden rounded shadow">
|
||||||
|
<table class="min-w-full leading-normal bg-white dark:bg-gray-700 text-sm">
|
||||||
|
<thead class="text-gray-800 dark:text-gray-400">
|
||||||
|
<tr>
|
||||||
|
<th class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800 w-12">
|
||||||
|
{{ template "svg/add" }}
|
||||||
|
</th>
|
||||||
|
<th class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800">
|
||||||
|
User
|
||||||
|
</th>
|
||||||
|
<th class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800 text-center">
|
||||||
|
Permissions
|
||||||
|
</th>
|
||||||
|
<th class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800 w-48">
|
||||||
|
Created
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="text-black dark:text-white">
|
||||||
|
{{ if not .Data }}
|
||||||
|
<tr>
|
||||||
|
<td class="text-center p-3" colspan="2">No Results</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{range $user := .Data }}
|
||||||
|
<tr>
|
||||||
|
<td class="p-3 border-b border-gray-200 text-gray-800 dark:text-gray-400">
|
||||||
|
{{ template "svg/delete" }}
|
||||||
|
</td>
|
||||||
|
<td class="p-3 border-b border-gray-200">
|
||||||
|
<p>{{ $user.ID }}</p>
|
||||||
|
</td>
|
||||||
|
<td class="p-3 border-b border-gray-200 text-center min-w-40">
|
||||||
|
<span class="px-2 py-1 rounded-md text-white dark:text-black {{if $user.Admin}}bg-gray-800 dark:bg-gray-100{{else}}bg-gray-400 dark:bg-gray-600 cursor-pointer{{end}}">admin</span>
|
||||||
|
<span class="px-2 py-1 rounded-md text-white dark:text-black {{if $user.Admin}}bg-gray-400 dark:bg-gray-600 cursor-pointer{{else}}bg-gray-800 dark:bg-gray-100{{end}}">user</span>
|
||||||
|
</td>
|
||||||
|
<td class="p-3 border-b border-gray-200">
|
||||||
|
<p>{{ $user.CreatedAt }}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
@ -1,6 +1,6 @@
|
|||||||
{{template "base" .}} {{define "title"}}Administration{{end}} {{define
|
{{template "base" .}} {{define "title"}}Admin - General{{end}} {{define
|
||||||
"header"}}
|
"header"}}
|
||||||
<a href="./admin">Administration</a>
|
<a class="whitespace-pre" href="./admin">Admin - General</a>
|
||||||
{{end}} {{define "content"}}
|
{{end}} {{define "content"}}
|
||||||
<div class="w-full flex flex-col md:flex-row gap-4">
|
<div class="w-full flex flex-col md:flex-row gap-4">
|
||||||
<div>
|
<div>
|
||||||
@ -168,7 +168,6 @@
|
|||||||
<td class="py-2 float-right">
|
<td class="py-2 float-right">
|
||||||
<a
|
<a
|
||||||
href="./admin/logs"
|
href="./admin/logs"
|
||||||
target="_blank"
|
|
||||||
class="inline-block w-40 px-10 py-2 text-base font-semibold text-center text-white transition duration-200 ease-in bg-black shadow-md hover:text-black hover:bg-white focus:outline-none focus:ring-2"
|
class="inline-block w-40 px-10 py-2 text-base font-semibold text-center text-white transition duration-200 ease-in bg-black shadow-md hover:text-black hover:bg-white focus:outline-none focus:ring-2"
|
||||||
>
|
>
|
||||||
<span class="w-full">View</span>
|
<span class="w-full">View</span>
|
||||||
|
20
templates/svgs/add.svg
Normal file
20
templates/svgs/add.svg
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<svg
|
||||||
|
width="{{ or .Size 24 }}"
|
||||||
|
height="{{ or .Size 24 }}"
|
||||||
|
|
||||||
|
{{ if .Disabled }}
|
||||||
|
class="text-gray-200 dark:text-gray-600"
|
||||||
|
{{ else }}
|
||||||
|
class="hover:text-gray-800 dark:hover:text-gray-100"
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M12 22C7.28595 22 4.92893 22 3.46447 20.5355C2 19.0711 2 16.714 2 12C2 7.28595 2 4.92893 3.46447 3.46447C4.92893 2 7.28595 2 12 2C16.714 2 19.0711 2 20.5355 3.46447C22 4.92893 22 7.28595 22 12C22 16.714 22 19.0711 20.5355 20.5355C19.0711 22 16.714 22 12 22ZM12 8.25C12.4142 8.25 12.75 8.58579 12.75 9V11.25H15C15.4142 11.25 15.75 11.5858 15.75 12C15.75 12.4142 15.4142 12.75 15 12.75H12.75L12.75 15C12.75 15.4142 12.4142 15.75 12 15.75C11.5858 15.75 11.25 15.4142 11.25 15V12.75H9C8.58579 12.75 8.25 12.4142 8.25 12C8.25 11.5858 8.58579 11.25 9 11.25H11.25L11.25 9C11.25 8.58579 11.5858 8.25 12 8.25Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 972 B |
20
templates/svgs/settings.svg
Normal file
20
templates/svgs/settings.svg
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<svg
|
||||||
|
width="{{ or .Size 24 }}"
|
||||||
|
height="{{ or .Size 24 }}"
|
||||||
|
|
||||||
|
{{ if .Disabled }}
|
||||||
|
class="text-gray-200 dark:text-gray-600"
|
||||||
|
{{ else }}
|
||||||
|
class="hover:text-gray-800 dark:hover:text-gray-100"
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M14.2788 2.15224C13.9085 2 13.439 2 12.5 2C11.561 2 11.0915 2 10.7212 2.15224C10.2274 2.35523 9.83509 2.74458 9.63056 3.23463C9.53719 3.45834 9.50065 3.7185 9.48635 4.09799C9.46534 4.65568 9.17716 5.17189 8.69017 5.45093C8.20318 5.72996 7.60864 5.71954 7.11149 5.45876C6.77318 5.2813 6.52789 5.18262 6.28599 5.15102C5.75609 5.08178 5.22018 5.22429 4.79616 5.5472C4.47814 5.78938 4.24339 6.1929 3.7739 6.99993C3.30441 7.80697 3.06967 8.21048 3.01735 8.60491C2.94758 9.1308 3.09118 9.66266 3.41655 10.0835C3.56506 10.2756 3.77377 10.437 4.0977 10.639C4.57391 10.936 4.88032 11.4419 4.88029 12C4.88026 12.5581 4.57386 13.0639 4.0977 13.3608C3.77372 13.5629 3.56497 13.7244 3.41645 13.9165C3.09108 14.3373 2.94749 14.8691 3.01725 15.395C3.06957 15.7894 3.30432 16.193 3.7738 17C4.24329 17.807 4.47804 18.2106 4.79606 18.4527C5.22008 18.7756 5.75599 18.9181 6.28589 18.8489C6.52778 18.8173 6.77305 18.7186 7.11133 18.5412C7.60852 18.2804 8.2031 18.27 8.69012 18.549C9.17714 18.8281 9.46533 19.3443 9.48635 19.9021C9.50065 20.2815 9.53719 20.5417 9.63056 20.7654C9.83509 21.2554 10.2274 21.6448 10.7212 21.8478C11.0915 22 11.561 22 12.5 22C13.439 22 13.9085 22 14.2788 21.8478C14.7726 21.6448 15.1649 21.2554 15.3694 20.7654C15.4628 20.5417 15.4994 20.2815 15.5137 19.902C15.5347 19.3443 15.8228 18.8281 16.3098 18.549C16.7968 18.2699 17.3914 18.2804 17.8886 18.5412C18.2269 18.7186 18.4721 18.8172 18.714 18.8488C19.2439 18.9181 19.7798 18.7756 20.2038 18.4527C20.5219 18.2105 20.7566 17.807 21.2261 16.9999C21.6956 16.1929 21.9303 15.7894 21.9827 15.395C22.0524 14.8691 21.9088 14.3372 21.5835 13.9164C21.4349 13.7243 21.2262 13.5628 20.9022 13.3608C20.4261 13.0639 20.1197 12.558 20.1197 11.9999C20.1197 11.4418 20.4261 10.9361 20.9022 10.6392C21.2263 10.4371 21.435 10.2757 21.5836 10.0835C21.9089 9.66273 22.0525 9.13087 21.9828 8.60497C21.9304 8.21055 21.6957 7.80703 21.2262 7C20.7567 6.19297 20.522 5.78945 20.2039 5.54727C19.7799 5.22436 19.244 5.08185 18.7141 5.15109C18.4722 5.18269 18.2269 5.28136 17.8887 5.4588C17.3915 5.71959 16.7969 5.73002 16.3099 5.45096C15.8229 5.17191 15.5347 4.65566 15.5136 4.09794C15.4993 3.71848 15.4628 3.45833 15.3694 3.23463C15.1649 2.74458 14.7726 2.35523 14.2788 2.15224ZM12.5 15C14.1695 15 15.5228 13.6569 15.5228 12C15.5228 10.3431 14.1695 9 12.5 9C10.8305 9 9.47716 10.3431 9.47716 12C9.47716 13.6569 10.8305 15 12.5 15Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
Loading…
Reference in New Issue
Block a user