Implement Admin Panel #43
277
cmd/web/handlers_admin.go
Normal file
277
cmd/web/handlers_admin.go
Normal file
@ -0,0 +1,277 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/validator"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/ui/views"
|
||||
)
|
||||
|
||||
func (app *application) getAdminPanelLanding(w http.ResponseWriter, r *http.Request) {
|
||||
websites, err := app.websites.GetCount()
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
users, err := app.users.GetCount()
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
comments, err := app.guestbookComments.GetCount()
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
stats := views.AdminStat{
|
||||
WebsiteCount: websites,
|
||||
UserCount: users,
|
||||
CommentCount: comments,
|
||||
}
|
||||
data := app.newCommonData(r)
|
||||
views.AdminPanelLandingView("Admin Panel", data, stats).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (app *application) getAdminPanelAllUsers(w http.ResponseWriter, r *http.Request) {
|
||||
page := r.URL.Query().Get("page")
|
||||
count := r.URL.Query().Get("count")
|
||||
var pageNum int64 = 1
|
||||
var pageSize int64 = 5
|
||||
var err error
|
||||
if page != "" {
|
||||
pageNum, err = strconv.ParseInt(page, 10, 0)
|
||||
if err != nil {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
if count != "" {
|
||||
pageSize, err = strconv.ParseInt(count, 10, 0)
|
||||
if err != nil {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
users, err := app.users.GetAllPage(pageNum, pageSize)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
total, err := app.users.GetCount()
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
data := app.newCommonData(r)
|
||||
views.AdminPanelUsersView("All Users - Admin", data, users, pageNum, pageSize, total).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (app *application) getAdminPanelUser(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("id")
|
||||
u, err := app.users.Get(slugToShortId(slug))
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrNoRecord) {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
app.serverError(w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
data := app.newCommonData(r)
|
||||
views.AdminPanelUserMgmtView(fmt.Sprintf("User Management - %s", u.Username), data, u).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (app *application) getAdminPanelUserMgmtDetail(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("id")
|
||||
u, err := app.users.Get(slugToShortId(slug))
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrNoRecord) {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
app.serverError(w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
commonData := app.newCommonData(r)
|
||||
views.AdminPanelUserMgmtDetail(commonData.CSRFToken, u).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (app *application) getAdminPanelUserMgmtForm(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("id")
|
||||
u, err := app.users.Get(slugToShortId(slug))
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrNoRecord) {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
app.serverError(w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
var form forms.AdminUserMgmtForm
|
||||
form.Username = u.Username
|
||||
form.Email = u.Email
|
||||
|
||||
data := app.newCommonData(r)
|
||||
views.AdminPanelUserMgmtEditForm(data.CSRFToken, form, u, []models.UserGroupId{}).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (app *application) putAdminPanelUserMgmtForm(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("id")
|
||||
u, err := app.users.Get(slugToShortId(slug))
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrNoRecord) {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
app.serverError(w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
var form forms.AdminUserMgmtForm
|
||||
err = app.decodePostForm(r, &form)
|
||||
if err != nil {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
form.CheckField(validator.NotBlank(form.Username), "admin_username", "This field cannot be blank")
|
||||
form.CheckField(validator.NotBlank(form.Email), "admin_useremail", "This field cannot be blank")
|
||||
form.CheckField(validator.Matches(form.Email, validator.EmailRX), "admin_useremail", "Please provide a valid email address")
|
||||
if !form.Valid() {
|
||||
data := app.newCommonData(r)
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
views.AdminPanelUserMgmtEditForm(data.CSRFToken, form, u, []models.UserGroupId{}).Render(r.Context(), w)
|
||||
return
|
||||
}
|
||||
updatedUser := u
|
||||
updatedUser.Username = form.Username
|
||||
updatedUser.Email = form.Email
|
||||
err = app.users.UpdateUser(updatedUser)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
commonData := app.newCommonData(r)
|
||||
views.AdminPanelUserMgmtDetail(commonData.CSRFToken, updatedUser).Render(r.Context(), w)
|
||||
|
||||
}
|
||||
|
||||
func (app *application) putAdminPanelBanUser(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("id")
|
||||
u, err := app.users.Get(slugToShortId(slug))
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrNoRecord) {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
app.serverError(w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
err = app.users.BanUser(u.ID)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
u.Banned = time.Now()
|
||||
commonData := app.newCommonData(r)
|
||||
views.AdminPanelUserMgmtDetail(commonData.CSRFToken, u).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (app *application) putAdminPanelUnbanUser(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("id")
|
||||
u, err := app.users.Get(slugToShortId(slug))
|
||||
if err != nil {
|
||||
if errors.Is(err, models.ErrNoRecord) {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
app.serverError(w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
err = app.users.UnbanUser(u.ID)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
u.Banned = time.Time{}
|
||||
commonData := app.newCommonData(r)
|
||||
views.AdminPanelUserMgmtDetail(commonData.CSRFToken, u).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (app *application) getAdminPanelWebsites(w http.ResponseWriter, r *http.Request) {
|
||||
page := r.URL.Query().Get("page")
|
||||
count := r.URL.Query().Get("count")
|
||||
var pageNum int64 = 1
|
||||
var pageSize int64 = 5
|
||||
var err error
|
||||
if page != "" {
|
||||
pageNum, err = strconv.ParseInt(page, 10, 0)
|
||||
if err != nil {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
if count != "" {
|
||||
pageSize, err = strconv.ParseInt(count, 10, 0)
|
||||
if err != nil {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
websites, err := app.websites.GetAllPage(pageNum, pageSize)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
total, err := app.websites.GetCount()
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
commonData := app.newCommonData(r)
|
||||
views.AdminPanelAllWebsitesView("All websites", commonData, websites, pageNum, pageSize, total).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (app *application) getAdminPanelWebsiteDetails(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("id")
|
||||
page := r.URL.Query().Get("page")
|
||||
count := r.URL.Query().Get("count")
|
||||
var pageNum int64 = 1
|
||||
var pageSize int64 = 25
|
||||
var err error
|
||||
if page != "" {
|
||||
pageNum, err = strconv.ParseInt(page, 10, 0)
|
||||
if err != nil {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
if count != "" {
|
||||
pageSize, err = strconv.ParseInt(count, 10, 0)
|
||||
if err != nil {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
website, err := app.websites.Get(slugToShortId(slug))
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
total, err := app.guestbookComments.GetAllCount(website.Guestbook.ID)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
comments, err := app.guestbookComments.GetAllPage(website.Guestbook.ID, pageNum, pageSize)
|
||||
if err != nil {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
commonData := app.newCommonData(r)
|
||||
views.AdminPanelWebsiteDetailView(fmt.Sprintf("Admin - %s", website.Name), commonData, website, comments, pageNum, pageSize, total).Render(r.Context(), w)
|
||||
}
|
||||
69
cmd/web/installer.go
Normal file
69
cmd/web/installer.go
Normal file
@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/validator"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/ui"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/ui/views"
|
||||
"github.com/justinas/alice"
|
||||
)
|
||||
|
||||
func (i *appInstaller) installRoutes() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
if i.app.config.environment == "PROD" {
|
||||
mux.Handle("GET /static/", http.FileServerFS(ui.Files))
|
||||
} else {
|
||||
fileServer := http.FileServer(http.Dir("./ui/static/"))
|
||||
mux.Handle("GET /static/", http.StripPrefix("/static", fileServer))
|
||||
}
|
||||
|
||||
mux.HandleFunc("GET /ping", ping)
|
||||
standard := alice.New(i.app.recoverPanic, i.app.logRequest, commonHeaders)
|
||||
mux.Handle("/{$}", standard.ThenFunc(i.getInstallHomepage))
|
||||
mux.Handle("GET /install", standard.ThenFunc(i.getInstallForm))
|
||||
mux.Handle("POST /install", standard.ThenFunc(i.postInstallForm))
|
||||
|
||||
return standard.Then(mux)
|
||||
}
|
||||
|
||||
func (i *appInstaller) getInstallHomepage(w http.ResponseWriter, r *http.Request) {
|
||||
views.InitialInstallView("Installation").Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (i *appInstaller) getInstallForm(w http.ResponseWriter, r *http.Request) {
|
||||
var form forms.InstallForm
|
||||
views.InstallFormView("Installation - Settings", form).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func (i *appInstaller) postInstallForm(w http.ResponseWriter, r *http.Request) {
|
||||
var form forms.InstallForm
|
||||
err := i.app.decodePostForm(r, &form)
|
||||
if err != nil {
|
||||
i.app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
form.CheckField(validator.NotBlank(form.Name), "name", "This field cannot be blank")
|
||||
form.CheckField(validator.NotBlank(form.Email), "email", "This field cannot be blank")
|
||||
form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "This field must be a valid email address")
|
||||
form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank")
|
||||
form.CheckField(validator.MinChars(form.Password, 8), "password", "This field must be at least 8 characters long")
|
||||
if !form.Valid() {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
views.InstallFormView("User Registration", form).Render(r.Context(), w)
|
||||
return
|
||||
}
|
||||
sId := i.app.createShortId()
|
||||
err = i.app.users.Insert(sId, form.Name, form.Email, form.Password, DefaultUserSettings())
|
||||
if err != nil {
|
||||
i.app.serverError(w, r, err)
|
||||
}
|
||||
err = i.app.users.AddUserToGroup(1, models.AdminGroup)
|
||||
if err != nil {
|
||||
i.app.serverError(w, r, err)
|
||||
}
|
||||
views.InstallSuccessView().Render(r.Context(), w)
|
||||
i.srv.Close()
|
||||
}
|
||||
@ -56,6 +56,12 @@ type application struct {
|
||||
timezones []string
|
||||
}
|
||||
|
||||
type appInstaller struct {
|
||||
app *application
|
||||
srv *http.Server
|
||||
installModel models.InstallModelInterface
|
||||
}
|
||||
|
||||
func main() {
|
||||
addr := flag.String("addr", ":3000", "HTTP network address")
|
||||
dsn := flag.String("dsn", "guestbook.db", "data source name")
|
||||
@ -102,6 +108,29 @@ func main() {
|
||||
timezones: getAvailableTimezones(),
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
|
||||
}
|
||||
|
||||
installer := &appInstaller{
|
||||
app: app,
|
||||
installModel: &models.InstallModel{DB: db},
|
||||
}
|
||||
installer.srv = &http.Server{
|
||||
Addr: *addr,
|
||||
Handler: installer.installRoutes(),
|
||||
ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError),
|
||||
TLSConfig: tlsConfig,
|
||||
IdleTimeout: time.Minute,
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
err = runInstaller(installer)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = app.users.InitializeSettingsMap()
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
@ -113,10 +142,6 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: *addr,
|
||||
Handler: app.routes(),
|
||||
@ -273,3 +298,31 @@ func walkTzDir(path string, zones []string) []string {
|
||||
return zones
|
||||
|
||||
}
|
||||
|
||||
func runInstaller(i *appInstaller) error {
|
||||
i.app.logger.Info("Performing migrations")
|
||||
err := i.installModel.SetupDatabase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installed, _ := i.installModel.GetInstalledFlag()
|
||||
if installed {
|
||||
return nil
|
||||
}
|
||||
i.app.logger.Info("App not installed, running installer...")
|
||||
i.app.logger.Info("Starting installation server", slog.Any("addr", i.srv.Addr))
|
||||
if i.app.debug {
|
||||
err = i.srv.ListenAndServeTLS("./tls/cert.pem", "./tls/key.pem")
|
||||
} else {
|
||||
err = i.srv.ListenAndServe()
|
||||
}
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
i.app.logger.Info("Installation complete")
|
||||
err = i.installModel.SetInstalledFlag()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
"github.com/justinas/nosurf"
|
||||
)
|
||||
|
||||
@ -56,6 +58,17 @@ func (app *application) requireAuthentication(next http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func (app *application) requireAdmin(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user := app.getCurrentUser(r)
|
||||
if !slices.Contains(user.Groups, models.AdminGroup) {
|
||||
app.clientError(w, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func noSurf(next http.Handler) http.Handler {
|
||||
csrfHandler := nosurf.New(next)
|
||||
csrfHandler.SetBaseCookie(http.Cookie{
|
||||
@ -84,6 +97,9 @@ func (app *application) authenticate(next http.Handler) http.Handler {
|
||||
app.serverError(w, r, err)
|
||||
return
|
||||
}
|
||||
if !user.Banned.IsZero() {
|
||||
http.Redirect(w, r, "/banned", http.StatusSeeOther)
|
||||
}
|
||||
if exists {
|
||||
ctx := context.WithValue(r.Context(), isAuthenticatedContextKey, true)
|
||||
ctx = context.WithValue(ctx, userNameContextKey, user)
|
||||
|
||||
@ -39,13 +39,11 @@ func (app *application) routes() http.Handler {
|
||||
|
||||
protected := dynamic.Append(app.requireAuthentication)
|
||||
|
||||
// mux.Handle("GET /users", protected.ThenFunc(app.getUsersList))
|
||||
mux.Handle("GET /users/{id}", protected.ThenFunc(app.getUser))
|
||||
mux.Handle("POST /users/logout", protected.ThenFunc(app.postUserLogout))
|
||||
mux.Handle("GET /users/settings", protected.ThenFunc(app.getUserSettings))
|
||||
mux.Handle("PUT /users/settings", protected.ThenFunc(app.putUserSettings))
|
||||
mux.Handle("GET /guestbooks", protected.ThenFunc(app.getAllGuestbooks))
|
||||
|
||||
mux.Handle("GET /websites", protected.ThenFunc(app.getWebsiteList))
|
||||
mux.Handle("GET /websites/create", protected.ThenFunc(app.getWebsiteCreate))
|
||||
mux.Handle("POST /websites/create", protected.ThenFunc(app.postWebsiteCreate))
|
||||
@ -60,5 +58,17 @@ func (app *application) routes() http.Handler {
|
||||
mux.Handle("GET /websites/{id}/dashboard/guestbook/themes", protected.ThenFunc(app.getComingSoon))
|
||||
mux.Handle("GET /websites/{id}/dashboard/guestbook/customize", protected.ThenFunc(app.getComingSoon))
|
||||
|
||||
adminOnly := protected.Append(app.requireAdmin)
|
||||
mux.Handle("GET /admin", adminOnly.ThenFunc(app.getAdminPanelLanding))
|
||||
mux.Handle("GET /admin/users", adminOnly.ThenFunc(app.getAdminPanelAllUsers))
|
||||
mux.Handle("GET /admin/users/{id}", adminOnly.ThenFunc(app.getAdminPanelUser))
|
||||
mux.Handle("GET /admin/users/{id}/edit", adminOnly.ThenFunc(app.getAdminPanelUserMgmtForm))
|
||||
mux.Handle("GET /admin/users/{id}/detail", adminOnly.ThenFunc(app.getAdminPanelUserMgmtDetail))
|
||||
mux.Handle("PUT /admin/users/{id}/edit", adminOnly.ThenFunc(app.putAdminPanelUserMgmtForm))
|
||||
mux.Handle("PUT /admin/users/{id}/ban", adminOnly.ThenFunc(app.putAdminPanelBanUser))
|
||||
mux.Handle("PUT /admin/users/{id}/unban", adminOnly.ThenFunc(app.putAdminPanelUnbanUser))
|
||||
mux.Handle("GET /admin/websites", adminOnly.ThenFunc(app.getAdminPanelWebsites))
|
||||
mux.Handle("GET /admin/websites/{id}", adminOnly.ThenFunc(app.getAdminPanelWebsiteDetails))
|
||||
|
||||
return standard.Then(mux)
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
package forms
|
||||
|
||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/validator"
|
||||
import (
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/validator"
|
||||
)
|
||||
|
||||
type UserRegistrationForm struct {
|
||||
Name string `schema:"username"`
|
||||
@ -25,9 +28,9 @@ type CommentCreateForm struct {
|
||||
}
|
||||
|
||||
type WebsiteCreateForm struct {
|
||||
Name string `schema:"sitename"`
|
||||
SiteUrl string `schema:"siteurl"`
|
||||
AuthorName string `schema:"authorname"`
|
||||
Name string `schema:"ws_name"`
|
||||
SiteUrl string `schema:"ws_url"`
|
||||
AuthorName string `schema:"ws_author"`
|
||||
validator.Validator `schema:"-"`
|
||||
}
|
||||
|
||||
@ -50,3 +53,17 @@ type WebsiteSettingsForm struct {
|
||||
WidgetsEnabled string `schema:"gb_remote"`
|
||||
validator.Validator `schema:"-"`
|
||||
}
|
||||
|
||||
type AdminUserMgmtForm struct {
|
||||
Username string `schema:"admin_username"`
|
||||
Email string `schema:"admin_useremail"`
|
||||
Groups []models.UserGroupId `schema:"admin_usergroups"`
|
||||
validator.Validator `schema:"-"`
|
||||
}
|
||||
|
||||
type InstallForm struct {
|
||||
Name string `schema:"username"`
|
||||
Email string `schema:"email"`
|
||||
Password string `schema:"password"`
|
||||
validator.Validator `schema:"-"`
|
||||
}
|
||||
|
||||
@ -33,10 +33,13 @@ type GuestbookCommentModel struct {
|
||||
type GuestbookCommentModelInterface interface {
|
||||
Insert(shortId uint64, guestbookId, parentId int64, authorName, authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (int64, error)
|
||||
Get(shortId uint64) (GuestbookComment, error)
|
||||
GetCount() (int64, error)
|
||||
GetVisible(guestbookId int64) ([]GuestbookComment, error)
|
||||
GetVisibleSerialized(guestbookId int64) ([]GuestbookCommentSerialized, error)
|
||||
GetDeleted(guestbookId int64) ([]GuestbookComment, error)
|
||||
GetAll(guestbookId int64) ([]GuestbookComment, error)
|
||||
GetAllCount(guestbookId int64) (int64, error)
|
||||
GetAllPage(guestbookId int64, pageNum int64, pageSize int64) ([]GuestbookComment, error)
|
||||
UpdateComment(comment *GuestbookComment) error
|
||||
}
|
||||
|
||||
@ -74,6 +77,20 @@ func (m *GuestbookCommentModel) Get(shortId uint64) (GuestbookComment, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *GuestbookCommentModel) GetCount() (int64, error) {
|
||||
stmt := `SELECT COUNT(*) FROM guestbook_comments WHERE Deleted IS NULL`
|
||||
row := m.DB.QueryRow(stmt)
|
||||
var result int64
|
||||
err := row.Scan(&result)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if err = row.Err(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *GuestbookCommentModel) GetVisible(guestbookId int64) ([]GuestbookComment, error) {
|
||||
stmt := `SELECT Id, ShortId, GuestbookId, ParentId, AuthorName, AuthorEmail, AuthorSite,
|
||||
CommentText, PageUrl, Created, IsPublished
|
||||
@ -180,6 +197,47 @@ func (m *GuestbookCommentModel) GetAll(guestbookId int64) ([]GuestbookComment, e
|
||||
return comments, nil
|
||||
}
|
||||
|
||||
func (m *GuestbookCommentModel) GetAllCount(guestbookId int64) (int64, error) {
|
||||
stmt := `SELECT COUNT(*) FROM guestbook_comments WHERE GuestbookId = ? AND Deleted IS NULL`
|
||||
row := m.DB.QueryRow(stmt, guestbookId)
|
||||
var result int64
|
||||
err := row.Scan(&result)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if err = row.Err(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *GuestbookCommentModel) GetAllPage(guestbookId int64, pageNum int64, pageSize int64) ([]GuestbookComment, error) {
|
||||
stmt := `SELECT Id, ShortId, GuestbookId, ParentId, AuthorName, AuthorEmail, AuthorSite,
|
||||
CommentText, PageUrl, Created, IsPublished
|
||||
FROM guestbook_comments
|
||||
WHERE GuestbookId = ? AND Deleted IS NULL
|
||||
ORDER BY Created DESC
|
||||
LIMIT ? OFFSET ?`
|
||||
rows, err := m.DB.Query(stmt, guestbookId, pageSize, (pageNum-1)*pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var comments []GuestbookComment
|
||||
for rows.Next() {
|
||||
var c GuestbookComment
|
||||
err = rows.Scan(&c.ID, &c.ShortId, &c.GuestbookId, &c.ParentId, &c.AuthorName, &c.AuthorEmail, &c.AuthorSite,
|
||||
&c.CommentText, &c.PageUrl, &c.Created, &c.IsPublished)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
comments = append(comments, c)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return comments, nil
|
||||
}
|
||||
|
||||
func (m *GuestbookCommentModel) UpdateComment(comment *GuestbookComment) error {
|
||||
stmt := `UPDATE guestbook_comments
|
||||
SET CommentText = ?,
|
||||
|
||||
62
internal/models/install.go
Normal file
62
internal/models/install.go
Normal file
@ -0,0 +1,62 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type InstallModel struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
type InstallModelInterface interface {
|
||||
SetupDatabase() error
|
||||
SetInstalledFlag() error
|
||||
GetInstalledFlag() (bool, error)
|
||||
}
|
||||
|
||||
func (m *InstallModel) SetupDatabase() error {
|
||||
driver, err := sqlite3.WithInstance(m.DB, &sqlite3.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mi, err := migrate.NewWithDatabaseInstance("file://migrations", "sqlite", driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mi.Up()
|
||||
if err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *InstallModel) SetInstalledFlag() error {
|
||||
stmt := `INSERT INTO installed (InstallDate) VALUES (?)`
|
||||
_, err := m.DB.Exec(stmt, time.Now().UTC())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *InstallModel) GetInstalledFlag() (bool, error) {
|
||||
stmt := `SELECT InstallDate FROM installed`
|
||||
row := m.DB.QueryRow(stmt)
|
||||
var d time.Time
|
||||
err := row.Scan(&d)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@ -13,7 +13,6 @@ var mockUser = models.User{
|
||||
Username: "tester",
|
||||
Email: "test@example.com",
|
||||
Deleted: false,
|
||||
IsBanned: false,
|
||||
Created: time.Now(),
|
||||
Settings: mockUserSettings,
|
||||
}
|
||||
@ -26,6 +25,31 @@ type UserModel struct {
|
||||
Settings map[string]models.Setting
|
||||
}
|
||||
|
||||
func (m *UserModel) AddUserToGroup(userId int64, groupId models.UserGroupId) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *UserModel) BanUser(userId int64) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *UserModel) UpdateUser(u models.User) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *UserModel) UpdatePassword(userId int64, password string) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *UserModel) Delete(userId int64) error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *UserModel) InitializeSettingsMap() error {
|
||||
return nil
|
||||
}
|
||||
@ -120,3 +144,7 @@ func (m *UserModel) UpdateSubject(userId int64, subject string) error {
|
||||
}
|
||||
return errors.New("invalid")
|
||||
}
|
||||
|
||||
func (m *UserModel) GetNumberOfUsers() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
@ -56,6 +56,14 @@ func (m *WebsiteModel) GetAllUser(userId int64) ([]models.Website, error) {
|
||||
return []models.Website{mockWebsite}, nil
|
||||
}
|
||||
|
||||
func GetCountUser(userId int64) (int64, error) {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) GetAllUserPage(userId int64, pageNum int64) ([]models.Website, error) {
|
||||
return []models.Website{mockWebsite}, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) GetById(id int64) (models.Website, error) {
|
||||
switch id {
|
||||
case 1:
|
||||
@ -69,6 +77,14 @@ func (m *WebsiteModel) GetAll() ([]models.Website, error) {
|
||||
return []models.Website{mockWebsite}, nil
|
||||
}
|
||||
|
||||
func GetCount() (int64, error) {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) GetAllPage(pageNum int64) ([]models.Website, error) {
|
||||
return []models.Website{mockWebsite}, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) Update(w models.Website) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -10,6 +10,13 @@ import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type UserGroupId int64
|
||||
|
||||
const (
|
||||
AdminGroup UserGroupId = 1
|
||||
UserGroup UserGroupId = 2
|
||||
)
|
||||
|
||||
type UserSettings struct {
|
||||
LocalTimezone *time.Location
|
||||
}
|
||||
@ -24,10 +31,11 @@ type User struct {
|
||||
Username string
|
||||
Email string
|
||||
Deleted bool
|
||||
IsBanned bool
|
||||
HashedPassword []byte
|
||||
Created time.Time
|
||||
Banned time.Time
|
||||
Settings UserSettings
|
||||
Groups []UserGroupId
|
||||
}
|
||||
|
||||
type UserModel struct {
|
||||
@ -48,15 +56,24 @@ type UserModelInterface interface {
|
||||
Insert(shortId uint64, username string, email string, password string, settings UserSettings) error
|
||||
InsertWithoutPassword(shortId uint64, username string, email string, subject string, settings UserSettings) (int64, error)
|
||||
Get(shortId uint64) (User, error)
|
||||
GetCount() (int64, error)
|
||||
GetById(id int64) (User, error)
|
||||
GetByEmail(email string) (int64, error)
|
||||
GetBySubject(subject string) (int64, error)
|
||||
GetAll() ([]User, error)
|
||||
GetAllPage(pageNum, pageSize int64) ([]User, error)
|
||||
Authenticate(email, password string) (int64, error)
|
||||
Exists(id int64) (bool, error)
|
||||
UpdateUserSettings(userId int64, settings UserSettings) error
|
||||
UpdateSetting(userId int64, setting Setting, value string) error
|
||||
UpdateSubject(userId int64, subject string) error
|
||||
GetNumberOfUsers() int
|
||||
AddUserToGroup(userId int64, groupId UserGroupId) error
|
||||
BanUser(userId int64) error
|
||||
UnbanUser(userId int64) error
|
||||
UpdateUser(u User) error
|
||||
UpdatePassword(userId int64, password string) error
|
||||
Delete(userId int64) error
|
||||
}
|
||||
|
||||
func (m *UserModel) InitializeSettingsMap() error {
|
||||
@ -96,8 +113,8 @@ func (m *UserModel) Insert(shortId uint64, username string, email string, passwo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stmt := `INSERT INTO users (ShortId, Username, Email, IsBanned, HashedPassword, Created)
|
||||
VALUES (?, ?, ?, FALSE, ?, ?)`
|
||||
stmt := `INSERT INTO users (ShortId, Username, Email, HashedPassword, Created)
|
||||
VALUES (?, ?, ?, ?, ?)`
|
||||
tx, err := m.DB.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -107,7 +124,8 @@ func (m *UserModel) Insert(shortId uint64, username string, email string, passwo
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
return err
|
||||
}
|
||||
if sqliteError, ok := err.(sqlite3.Error); ok {
|
||||
var sqliteError sqlite3.Error
|
||||
if errors.As(err, &sqliteError) {
|
||||
if sqliteError.ExtendedCode == 2067 && strings.Contains(sqliteError.Error(), "Email") {
|
||||
return ErrDuplicateEmail
|
||||
}
|
||||
@ -130,17 +148,20 @@ func (m *UserModel) Insert(shortId uint64, username string, email string, passwo
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = m.addUserToGroup(tx, id, UserGroup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) InsertWithoutPassword(shortId uint64, username string, email string, subject string, settings UserSettings) (int64, error) {
|
||||
stmt := `INSERT INTO users (ShortId, Username, Email, IsBanned, OIDCSubject, Created)
|
||||
VALUES (?, ?, ?, FALSE, ?, ?)`
|
||||
stmt := `INSERT INTO users (ShortId, Username, Email, OIDCSubject, Created)
|
||||
VALUES (?, ?, ?, ?, ?)`
|
||||
tx, err := m.DB.Begin()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
@ -150,7 +171,8 @@ func (m *UserModel) InsertWithoutPassword(shortId uint64, username string, email
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
return -1, err
|
||||
}
|
||||
if sqliteError, ok := err.(sqlite3.Error); ok {
|
||||
var sqliteError sqlite3.Error
|
||||
if errors.As(err, &sqliteError) {
|
||||
if sqliteError.ExtendedCode == 2067 && strings.Contains(sqliteError.Error(), "Email") {
|
||||
return -1, ErrDuplicateEmail
|
||||
}
|
||||
@ -173,6 +195,7 @@ func (m *UserModel) InsertWithoutPassword(shortId uint64, username string, email
|
||||
}
|
||||
return -1, err
|
||||
}
|
||||
err = m.addUserToGroup(tx, id, UserGroup)
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
@ -182,14 +205,15 @@ func (m *UserModel) InsertWithoutPassword(shortId uint64, username string, email
|
||||
}
|
||||
|
||||
func (m *UserModel) Get(shortId uint64) (User, error) {
|
||||
stmt := `SELECT Id, ShortId, Username, Email, Created FROM users WHERE ShortId = ? AND Deleted IS NULL`
|
||||
stmt := `SELECT Id, ShortId, Username, Email, Created, Banned FROM users WHERE ShortId = ? AND Deleted IS NULL`
|
||||
tx, err := m.DB.Begin()
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
row := tx.QueryRow(stmt, shortId)
|
||||
var u User
|
||||
err = row.Scan(&u.ID, &u.ShortId, &u.Username, &u.Email, &u.Created)
|
||||
var b sql.NullTime
|
||||
err = row.Scan(&u.ID, &u.ShortId, &u.Username, &u.Email, &u.Created, &b)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
return User{}, err
|
||||
@ -199,6 +223,9 @@ func (m *UserModel) Get(shortId uint64) (User, error) {
|
||||
}
|
||||
return User{}, err
|
||||
}
|
||||
if b.Valid {
|
||||
u.Banned = b.Time
|
||||
}
|
||||
settings, err := m.getSettings(tx, u.ID)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
@ -207,6 +234,14 @@ func (m *UserModel) Get(shortId uint64) (User, error) {
|
||||
return User{}, err
|
||||
}
|
||||
u.Settings = settings
|
||||
groups, err := m.getGroups(tx, u.ID)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
return User{}, err
|
||||
}
|
||||
return User{}, err
|
||||
}
|
||||
u.Groups = groups
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
@ -214,15 +249,30 @@ func (m *UserModel) Get(shortId uint64) (User, error) {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (m *UserModel) GetCount() (int64, error) {
|
||||
stmt := `SELECT COUNT(*) FROM users WHERE Deleted IS NULL`
|
||||
row := m.DB.QueryRow(stmt)
|
||||
var result int64
|
||||
err := row.Scan(&result)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if err = row.Err(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *UserModel) GetById(id int64) (User, error) {
|
||||
stmt := `SELECT Id, ShortId, Username, Email, Created FROM users WHERE Id = ? AND Deleted IS NULL`
|
||||
stmt := `SELECT Id, ShortId, Username, Email, Created, Banned FROM users WHERE Id = ? AND Deleted IS NULL`
|
||||
tx, err := m.DB.Begin()
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
row := tx.QueryRow(stmt, id)
|
||||
var u User
|
||||
err = row.Scan(&u.ID, &u.ShortId, &u.Username, &u.Email, &u.Created)
|
||||
var b sql.NullTime
|
||||
err = row.Scan(&u.ID, &u.ShortId, &u.Username, &u.Email, &u.Created, &b)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
return User{}, err
|
||||
@ -232,6 +282,9 @@ func (m *UserModel) GetById(id int64) (User, error) {
|
||||
}
|
||||
return User{}, err
|
||||
}
|
||||
if b.Valid {
|
||||
u.Banned = b.Time
|
||||
}
|
||||
settings, err := m.getSettings(tx, u.ID)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
@ -240,6 +293,14 @@ func (m *UserModel) GetById(id int64) (User, error) {
|
||||
return User{}, err
|
||||
}
|
||||
u.Settings = settings
|
||||
groups, err := m.getGroups(tx, u.ID)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
return User{}, err
|
||||
}
|
||||
return User{}, err
|
||||
}
|
||||
u.Groups = groups
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
@ -268,6 +329,27 @@ func (m *UserModel) GetAll() ([]User, error) {
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (m *UserModel) GetAllPage(pageNum, pageSize int64) ([]User, error) {
|
||||
stmt := `SELECT Id, ShortId, Username, Email, Created FROM users WHERE DELETED IS NULL LIMIT ? OFFSET ?`
|
||||
rows, err := m.DB.Query(stmt, pageSize, (pageNum-1)*pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var users []User
|
||||
for rows.Next() {
|
||||
var u User
|
||||
err = rows.Scan(&u.ID, &u.ShortId, &u.Username, &u.Email, &u.Created)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (m *UserModel) Authenticate(email, password string) (int64, error) {
|
||||
var id int64
|
||||
var hashedPassword []byte
|
||||
@ -400,3 +482,115 @@ func (m *UserModel) UpdateSetting(userId int64, setting Setting, value string) e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) GetNumberOfUsers() int {
|
||||
stmt := `SELECT COUNT(Id) FROM users WHERE Deleted IS NULL;`
|
||||
row := m.DB.QueryRow(stmt)
|
||||
var count int
|
||||
err := row.Scan(&count)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
count = 0
|
||||
} else {
|
||||
count = 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (m *UserModel) AddUserToGroup(userId int64, groupId UserGroupId) error {
|
||||
stmt := `INSERT OR IGNORE INTO users_groups (UserId, GroupId) VALUES (?, ?)`
|
||||
_, err := m.DB.Exec(stmt, userId, groupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) RemoveUserFromGroup(userId int64, groupId UserGroupId) error {
|
||||
stmt := `DELETE FROM users_groups WHERE UserId = ? AND GroupId = ?`
|
||||
_, err := m.DB.Exec(stmt, userId, groupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) BanUser(userId int64) error {
|
||||
stmt := `UPDATE users SET Banned=? WHERE Id=?`
|
||||
_, err := m.DB.Exec(stmt, time.Now().UTC().Format(time.RFC3339), userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) UnbanUser(userId int64) error {
|
||||
stmt := `UPDATE users SET Banned=NULL WHERE Id=?`
|
||||
_, err := m.DB.Exec(stmt, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) UpdateUser(u User) error {
|
||||
stmt := `UPDATE users SET Email=?, Username=? WHERE Id=?`
|
||||
_, err := m.DB.Exec(stmt, u.Email, u.Username, u.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) UpdatePassword(userId int64, password string) error {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stmt := `UPDATE users SET HashedPassword=? WHERE Id=?`
|
||||
_, err = m.DB.Exec(stmt, hashedPassword, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) Delete(userId int64) error {
|
||||
stmt := `UPDATE users SET Deleted=? WHERE Id=?`
|
||||
_, err := m.DB.Exec(stmt, time.Now().UTC().Format(time.RFC3339), userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) addUserToGroup(tx *sql.Tx, userId int64, groupId UserGroupId) error {
|
||||
stmt := `INSERT INTO users_groups (UserId, GroupId) VALUES (?, ?)`
|
||||
_, err := tx.Exec(stmt, userId, groupId)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UserModel) getGroups(tx *sql.Tx, userId int64) ([]UserGroupId, error) {
|
||||
stmt := `SELECT DISTINCT GroupId FROM users_groups WHERE UserId = ?`
|
||||
rows, err := tx.Query(stmt, userId)
|
||||
result := make([]UserGroupId, 0, 10)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
for rows.Next() {
|
||||
var g UserGroupId
|
||||
err = rows.Scan(&g)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = append(result, g)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@ -95,7 +95,11 @@ type WebsiteModelInterface interface {
|
||||
Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error)
|
||||
Get(shortId uint64) (Website, error)
|
||||
GetAllUser(userId int64) ([]Website, error)
|
||||
GetCountUser(userId int64) (int64, error)
|
||||
GetAllUserPage(userId int64, pageNum int64, pageSize int64) ([]Website, error)
|
||||
GetAll() ([]Website, error)
|
||||
GetCount() (int64, error)
|
||||
GetAllPage(pageNum int64, pageSize int64) ([]Website, error)
|
||||
Update(w Website) error
|
||||
InitializeSettingsMap() error
|
||||
UpdateGuestbookSettings(guestbookId int64, settings GuestbookSettings) error
|
||||
@ -270,6 +274,51 @@ func (m *WebsiteModel) GetAllUser(userId int64) ([]Website, error) {
|
||||
return websites, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) GetCountUser(userId int64) (int64, error) {
|
||||
stmt := `SELECT COUNT(*) FROM websites WHERE UserId = ? AND Deleted IS NULL`
|
||||
row := m.DB.QueryRow(stmt, userId)
|
||||
var result int64
|
||||
err := row.Scan(&result)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if err = row.Err(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) GetAllUserPage(userId int64, pageNum int64, pageSize int64) ([]Website, error) {
|
||||
stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created,
|
||||
g.Id, g.ShortId, g.Created, g.IsActive
|
||||
FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId
|
||||
WHERE w.UserId = ? AND w.Deleted IS NULL
|
||||
LIMIT ? OFFSET ?`
|
||||
rows, err := m.DB.Query(stmt, userId, pageSize, (pageNum-1)*pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var websites []Website
|
||||
for rows.Next() {
|
||||
var w Website
|
||||
var u string
|
||||
err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created,
|
||||
&w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w.Url, err = url.Parse(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
websites = append(websites, w)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return websites, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) GetAll() ([]Website, error) {
|
||||
stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created,
|
||||
g.Id, g.ShortId, g.Created, g.IsActive
|
||||
@ -299,6 +348,50 @@ func (m *WebsiteModel) GetAll() ([]Website, error) {
|
||||
return websites, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) GetCount() (int64, error) {
|
||||
stmt := `SELECT COUNT(*) FROM websites WHERE Deleted IS NULL`
|
||||
row := m.DB.QueryRow(stmt)
|
||||
var result int64
|
||||
err := row.Scan(&result)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if err = row.Err(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) GetAllPage(pageNum int64, pageSize int64) ([]Website, error) {
|
||||
stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created,
|
||||
g.Id, g.ShortId, g.Created, g.IsActive
|
||||
FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId WHERE w.Deleted IS NULL
|
||||
LIMIT ? OFFSET ?`
|
||||
rows, err := m.DB.Query(stmt, pageSize, (pageNum-1)*pageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var websites []Website
|
||||
for rows.Next() {
|
||||
var w Website
|
||||
var u string
|
||||
err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created,
|
||||
&w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w.Url, err = url.Parse(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
websites = append(websites, w)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return websites, nil
|
||||
}
|
||||
|
||||
func (m *WebsiteModel) Update(w Website) error {
|
||||
stmt := `UPDATE websites SET Name = ?, SiteUrl = ?, AuthorName = ? WHERE ID = ?`
|
||||
r, err := m.DB.Exec(stmt, w.Name, w.Url.String(), w.AuthorName, w.ID)
|
||||
|
||||
2
migrations/000008_add_user_groups.down.sql
Normal file
2
migrations/000008_add_user_groups.down.sql
Normal file
@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS users_groups;
|
||||
DROP TABLE IF EXISTS groups;
|
||||
20
migrations/000008_add_user_groups.up.sql
Normal file
20
migrations/000008_add_user_groups.up.sql
Normal file
@ -0,0 +1,20 @@
|
||||
CREATE TABLE IF NOT EXISTS groups (
|
||||
Id integer PRIMARY KEY NOT NULL,
|
||||
Description varchar(256)
|
||||
);
|
||||
|
||||
INSERT INTO groups (Id, Description)
|
||||
VALUES (1, 'admin'),(2, 'user');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users_groups (
|
||||
Id integer primary key autoincrement,
|
||||
UserId integer NOT NULL,
|
||||
GroupId integer NOT NULL,
|
||||
FOREIGN KEY (UserId) REFERENCES users(Id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT,
|
||||
FOREIGN KEY (GroupId) REFERENCES groups(Id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE RESTRICT
|
||||
UNIQUE(UserId, GroupId)
|
||||
);
|
||||
2
migrations/000009_user_ban_date.down.sql
Normal file
2
migrations/000009_user_ban_date.down.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE users DROP COLUMN Banned;
|
||||
ALTER TABLE users ADD COLUMN IsBanned boolean NOT NULL DEFAULT false;
|
||||
2
migrations/000009_user_ban_date.up.sql
Normal file
2
migrations/000009_user_ban_date.up.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE users DROP COLUMN IsBanned;
|
||||
ALTER TABLE users ADD COLUMN Banned datetime NULL;
|
||||
1
migrations/000010_installation.down.sql
Normal file
1
migrations/000010_installation.down.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS installed;
|
||||
5
migrations/000010_installation.up.sql
Normal file
5
migrations/000010_installation.up.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE installed (
|
||||
Id integer primary key autoincrement,
|
||||
InstallDate datetime NOT NULL,
|
||||
InstallVersion varchar(100) NULL
|
||||
);
|
||||
5
migrations/000011_create_indices.down.sql
Normal file
5
migrations/000011_create_indices.down.sql
Normal file
@ -0,0 +1,5 @@
|
||||
DROP INDEX websites_users;
|
||||
DROP INDEX guestbooks_websites;
|
||||
DROP INDEX comments_guestbooks;
|
||||
DROP INDEX web_settings_websites;
|
||||
DROP INDEX user_settings_users;
|
||||
5
migrations/000011_create_indices.up.sql
Normal file
5
migrations/000011_create_indices.up.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE INDEX websites_users ON websites(UserId);
|
||||
CREATE INDEX guestbooks_websites ON guestbooks(WebsiteId);
|
||||
CREATE INDEX comments_guestbooks ON guestbook_comments(GuestbookId);
|
||||
CREATE INDEX web_settings_websites ON guestbook_settings(GuestbookId);
|
||||
CREATE INDEX user_settings_users ON user_settings(UserId);
|
||||
@ -319,6 +319,7 @@ label {
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="url"],
|
||||
input[type="password"],
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
@ -708,6 +709,54 @@ ul#websites li {
|
||||
margin-top: var(--space-2xl);
|
||||
}
|
||||
|
||||
/* Admin Panel */
|
||||
.admin-section {
|
||||
background-color: var(--color-surface);
|
||||
padding: var(--space-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--color-border);
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.admin-section table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.admin-section th {
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.admin-section td {
|
||||
padding: var(--space-sm);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.admin-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.admin-info {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.pagination-btn {
|
||||
background-color: var(--color-surface);
|
||||
padding: var(--space-sm);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--color-border);
|
||||
margin: var(--space-xs);
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
hr {
|
||||
border: none;
|
||||
|
||||
277
ui/views/admin.templ
Normal file
277
ui/views/admin.templ
Normal file
@ -0,0 +1,277 @@
|
||||
package views
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AdminStat struct {
|
||||
WebsiteCount int64
|
||||
UserCount int64
|
||||
CommentCount int64
|
||||
}
|
||||
|
||||
templ adminBase(title string, data CommonData) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{ title } - webweav.ing</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<meta name="htmx-config" content={ `{"includeIndicatorStyles":false}` }/>
|
||||
<link href="/static/css/style.css" rel="stylesheet"/>
|
||||
<link href="/static/fontawesome/css/fontawesome.css" rel="stylesheet"/>
|
||||
<link href="/static/fontawesome/css/solid.css" rel="stylesheet"/>
|
||||
<script src="/static/js/htmx.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a href="/">Back to webweav.ing</a>
|
||||
</header>
|
||||
<main>
|
||||
{ children... }
|
||||
</main>
|
||||
@commonFooter()
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
||||
templ adminSidebar() {
|
||||
<nav aria-label="Admin panel navigation">
|
||||
<div>
|
||||
<section>
|
||||
<h3>Administration</h3>
|
||||
<ul role="list">
|
||||
<li><a href="/admin">Dashboard</a></li>
|
||||
<li><a href="/admin/users">Users</a></li>
|
||||
<li><a href="/admin/websites">Websites</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</nav>
|
||||
}
|
||||
|
||||
templ AdminPanelLandingView(title string, data CommonData, stats AdminStat) {
|
||||
@adminBase(title, data) {
|
||||
<div id="dashboard">
|
||||
@adminSidebar()
|
||||
<div>
|
||||
<section class="admin-section">
|
||||
<h1>{ title }</h1>
|
||||
<p>Welcome to the admin panel</p>
|
||||
</section>
|
||||
<section class="admin-flex">
|
||||
<section class="admin-section admin-info">
|
||||
<h2>Users</h2>
|
||||
<p>{ fmt.Sprintf("%d", stats.UserCount) }</p>
|
||||
</section>
|
||||
<section class="admin-section admin-info">
|
||||
<h2>Websites</h2>
|
||||
<p>{ fmt.Sprintf("%d", stats.WebsiteCount) }</p>
|
||||
</section>
|
||||
<section class="admin-section admin-info">
|
||||
<h2>Comments</h2>
|
||||
<p>{ fmt.Sprintf("%d", stats.CommentCount) }</p>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
templ AdminPanelUsersView(title string, data CommonData, users []models.User, pageNum, pageSize, total int64) {
|
||||
@adminBase(title, data) {
|
||||
<div id="dashboard">
|
||||
@adminSidebar()
|
||||
<div>
|
||||
<section class="admin-section">
|
||||
<table>
|
||||
<th>Username</th>
|
||||
<th>Joined</th>
|
||||
<th>Email</th>
|
||||
for _, u := range users {
|
||||
<tr>
|
||||
{{ url := fmt.Sprintf("/admin/users/%s", shortIdToSlug(u.ShortId)) }}
|
||||
<td><a href={ templ.URL(url) }>{ u.Username }</a></td>
|
||||
<td>{ u.Created.Format(time.RFC3339) }</td>
|
||||
<td>{ u.Email }</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</section>
|
||||
@pagination("/admin/users", pageNum, total, pageSize)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
templ AdminPanelUserMgmtDetail(csrfToken string, user models.User) {
|
||||
<div id="user-info">
|
||||
<form>
|
||||
<input type="hidden" name="csrf_token" value={ csrfToken }/>
|
||||
<section>
|
||||
<h3>User Info</h3>
|
||||
<div>
|
||||
<h5>Username</h5>
|
||||
<p>{ user.Username }</p>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Email</h5>
|
||||
<p>{ user.Email }</p>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Joined</h5>
|
||||
<p>{ user.Created.Format(time.RFC3339) }</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Groups</h3>
|
||||
<ul>
|
||||
for _, g := range user.Groups {
|
||||
<li>{ fmt.Sprintf("%s", getGroupName(g)) }</li>
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Actions</h3>
|
||||
{{ getFormUrl := fmt.Sprintf("/admin/users/%s/edit", shortIdToSlug(user.ShortId)) }}
|
||||
{{ putBanUrl := fmt.Sprintf("/admin/users/%s/ban", shortIdToSlug(user.ShortId)) }}
|
||||
{{ putUnbanUrl := fmt.Sprintf("/admin/users/%s/unban", shortIdToSlug(user.ShortId)) }}
|
||||
{{ deleteUrl := fmt.Sprintf("/admin/users/%s", shortIdToSlug(user.ShortId)) }}
|
||||
<button type="button" hx-get={ getFormUrl } hx-target="#user-info">Edit</button>
|
||||
if user.ID != 1 {
|
||||
if user.Banned.IsZero() {
|
||||
<button type="button" hx-put={ putBanUrl } hx-confirm="Are you sure you want to ban this user?" hx-target="#user-info" class="danger">Ban</button>
|
||||
} else {
|
||||
<button type="button" hx-put={ putUnbanUrl } hx-confirm="Are you sure you want to unban this user?" hx-target="#user-info">Unban</button>
|
||||
}
|
||||
<button type="button" hx-delete={ deleteUrl } hx-confirm="Are you sure you want to delete this user? This is irreversible" class="danger">Delete</button>
|
||||
}
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ AdminPanelUserMgmtView(title string, data CommonData, user models.User) {
|
||||
@adminBase(title, data) {
|
||||
<div id="dashboard">
|
||||
@adminSidebar()
|
||||
@AdminPanelUserMgmtDetail(data.CSRFToken, user)
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
templ AdminPanelUserMgmtEditForm(csrfToken string, form forms.AdminUserMgmtForm, user models.User, groups []models.UserGroupId) {
|
||||
<div id="user-info">
|
||||
<form>
|
||||
<input type="hidden" name="csrf_token" value={ csrfToken }/>
|
||||
<fieldset>
|
||||
<h3>User Info</h3>
|
||||
<div>
|
||||
<h5>Username</h5>
|
||||
<input type="text" name="admin_username" id="username" value={ form.Username } required/>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Email</h5>
|
||||
<input type="text" name="admin_useremail" id="useremail" value={ form.Email } required/>
|
||||
</div>
|
||||
<div>
|
||||
<h5>Joined</h5>
|
||||
<p>{ user.Created.Format(time.RFC3339) }</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
<section>
|
||||
<fieldset>
|
||||
<h3>Groups</h3>
|
||||
{{ isAdmin := slices.Contains(user.Groups, models.AdminGroup) }}
|
||||
<input type="checkbox" name="admin_usergroup_admin" id="usergroup_admin" checked?={ isAdmin } disabled?={ user.ID == 1 }/>
|
||||
<label for="usergroup_admin">Administrator</label>
|
||||
<input type="checkbox" name="admin_usergroup_user" id="usergroup_user" checked disabled/>
|
||||
<label for="usergroup_user">User</label>
|
||||
</fieldset>
|
||||
</section>
|
||||
<section>
|
||||
<h3>Actions</h3>
|
||||
{{ putFormUrl := fmt.Sprintf("/admin/users/%s/edit", shortIdToSlug(user.ShortId)) }}
|
||||
{{ getDetailUrl := fmt.Sprintf("/admin/users/%s/detail", shortIdToSlug(user.ShortId)) }}
|
||||
<button type="button" hx-put={ putFormUrl } hx-target="#user-info">Save</button>
|
||||
<button type="reset" hx-get={ getDetailUrl } hx-target="#user-info">Cancel</button>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ AdminPanelAllWebsitesView(title string, data CommonData, websites []models.Website, pageNum, pageCount, total int64) {
|
||||
@adminBase(title, data) {
|
||||
<div id="dashboard">
|
||||
@adminSidebar()
|
||||
<div>
|
||||
<section class="admin-section">
|
||||
<table>
|
||||
<th>Site Name</th>
|
||||
<th>Owner</th>
|
||||
<th>URL</th>
|
||||
<th>Created</th>
|
||||
<th>Guestbook</th>
|
||||
for _, w := range websites {
|
||||
<tr>
|
||||
{{ detailUrl := fmt.Sprintf("/admin/websites/%s", shortIdToSlug(w.ShortId)) }}
|
||||
<td><a href={ templ.SafeURL(detailUrl) }>{ w.Name }</a></td>
|
||||
<td>{ w.AuthorName }</td>
|
||||
<td><a href={ templ.SafeURL(w.Url.String()) }>{ w.Url.String() }</a></td>
|
||||
<td>{ w.Created.Format(time.RFC1123) }</td>
|
||||
{{ gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId)) }}
|
||||
<td><a href={ templ.SafeURL(gbUrl) }>View</a></td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</section>
|
||||
@pagination("/admin/websites", pageNum, total, pageCount)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
func truncateComment(s string, n int) string {
|
||||
words := strings.Fields(s)
|
||||
if len(words) < n {
|
||||
return s
|
||||
}
|
||||
truncated := words[:n]
|
||||
truncated = append(truncated, "...")
|
||||
return strings.Join(truncated, " ")
|
||||
}
|
||||
|
||||
templ AdminPanelWebsiteDetailView(title string, data CommonData, website models.Website, comments []models.GuestbookComment, pageNum, pageAmount, total int64) {
|
||||
@adminBase(title, data) {
|
||||
<div id="dashboard">
|
||||
@adminSidebar()
|
||||
<div>
|
||||
<section class="admin-section">
|
||||
<table>
|
||||
<th>Author</th>
|
||||
<th>Created</th>
|
||||
<th>Email</th>
|
||||
<th>Homepage</th>
|
||||
<th>Comment</th>
|
||||
for _, c := range comments {
|
||||
<tr>
|
||||
<td>{ c.AuthorName }</td>
|
||||
<td>{ c.Created.Format(time.RFC1123) }</td>
|
||||
<td>{ c.AuthorEmail }</td>
|
||||
<td>{ c.AuthorSite }</td>
|
||||
<td>{ truncateComment(c.CommentText, 15) }</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</section>
|
||||
{{ url := fmt.Sprintf("/admin/websites/%s", shortIdToSlug(website.ShortId)) }}
|
||||
@pagination(url, pageNum, total, pageAmount)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
1048
ui/views/admin_templ.go
Normal file
1048
ui/views/admin_templ.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,13 @@
|
||||
package views
|
||||
|
||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
import "strconv"
|
||||
import "fmt"
|
||||
import "strings"
|
||||
import (
|
||||
"fmt"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CommonData struct {
|
||||
CurrentYear int
|
||||
@ -33,6 +37,16 @@ func externalUrl(url string) string {
|
||||
return url
|
||||
}
|
||||
|
||||
func getGroupName(id models.UserGroupId) string {
|
||||
switch id {
|
||||
case 1:
|
||||
return "Admin"
|
||||
case 2:
|
||||
return "User"
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
templ commonHeader() {
|
||||
<header>
|
||||
<h1><a href="/">webweav.ing</a></h1>
|
||||
@ -54,6 +68,9 @@ templ topNav(data CommonData) {
|
||||
<li><a href="/guestbooks">All Guestbooks</a></li>
|
||||
<li><a href="/websites">My Websites</a></li>
|
||||
<li><a href="/users/settings">Settings</a></li>
|
||||
if slices.Contains(data.CurrentUser.Groups, models.AdminGroup) {
|
||||
<li><a href="/admin">Admin Panel</a></li>
|
||||
}
|
||||
<li><a href="#" hx-post="/users/logout" hx-headers={ hxHeaders }>Logout</a></li>
|
||||
} else {
|
||||
if data.LocalAuthEnabled {
|
||||
@ -101,3 +118,35 @@ templ base(title string, data CommonData) {
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
||||
templ pagination(baseUrl string, currentPage int64, recordsAmount int64, recordsPerPage int64) {
|
||||
{{ totalPages := int64(math.Ceil(float64(recordsAmount) / float64(recordsPerPage))) }}
|
||||
<div class="pagination">
|
||||
<span class="pagination-btn">
|
||||
if currentPage > 1 {
|
||||
{{ url := fmt.Sprintf("%s?page=%d&count=%d", baseUrl, currentPage-1, recordsPerPage) }}
|
||||
<a href={ templ.URL(url) }>Prev</a>
|
||||
} else {
|
||||
Prev
|
||||
}
|
||||
</span>
|
||||
for i := range totalPages {
|
||||
<span class="pagination-btn">
|
||||
{{ url := fmt.Sprintf("%s?page=%d&count=%d", baseUrl, i+1, recordsPerPage) }}
|
||||
if i+1 == currentPage {
|
||||
{ fmt.Sprintf("%d",i+1) }
|
||||
} else {
|
||||
<a href={ templ.URL(url) }>{ fmt.Sprintf("%d",i+1) }</a>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
<span class="pagination-btn">
|
||||
if currentPage < totalPages {
|
||||
{{ url := fmt.Sprintf("%s?page=%d&count=%d", baseUrl, currentPage+1, recordsPerPage) }}
|
||||
<a href={ templ.URL(url) }>Next</a>
|
||||
} else {
|
||||
Next
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -8,10 +8,14 @@ package views
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
import "strconv"
|
||||
import "fmt"
|
||||
import "strings"
|
||||
import (
|
||||
"fmt"
|
||||
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CommonData struct {
|
||||
CurrentYear int
|
||||
@ -41,6 +45,16 @@ func externalUrl(url string) string {
|
||||
return url
|
||||
}
|
||||
|
||||
func getGroupName(id models.UserGroupId) string {
|
||||
switch id {
|
||||
case 1:
|
||||
return "Admin"
|
||||
case 2:
|
||||
return "User"
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
func commonHeader() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
@ -104,7 +118,7 @@ func topNav(data CommonData) templ.Component {
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.CurrentUser.Username)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 48, Col: 41}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 62, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -116,36 +130,46 @@ func topNav(data CommonData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if data.IsAuthenticated {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<li><a href=\"/guestbooks\">All Guestbooks</a></li><li><a href=\"/websites\">My Websites</a></li><li><a href=\"/users/settings\">Settings</a></li><li><a href=\"#\" hx-post=\"/users/logout\" hx-headers=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<li><a href=\"/guestbooks\">All Guestbooks</a></li><li><a href=\"/websites\">My Websites</a></li><li><a href=\"/users/settings\">Settings</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if slices.Contains(data.CurrentUser.Groups, models.AdminGroup) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<li><a href=\"/admin\">Admin Panel</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " <li><a href=\"#\" hx-post=\"/users/logout\" hx-headers=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(hxHeaders)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 57, Col: 66}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 74, Col: 66}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\">Logout</a></li>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\">Logout</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
if data.LocalAuthEnabled {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<li><a href=\"/users/register\">Create an Account</a></li>| ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<li><a href=\"/users/register\">Create an Account</a></li>| ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " <li><a href=\"/users/login\">Login</a></li>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " <li><a href=\"/users/login\">Login</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</ul></nav>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</ul></nav>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -174,7 +198,7 @@ func commonFooter() templ.Component {
|
||||
templ_7745c5c3_Var5 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<footer><p>A <a href=\"https://32bit.cafe\" rel=\"noopener\">32bit.cafe</a> Project</p><ul class=\"footer-links\"><li><a href=\"/about\">About</a></li><li><a href=\"/help\">Help</a></li></ul></footer>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<footer><p>A <a href=\"https://32bit.cafe\" rel=\"noopener\">32bit.cafe</a> Project</p><ul class=\"footer-links\"><li><a href=\"/about\">About</a></li><li><a href=\"/help\">Help</a></li></ul></footer>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -203,33 +227,33 @@ func base(title string, data CommonData) templ.Component {
|
||||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<!doctype html><html lang=\"en\"><head><title>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<!doctype html><html lang=\"en\"><head><title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 82, Col: 17}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 99, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " - webweav.ing</title><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"htmx-config\" content=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, " - webweav.ing</title><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"htmx-config\" content=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(`{"includeIndicatorStyles":false}`)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 85, Col: 72}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 102, Col: 72}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"><link href=\"/static/css/style.css\" rel=\"stylesheet\"><link href=\"/static/fontawesome/css/fontawesome.css\" rel=\"stylesheet\"><link href=\"/static/fontawesome/css/solid.css\" rel=\"stylesheet\"><script src=\"/static/js/htmx.min.js\"></script></head><body>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\"><link href=\"/static/css/style.css\" rel=\"stylesheet\"><link href=\"/static/fontawesome/css/fontawesome.css\" rel=\"stylesheet\"><link href=\"/static/fontawesome/css/solid.css\" rel=\"stylesheet\"><script src=\"/static/js/htmx.min.js\"></script></head><body>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -241,25 +265,25 @@ func base(title string, data CommonData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<main role=\"main\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<main role=\"main\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if data.Flash != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<div class=\"notice flash\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"notice flash\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(data.Flash)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 96, Col: 43}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 113, Col: 43}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -268,7 +292,7 @@ func base(title string, data CommonData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</main>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</main>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -276,7 +300,140 @@ func base(title string, data CommonData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</body></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func pagination(baseUrl string, currentPage int64, recordsAmount int64, recordsPerPage int64) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var10 == nil {
|
||||
templ_7745c5c3_Var10 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
totalPages := int64(math.Ceil(float64(recordsAmount) / float64(recordsPerPage)))
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<div class=\"pagination\"><span class=\"pagination-btn\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if currentPage > 1 {
|
||||
url := fmt.Sprintf("%s?page=%d&count=%d", baseUrl, currentPage-1, recordsPerPage)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 templ.SafeURL = templ.URL(url)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var11)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\">Prev</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "Prev")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for i := range totalPages {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<span class=\"pagination-btn\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
url := fmt.Sprintf("%s?page=%d&count=%d", baseUrl, i+1, recordsPerPage)
|
||||
if i+1 == currentPage {
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", i+1))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 137, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 templ.SafeURL = templ.URL(url)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var13)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", i+1))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 139, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<span class=\"pagination-btn\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if currentPage < totalPages {
|
||||
url := fmt.Sprintf("%s?page=%d&count=%d", baseUrl, currentPage+1, recordsPerPage)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 templ.SafeURL = templ.URL(url)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var15)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\">Next</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "Next")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</span></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
75
ui/views/install.templ
Normal file
75
ui/views/install.templ
Normal file
@ -0,0 +1,75 @@
|
||||
package views
|
||||
|
||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/forms"
|
||||
|
||||
templ installBase(title string) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{ title } - webweav.ing</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link href="/static/css/style.css" rel="stylesheet"/>
|
||||
<script src="/static/js/htmx.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>{ title }</h1>
|
||||
{ children... }
|
||||
</main>
|
||||
@commonFooter()
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
|
||||
templ InitialInstallView(title string) {
|
||||
@installBase(title) {
|
||||
<p>Installing</p>
|
||||
<a href="/install">Next</a>
|
||||
}
|
||||
}
|
||||
|
||||
templ InstallFormView(title string, form forms.InstallForm) {
|
||||
@installBase(title) {
|
||||
<form action="/install" method="post">
|
||||
<fieldset>
|
||||
<legend>Admin User</legend>
|
||||
<div class="form-group">
|
||||
<div>
|
||||
{{ error, exists := form.FieldErrors[""] }}
|
||||
<label for="username">Username: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="text" name="username" id="username" value={ form.Name } required/>
|
||||
</div>
|
||||
<div>
|
||||
{{ error, exists = form.FieldErrors["email"] }}
|
||||
<label for="email">Email: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="text" name="email" id="email" value={ form.Email } required/>
|
||||
</div>
|
||||
<div>
|
||||
{{ error, exists = form.FieldErrors["password"] }}
|
||||
<label for="password">Password: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="password" name="password" id="password"/>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div>
|
||||
<input type="submit" value="Install"/>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
|
||||
templ InstallSuccessView() {
|
||||
@installBase("Success") {
|
||||
<p>Installation was successful. Go <a href="/">home</a>.</p>
|
||||
}
|
||||
}
|
||||
335
ui/views/install_templ.go
Normal file
335
ui/views/install_templ.go
Normal file
@ -0,0 +1,335 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.833
|
||||
package views
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "git.32bit.cafe/32bitcafe/guestbook/internal/forms"
|
||||
|
||||
func installBase(title string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><title>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/install.templ`, Line: 9, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - webweav.ing</title><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link href=\"/static/css/style.css\" rel=\"stylesheet\"><script src=\"/static/js/htmx.min.js\"></script></head><body><main><h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/install.templ`, Line: 17, Col: 15}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</main>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = commonFooter().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func InitialInstallView(title string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var4 == nil {
|
||||
templ_7745c5c3_Var4 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<p>Installing</p><a href=\"/install\">Next</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = installBase(title).Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func InstallFormView(title string, form forms.InstallForm) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var6 == nil {
|
||||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<form action=\"/install\" method=\"post\"><fieldset><legend>Admin User</legend><div class=\"form-group\"><div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
error, exists := form.FieldErrors[""]
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<label for=\"username\">Username: </label> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if exists {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<label class=\"error\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/install.templ`, Line: 42, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</label> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<input type=\"text\" name=\"username\" id=\"username\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(form.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/install.templ`, Line: 44, Col: 72}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" required></div><div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
error, exists = form.FieldErrors["email"]
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<label for=\"email\">Email: </label> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if exists {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<label class=\"error\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/install.templ`, Line: 50, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</label> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<input type=\"text\" name=\"email\" id=\"email\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/install.templ`, Line: 52, Col: 67}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" required></div><div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
error, exists = form.FieldErrors["password"]
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<label for=\"password\">Password: </label> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if exists {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<label class=\"error\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/install.templ`, Line: 58, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</label> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<input type=\"password\" name=\"password\" id=\"password\"></div></div></fieldset><div><input type=\"submit\" value=\"Install\"></div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = installBase(title).Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func InstallSuccessView() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var13 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var13 == nil {
|
||||
templ_7745c5c3_Var13 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var14 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<p>Installation was successful. Go <a href=\"/\">home</a>.</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = installBase("Success").Render(templ.WithChildren(ctx, templ_7745c5c3_Var14), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@ -9,25 +9,27 @@ templ UserLogin(title string, data CommonData, form forms.UserLoginForm) {
|
||||
@base(title, data) {
|
||||
<h1>Login</h1>
|
||||
<form action="/users/login" method="POST" novalidate>
|
||||
<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
|
||||
for _, error := range form.NonFieldErrors {
|
||||
<div class="error">{ error }</div>
|
||||
}
|
||||
<div>
|
||||
<label>Email: </label>
|
||||
{{ error, exists := form.FieldErrors["email"] }}
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
<div class="form-group">
|
||||
<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
|
||||
for _, error := range form.NonFieldErrors {
|
||||
<div class="error">{ error }</div>
|
||||
}
|
||||
<input type="email" name="email" value={ form.Email }/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Password: </label>
|
||||
{{ error, exists = form.FieldErrors["password"] }}
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="password" name="password"/>
|
||||
<div>
|
||||
<label>Email: </label>
|
||||
{{ error, exists := form.FieldErrors["email"] }}
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="email" name="email" value={ form.Email }/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Password: </label>
|
||||
{{ error, exists = form.FieldErrors["password"] }}
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="password" name="password"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Login"/>
|
||||
@ -44,29 +46,31 @@ templ UserRegistration(title string, data CommonData, form forms.UserRegistratio
|
||||
<h1>User Registration</h1>
|
||||
<form action="/users/register" method="post">
|
||||
<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
|
||||
<div>
|
||||
{{ error, exists := form.FieldErrors["name"] }}
|
||||
<label for="username">Username: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="text" name="username" id="username" value={ form.Name } required/>
|
||||
</div>
|
||||
<div>
|
||||
{{ error, exists = form.FieldErrors["email"] }}
|
||||
<label for="email">Email: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="text" name="email" id="email" value={ form.Email } required/>
|
||||
</div>
|
||||
<div>
|
||||
{{ error, exists = form.FieldErrors["password"] }}
|
||||
<label for="password">Password: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="password" name="password" id="password"/>
|
||||
<div class="form-group">
|
||||
<div>
|
||||
{{ error, exists := form.FieldErrors["name"] }}
|
||||
<label for="username">Username: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="text" name="username" id="username" value={ form.Name } required/>
|
||||
</div>
|
||||
<div>
|
||||
{{ error, exists = form.FieldErrors["email"] }}
|
||||
<label for="email">Email: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="text" name="email" id="email" value={ form.Email } required/>
|
||||
</div>
|
||||
<div>
|
||||
{{ error, exists = form.FieldErrors["password"] }}
|
||||
<label for="password">Password: </label>
|
||||
if exists {
|
||||
<label class="error">{ error }</label>
|
||||
}
|
||||
<input type="password" name="password" id="password"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Register"/>
|
||||
@ -90,7 +94,6 @@ templ UserSettingsView(data CommonData, timezones []string) {
|
||||
<form hx-put="/users/settings">
|
||||
<input type="hidden" name="csrf_token" value={ data.CSRFToken }/>
|
||||
<fieldset>
|
||||
<legend id="user-settings-heading">User Settings</legend>
|
||||
<div class="form-group">
|
||||
<label>Local Timezone</label>
|
||||
<select name="timezones" id="timezone-select">
|
||||
|
||||
@ -46,14 +46,14 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<h1>Login</h1><form action=\"/users/login\" method=\"POST\" novalidate><input type=\"hidden\" name=\"csrf_token\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<h1>Login</h1><form action=\"/users/login\" method=\"POST\" novalidate><div class=\"form-group\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 12, Col: 64}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 13, Col: 65}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -71,7 +71,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 14, Col: 30}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 15, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -95,7 +95,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 20, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 21, Col: 34}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -113,7 +113,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 22, Col: 55}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 23, Col: 56}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -132,7 +132,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 28, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 29, Col: 34}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -143,7 +143,7 @@ func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Co
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<input type=\"password\" name=\"password\"></div><div><input type=\"submit\" value=\"Login\"> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<input type=\"password\" name=\"password\"></div></div><div><input type=\"submit\" value=\"Login\"> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -207,13 +207,13 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 46, Col: 64}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 48, Col: 64}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"><div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"><div class=\"form-group\"><div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -230,7 +230,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 51, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 54, Col: 34}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -248,7 +248,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(form.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 53, Col: 70}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 56, Col: 71}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -271,7 +271,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 59, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 62, Col: 34}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -289,7 +289,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 61, Col: 65}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 64, Col: 66}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -312,7 +312,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(error)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 67, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 70, Col: 34}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -323,7 +323,7 @@ func UserRegistration(title string, data CommonData, form forms.UserRegistration
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<input type=\"password\" name=\"password\" id=\"password\"></div><div><input type=\"submit\" value=\"Register\"></div></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<input type=\"password\" name=\"password\" id=\"password\"></div></div><div><input type=\"submit\" value=\"Register\"></div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -377,7 +377,7 @@ func UserProfile(title string, data CommonData, user models.User) templ.Componen
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 80, Col: 21}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 84, Col: 21}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -390,7 +390,7 @@ func UserProfile(title string, data CommonData, user models.User) templ.Componen
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 81, Col: 17}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 85, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -451,13 +451,13 @@ func UserSettingsView(data CommonData, timezones []string) templ.Component {
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 91, Col: 66}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 95, Col: 66}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\"><fieldset><legend id=\"user-settings-heading\">User Settings</legend><div class=\"form-group\"><label>Local Timezone</label> <select name=\"timezones\" id=\"timezone-select\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\"><fieldset><div class=\"form-group\"><label>Local Timezone</label> <select name=\"timezones\" id=\"timezone-select\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -470,7 +470,7 @@ func UserSettingsView(data CommonData, timezones []string) templ.Component {
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(tz)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 99, Col: 28}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 102, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -483,7 +483,7 @@ func UserSettingsView(data CommonData, timezones []string) templ.Component {
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(tz)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 99, Col: 51}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 102, Col: 51}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -501,7 +501,7 @@ func UserSettingsView(data CommonData, timezones []string) templ.Component {
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(tz)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 101, Col: 28}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 104, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -514,7 +514,7 @@ func UserSettingsView(data CommonData, timezones []string) templ.Component {
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(tz)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 101, Col: 35}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 104, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user