Add ntfy config settings
This commit is contained in:
@@ -73,6 +73,10 @@ func main() {
|
|||||||
configRepo := config.NewRepository(dbCon)
|
configRepo := config.NewRepository(dbCon)
|
||||||
configService := config.NewService(configRepo)
|
configService := config.NewService(configRepo)
|
||||||
|
|
||||||
|
if err := configService.EnsureDefaults(); err != nil {
|
||||||
|
panic(fmt.Errorf("failed to ensure config defaults: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
fileRepo := file.NewRepository(dbCon)
|
fileRepo := file.NewRepository(dbCon)
|
||||||
fileService := file.NewService(fileRepo, "./uploads")
|
fileService := file.NewService(fileRepo, "./uploads")
|
||||||
fileHandler := file.NewHandler(fileService, configService)
|
fileHandler := file.NewHandler(fileService, configService)
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
KeyUploadMaxFileSizeBytes = "upload.max_file_size_bytes"
|
KeyUploadMaxFileSizeBytes = "upload.max_file_size_bytes"
|
||||||
KeyUploadMultiMaxFiles = "upload.multi.max_files"
|
KeyUploadMultiMaxFiles = "upload.multi.max_files"
|
||||||
KeyUploadMaxHours = "upload.max_hours"
|
KeyUploadMaxHours = "upload.max_hours"
|
||||||
KeyRateLimitLoginPerMinute = "ratelimit.login.per_minute"
|
KeyRateLimitLoginPerMinute = "ratelimit.login.per_minute"
|
||||||
KeyRateLimitApiPerMinute = "ratelimit.api.per_minute"
|
KeyRateLimitApiPerMinute = "ratelimit.api.per_minute"
|
||||||
KeyRateLimitApiBurst = "ratelimit.api.burst"
|
KeyRateLimitApiBurst = "ratelimit.api.burst"
|
||||||
KeyRateLimitLoginBurst = "ratelimit.login.burst"
|
KeyRateLimitLoginBurst = "ratelimit.login.burst"
|
||||||
|
|
||||||
|
KeyUseNtfy = "use_ntfy"
|
||||||
|
KeyNtfyUrl = "ntfy.url"
|
||||||
|
KeyNtfyTopic = "ntfy.topic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defaults (used when DB does not have an override)
|
// Defaults (used when DB does not have an override)
|
||||||
@@ -20,4 +26,27 @@ const (
|
|||||||
DefaultRateLimitLoginBurst = 10
|
DefaultRateLimitLoginBurst = 10
|
||||||
DefaultRateLimitApiPerMinute = 60
|
DefaultRateLimitApiPerMinute = 60
|
||||||
DefaultRateLimitApiBurst = 30
|
DefaultRateLimitApiBurst = 30
|
||||||
|
|
||||||
|
DefaultUseNtfy = 0
|
||||||
|
DefaultNtfyUrl = ""
|
||||||
|
DefaultNtfyTopic = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultKeyValues returns a map of config keys to their default string values, for use when initializing the database.
|
||||||
|
// Code duplication be dammed
|
||||||
|
func DefaultKeyValues() map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
KeyUploadMaxFileSizeBytes: strconv.FormatInt(DefaultUploadMaxFileSizeBytes, 10),
|
||||||
|
KeyUploadMultiMaxFiles: strconv.Itoa(DefaultUploadMultiMaxFiles),
|
||||||
|
KeyUploadMaxHours: strconv.Itoa(DefaultUploadMaxHours),
|
||||||
|
|
||||||
|
KeyRateLimitLoginPerMinute: strconv.Itoa(DefaultRateLimitLoginPerMinute),
|
||||||
|
KeyRateLimitLoginBurst: strconv.Itoa(DefaultRateLimitLoginBurst),
|
||||||
|
KeyRateLimitApiPerMinute: strconv.Itoa(DefaultRateLimitApiPerMinute),
|
||||||
|
KeyRateLimitApiBurst: strconv.Itoa(DefaultRateLimitApiBurst),
|
||||||
|
|
||||||
|
KeyUseNtfy: strconv.Itoa(DefaultUseNtfy),
|
||||||
|
KeyNtfyUrl: DefaultNtfyUrl,
|
||||||
|
KeyNtfyTopic: DefaultNtfyTopic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import "gorm.io/gorm"
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
type Repository struct {
|
type Repository struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
@@ -37,3 +41,15 @@ func (r *Repository) List() ([]ConfigEntry, error) {
|
|||||||
}
|
}
|
||||||
return entries, nil
|
return entries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Repository) CreateIfMissing(key, value string) error {
|
||||||
|
var e ConfigEntry
|
||||||
|
err := r.db.First(&e, "key = ?", key).Error
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return r.db.Create(&ConfigEntry{Key: key, Value: value}).Error
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ func NewService(r *Repository) *Service {
|
|||||||
return &Service{repo: r, cache: make(map[string]string)}
|
return &Service{repo: r, cache: make(map[string]string)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) EnsureDefaults() error {
|
||||||
|
for k, v := range DefaultKeyValues() {
|
||||||
|
if err := s.repo.CreateIfMissing(k, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) List() ([]ConfigEntry, error) {
|
func (s *Service) List() ([]ConfigEntry, error) {
|
||||||
entries, err := s.repo.List()
|
entries, err := s.repo.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ type ConfigPageData struct {
|
|||||||
RateLimitLoginBurst int
|
RateLimitLoginBurst int
|
||||||
RateLimitApiPerMinute int
|
RateLimitApiPerMinute int
|
||||||
RateLimitApiBurst int
|
RateLimitApiBurst int
|
||||||
|
|
||||||
|
NtfyUse bool
|
||||||
|
NtfyUrl string
|
||||||
|
NtfyTopic string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigPage renders a modular admin config screen.
|
// ConfigPage renders a modular admin config screen.
|
||||||
@@ -28,14 +32,17 @@ func (h *Handler) ConfigPage(c *gin.Context) {
|
|||||||
|
|
||||||
maxBytes := cfg.GetInt64Default(config.KeyUploadMaxFileSizeBytes, config.DefaultUploadMaxFileSizeBytes)
|
maxBytes := cfg.GetInt64Default(config.KeyUploadMaxFileSizeBytes, config.DefaultUploadMaxFileSizeBytes)
|
||||||
data := ConfigPageData{
|
data := ConfigPageData{
|
||||||
Success: c.Query("saved") == "1",
|
Success: c.Query("saved") == "1",
|
||||||
UploadMaxFileSizeMB: maxBytes / (1024 * 1024),
|
UploadMaxFileSizeMB: maxBytes / (1024 * 1024),
|
||||||
UploadMultiMaxFiles: cfg.GetIntDefault(config.KeyUploadMultiMaxFiles, config.DefaultUploadMultiMaxFiles),
|
UploadMultiMaxFiles: cfg.GetIntDefault(config.KeyUploadMultiMaxFiles, config.DefaultUploadMultiMaxFiles),
|
||||||
UploadMaxHours: cfg.GetIntDefault(config.KeyUploadMaxHours, config.DefaultUploadMaxHours),
|
UploadMaxHours: cfg.GetIntDefault(config.KeyUploadMaxHours, config.DefaultUploadMaxHours),
|
||||||
RateLimitLoginPerMinute: cfg.GetIntDefault(config.KeyRateLimitLoginPerMinute, config.DefaultRateLimitLoginPerMinute),
|
RateLimitLoginPerMinute: cfg.GetIntDefault(config.KeyRateLimitLoginPerMinute, config.DefaultRateLimitLoginPerMinute),
|
||||||
RateLimitLoginBurst: cfg.GetIntDefault(config.KeyRateLimitLoginBurst, config.DefaultRateLimitLoginBurst),
|
RateLimitLoginBurst: cfg.GetIntDefault(config.KeyRateLimitLoginBurst, config.DefaultRateLimitLoginBurst),
|
||||||
RateLimitApiPerMinute: cfg.GetIntDefault(config.KeyRateLimitApiPerMinute, config.DefaultRateLimitApiPerMinute),
|
RateLimitApiPerMinute: cfg.GetIntDefault(config.KeyRateLimitApiPerMinute, config.DefaultRateLimitApiPerMinute),
|
||||||
RateLimitApiBurst: cfg.GetIntDefault(config.KeyRateLimitApiBurst, config.DefaultRateLimitApiBurst),
|
RateLimitApiBurst: cfg.GetIntDefault(config.KeyRateLimitApiBurst, config.DefaultRateLimitApiBurst),
|
||||||
|
NtfyUse: cfg.GetIntDefault(config.KeyUseNtfy, 0) != 0,
|
||||||
|
NtfyUrl: cfg.GetStringDefault(config.KeyNtfyUrl, config.DefaultNtfyUrl),
|
||||||
|
NtfyTopic: cfg.GetStringDefault(config.KeyNtfyTopic, config.DefaultNtfyTopic),
|
||||||
}
|
}
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "config.html", data)
|
c.HTML(http.StatusOK, "config.html", data)
|
||||||
@@ -113,6 +120,14 @@ func (h *Handler) ConfigSave(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useNTFY, err := strconv.ParseBool(c.PostForm("ntfy_use"))
|
||||||
|
if err != nil {
|
||||||
|
h.renderConfigError(c, "invalid ntfy use value")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ntfyUrl := c.PostForm("ntfy_url")
|
||||||
|
ntfyTopic := c.PostForm("ntfy_topic")
|
||||||
|
|
||||||
// Persist.
|
// Persist.
|
||||||
if err := cfg.SetString(config.KeyUploadMaxFileSizeBytes, strconv.FormatInt(maxMB*1024*1024, 10)); err != nil {
|
if err := cfg.SetString(config.KeyUploadMaxFileSizeBytes, strconv.FormatInt(maxMB*1024*1024, 10)); err != nil {
|
||||||
h.renderConfigError(c, err.Error())
|
h.renderConfigError(c, err.Error())
|
||||||
@@ -132,16 +147,25 @@ func (h *Handler) ConfigSave(c *gin.Context) {
|
|||||||
_ = cfg.SetString(config.KeyRateLimitApiPerMinute, strconv.Itoa(apiPerMin))
|
_ = cfg.SetString(config.KeyRateLimitApiPerMinute, strconv.Itoa(apiPerMin))
|
||||||
_ = cfg.SetString(config.KeyRateLimitApiBurst, strconv.Itoa(apiBurst))
|
_ = cfg.SetString(config.KeyRateLimitApiBurst, strconv.Itoa(apiBurst))
|
||||||
|
|
||||||
|
// shitty ah fix
|
||||||
|
actualBool := 0
|
||||||
|
if useNTFY {
|
||||||
|
actualBool = 1
|
||||||
|
}
|
||||||
|
_ = cfg.SetString(config.KeyUseNtfy, strconv.Itoa(actualBool))
|
||||||
|
_ = cfg.SetString(config.KeyNtfyUrl, ntfyUrl)
|
||||||
|
_ = cfg.SetString(config.KeyNtfyTopic, ntfyTopic)
|
||||||
|
|
||||||
c.Redirect(http.StatusFound, "/config?saved=1")
|
c.Redirect(http.StatusFound, "/config?saved=1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) renderConfigError(c *gin.Context, msg string) {
|
func (h *Handler) renderConfigError(c *gin.Context, msg string) {
|
||||||
maxBytes := h.configService.GetInt64Default(config.KeyUploadMaxFileSizeBytes, config.DefaultUploadMaxFileSizeBytes)
|
maxBytes := h.configService.GetInt64Default(config.KeyUploadMaxFileSizeBytes, config.DefaultUploadMaxFileSizeBytes)
|
||||||
data := ConfigPageData{
|
data := ConfigPageData{
|
||||||
Error: msg,
|
Error: msg,
|
||||||
UploadMaxFileSizeMB: maxBytes / (1024 * 1024),
|
UploadMaxFileSizeMB: maxBytes / (1024 * 1024),
|
||||||
UploadMultiMaxFiles: h.configService.GetIntDefault(config.KeyUploadMultiMaxFiles, config.DefaultUploadMultiMaxFiles),
|
UploadMultiMaxFiles: h.configService.GetIntDefault(config.KeyUploadMultiMaxFiles, config.DefaultUploadMultiMaxFiles),
|
||||||
UploadMaxHours: h.configService.GetIntDefault(config.KeyUploadMaxHours, config.DefaultUploadMaxHours),
|
UploadMaxHours: h.configService.GetIntDefault(config.KeyUploadMaxHours, config.DefaultUploadMaxHours),
|
||||||
RateLimitLoginPerMinute: h.configService.GetIntDefault(config.KeyRateLimitLoginPerMinute, config.DefaultRateLimitLoginPerMinute),
|
RateLimitLoginPerMinute: h.configService.GetIntDefault(config.KeyRateLimitLoginPerMinute, config.DefaultRateLimitLoginPerMinute),
|
||||||
RateLimitLoginBurst: h.configService.GetIntDefault(config.KeyRateLimitLoginBurst, config.DefaultRateLimitLoginBurst),
|
RateLimitLoginBurst: h.configService.GetIntDefault(config.KeyRateLimitLoginBurst, config.DefaultRateLimitLoginBurst),
|
||||||
RateLimitApiPerMinute: h.configService.GetIntDefault(config.KeyRateLimitApiPerMinute, config.DefaultRateLimitApiPerMinute),
|
RateLimitApiPerMinute: h.configService.GetIntDefault(config.KeyRateLimitApiPerMinute, config.DefaultRateLimitApiPerMinute),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type ConfigService interface {
|
|||||||
GetIntDefault(key string, def int) int
|
GetIntDefault(key string, def int) int
|
||||||
GetInt64Default(key string, def int64) int64
|
GetInt64Default(key string, def int64) int64
|
||||||
SetString(key, value string) error
|
SetString(key, value string) error
|
||||||
|
GetStringDefault(key string, value string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(fileService *file.Service, cfg ConfigService) *Handler {
|
func NewHandler(fileService *file.Service, cfg ConfigService) *Handler {
|
||||||
|
|||||||
@@ -207,6 +207,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="box mb-6">
|
||||||
|
<div class="section-title">NTFY_Settings</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<label>Use_NTFY</label>
|
||||||
|
<input type="checkbox" id="ntfy_use_seen" {{if .NtfyUse}}checked{{end}}>
|
||||||
|
<input type="hidden" name="ntfy_use" id="ntfy_use_hidden" value="{{if .NtfyUse}}true{{else}}false{{end}}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<label>NTFY_Url</label>
|
||||||
|
<input type="text" name="ntfy_url" value="{{.NtfyUrl}}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<label>NTFY_Topic</label>
|
||||||
|
<input type="text" name="ntfy_topic" value="{{.NtfyTopic}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ACTIONS -->
|
<!-- ACTIONS -->
|
||||||
<div class="flex justify-between items-center border-t-8 border-black pt-4">
|
<div class="flex justify-between items-center border-t-8 border-black pt-4">
|
||||||
<button type="submit">SAVE</button>
|
<button type="submit">SAVE</button>
|
||||||
@@ -250,6 +270,16 @@
|
|||||||
|
|
||||||
// Listen for changes
|
// Listen for changes
|
||||||
document.addEventListener('input', updateConversions);
|
document.addEventListener('input', updateConversions);
|
||||||
|
|
||||||
|
const checkbox = document.getElementById('ntfy_use_seen');
|
||||||
|
const hidden = document.getElementById('ntfy_use_hidden');
|
||||||
|
|
||||||
|
if (checkbox && hidden) {
|
||||||
|
// Update hidden input whenever checkbox changes
|
||||||
|
checkbox.addEventListener('change', () => {
|
||||||
|
hidden.value = checkbox.checked ? "true" : "false";
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user