form to edit registered configs
This commit is contained in:
parent
68e8f84220
commit
a38ebb61a9
|
@ -4,6 +4,11 @@ type ConfigRegister struct {
|
|||
configs map[string]interface{}
|
||||
}
|
||||
|
||||
type RegisteredConfig struct {
|
||||
Name string
|
||||
Config interface{}
|
||||
}
|
||||
|
||||
func NewConfigRegister() *ConfigRegister {
|
||||
return &ConfigRegister{configs: map[string]interface{}{}}
|
||||
}
|
||||
|
@ -11,3 +16,18 @@ func NewConfigRegister() *ConfigRegister {
|
|||
func (r *ConfigRegister) Register(name string, config interface{}) {
|
||||
r.configs[name] = config
|
||||
}
|
||||
|
||||
func (r *ConfigRegister) Configs() []RegisteredConfig {
|
||||
var configs []RegisteredConfig
|
||||
for name, config := range r.configs {
|
||||
configs = append(configs, RegisteredConfig{
|
||||
Name: name,
|
||||
Config: config,
|
||||
})
|
||||
}
|
||||
return configs
|
||||
}
|
||||
|
||||
func (r *ConfigRegister) GetConfig(name string) interface{} {
|
||||
return r.configs[name]
|
||||
}
|
||||
|
|
|
@ -44,7 +44,12 @@ func App(db infra.Database) *web.WebApp {
|
|||
binaryService := app.NewBinaryFileService(binRepo)
|
||||
authorService := app.NewAuthorService(authorRepo, siteConfigRepo)
|
||||
|
||||
return web.NewWebApp(entryService, registry, binaryService, authorService, siteConfigRepo)
|
||||
configRegister := app.NewConfigRegister()
|
||||
|
||||
return web.NewWebApp(
|
||||
entryService, registry, binaryService,
|
||||
authorService, siteConfigRepo, configRegister,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{{define "title"}}Admin{{end}}
|
||||
|
||||
{{define "main"}}
|
||||
<ul>
|
||||
{{ range . }}
|
||||
<li><a href="/admin/config/{{.Name}}">{{.Name}}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{end}}
|
|
@ -0,0 +1,11 @@
|
|||
{{define "title"}}Editor{{end}}
|
||||
|
||||
{{define "main"}}
|
||||
|
||||
<a href="/editor">Back</a>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
{{.}}
|
||||
|
||||
{{end}}
|
|
@ -0,0 +1,75 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"owl-blogs/app"
|
||||
"owl-blogs/app/repository"
|
||||
"owl-blogs/render"
|
||||
"owl-blogs/web/forms"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type adminHandler struct {
|
||||
configRepo repository.ConfigRepository
|
||||
configRegister *app.ConfigRegister
|
||||
}
|
||||
|
||||
func NewAdminHandler(configRepo repository.ConfigRepository, configRegister *app.ConfigRegister) *adminHandler {
|
||||
return &adminHandler{
|
||||
configRepo: configRepo,
|
||||
configRegister: configRegister,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *adminHandler) Handle(c *fiber.Ctx) error {
|
||||
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
|
||||
|
||||
siteConfig := getSiteConfig(h.configRepo)
|
||||
configs := h.configRegister.Configs()
|
||||
return render.RenderTemplateWithBase(c, siteConfig, "views/admin", configs)
|
||||
}
|
||||
|
||||
func (h *adminHandler) HandleConfigGet(c *fiber.Ctx) error {
|
||||
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
|
||||
|
||||
configName := c.Params("config")
|
||||
config := h.configRegister.GetConfig(configName)
|
||||
if config == nil {
|
||||
return c.SendStatus(404)
|
||||
}
|
||||
err := h.configRepo.Get(configName, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
siteConfig := getSiteConfig(h.configRepo)
|
||||
|
||||
form := forms.NewForm(config, nil)
|
||||
htmlForm, err := form.HtmlForm()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return render.RenderTemplateWithBase(c, siteConfig, "views/admin_config", htmlForm)
|
||||
}
|
||||
|
||||
func (h *adminHandler) HandleConfigPost(c *fiber.Ctx) error {
|
||||
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
|
||||
|
||||
configName := c.Params("config")
|
||||
config := h.configRegister.GetConfig(configName)
|
||||
if config == nil {
|
||||
return c.SendStatus(404)
|
||||
}
|
||||
|
||||
form := forms.NewForm(config, nil)
|
||||
|
||||
newConfig, err := form.Parse(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.configRepo.Update(configName, newConfig)
|
||||
|
||||
return c.Redirect("")
|
||||
|
||||
}
|
10
web/app.go
10
web/app.go
|
@ -29,6 +29,7 @@ func NewWebApp(
|
|||
binService *app.BinaryService,
|
||||
authorService *app.AuthorService,
|
||||
configRepo repository.ConfigRepository,
|
||||
configRegister *app.ConfigRegister,
|
||||
) *WebApp {
|
||||
app := fiber.New()
|
||||
|
||||
|
@ -45,6 +46,14 @@ func NewWebApp(
|
|||
app.Get("/auth/login", loginHandler.HandleGet)
|
||||
app.Post("/auth/login", loginHandler.HandlePost)
|
||||
|
||||
// admin
|
||||
adminHandler := NewAdminHandler(configRepo, configRegister)
|
||||
admin := app.Group("/admin")
|
||||
admin.Use(middleware.NewAuthMiddleware(authorService).Handle)
|
||||
admin.Get("/", adminHandler.Handle)
|
||||
admin.Get("/config/:config/", adminHandler.HandleConfigGet)
|
||||
admin.Post("/config/:config/", adminHandler.HandleConfigPost)
|
||||
|
||||
// Editor
|
||||
editor := app.Group("/editor")
|
||||
editor.Use(middleware.NewAuthMiddleware(authorService).Handle)
|
||||
|
@ -92,6 +101,7 @@ func NewWebApp(
|
|||
|
||||
// ActivityPub
|
||||
// activityPubServer := NewActivityPubServer(configRepo)
|
||||
configRegister.Register(ACT_PUB_CONF_NAME, &ActivityPubConfig{})
|
||||
// app.Get("/.well-known/webfinger", activityPubServer.HandleWebfinger)
|
||||
// app.Route("/activitypub", activityPubServer.Router)
|
||||
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
package forms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"owl-blogs/app"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HttpFormData interface {
|
||||
// FormFile returns the first file by key from a MultipartForm.
|
||||
FormFile(key string) (*multipart.FileHeader, error)
|
||||
// FormValue returns the first value by key from a MultipartForm.
|
||||
// Search is performed in QueryArgs, PostArgs, MultipartForm and FormFile in this particular order.
|
||||
// Defaults to the empty string "" if the form value doesn't exist.
|
||||
// If a default value is given, it will return that value if the form value does not exist.
|
||||
// Returned value is only valid within the handler. Do not store any references.
|
||||
// Make copies or use the Immutable setting instead.
|
||||
FormValue(key string, defaultValue ...string) string
|
||||
}
|
||||
|
||||
type Form struct {
|
||||
data interface{}
|
||||
binSvc *app.BinaryService
|
||||
}
|
||||
|
||||
type FormFieldParams struct {
|
||||
InputType string
|
||||
Widget string
|
||||
}
|
||||
|
||||
type FormField struct {
|
||||
Name string
|
||||
Value string
|
||||
Params FormFieldParams
|
||||
}
|
||||
|
||||
func NewForm(data interface{}, binaryService *app.BinaryService) *Form {
|
||||
return &Form{
|
||||
data: data,
|
||||
binSvc: binaryService,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FormFieldParams) ApplyTag(tagKey string, tagValue string) error {
|
||||
switch tagKey {
|
||||
case "inputType":
|
||||
s.InputType = tagValue
|
||||
case "widget":
|
||||
s.Widget = tagValue
|
||||
default:
|
||||
return fmt.Errorf("unknown tag key: %v", tagKey)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FormField) Html() string {
|
||||
html := ""
|
||||
html += fmt.Sprintf("<label for=\"%v\">%v</label>\n", s.Name, s.Name)
|
||||
if s.Params.InputType == "text" && s.Params.Widget == "textarea" {
|
||||
html += fmt.Sprintf("<textarea name=\"%v\" id=\"%v\" rows=\"20\">%v</textarea>\n", s.Name, s.Name, s.Value)
|
||||
} else {
|
||||
html += fmt.Sprintf("<input type=\"%v\" name=\"%v\" id=\"%v\" value=\"%v\" />\n", s.Params.InputType, s.Name, s.Name, s.Value)
|
||||
}
|
||||
return html
|
||||
}
|
||||
|
||||
func FieldToFormField(field reflect.StructField, value string) (FormField, error) {
|
||||
formField := FormField{
|
||||
Name: field.Name,
|
||||
Value: value,
|
||||
Params: FormFieldParams{},
|
||||
}
|
||||
tag := field.Tag.Get("owl")
|
||||
for _, param := range strings.Split(tag, " ") {
|
||||
parts := strings.Split(param, "=")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
err := formField.Params.ApplyTag(parts[0], parts[1])
|
||||
if err != nil {
|
||||
return FormField{}, err
|
||||
}
|
||||
}
|
||||
return formField, nil
|
||||
}
|
||||
|
||||
func StructToFormFields(data interface{}) ([]FormField, error) {
|
||||
dataValue := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataType := reflect.TypeOf(data).Elem()
|
||||
numFields := dataType.NumField()
|
||||
fields := []FormField{}
|
||||
for i := 0; i < numFields; i++ {
|
||||
field, err := FieldToFormField(dataType.Field(i), dataValue.FieldByIndex([]int{i}).String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields = append(fields, field)
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
func (s *Form) HtmlForm() (string, error) {
|
||||
fields, err := StructToFormFields(s.data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
html := "<form method=\"POST\" enctype=\"multipart/form-data\">\n"
|
||||
for _, field := range fields {
|
||||
html += field.Html()
|
||||
}
|
||||
html += "<input type=\"submit\" value=\"Submit\" />\n"
|
||||
html += "</form>\n"
|
||||
|
||||
return html, nil
|
||||
}
|
||||
|
||||
func (s *Form) Parse(ctx HttpFormData) (interface{}, error) {
|
||||
if ctx == nil {
|
||||
return nil, fmt.Errorf("nil context")
|
||||
}
|
||||
dataVal := reflect.ValueOf(s.data)
|
||||
if dataVal.Kind() != reflect.Ptr {
|
||||
return nil, fmt.Errorf("meta data is not a pointer")
|
||||
}
|
||||
fields, err := StructToFormFields(s.data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, field := range fields {
|
||||
fieldName := field.Name
|
||||
|
||||
if field.Params.InputType == "file" {
|
||||
file, err := ctx.FormFile(fieldName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileData, err := file.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fileData.Close()
|
||||
fileBytes := make([]byte, file.Size)
|
||||
_, err = fileData.Read(fileBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
binaryFile, err := s.binSvc.Create(file.Filename, fileBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metaField := dataVal.Elem().FieldByName(fieldName)
|
||||
if metaField.IsValid() {
|
||||
metaField.SetString(binaryFile.Id)
|
||||
}
|
||||
} else {
|
||||
formValue := ctx.FormValue(fieldName)
|
||||
metaField := dataVal.Elem().FieldByName(fieldName)
|
||||
if metaField.IsValid() {
|
||||
metaField.SetString(formValue)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return s.data, nil
|
||||
}
|
Loading…
Reference in New Issue