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{}
|
configs map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RegisteredConfig struct {
|
||||||
|
Name string
|
||||||
|
Config interface{}
|
||||||
|
}
|
||||||
|
|
||||||
func NewConfigRegister() *ConfigRegister {
|
func NewConfigRegister() *ConfigRegister {
|
||||||
return &ConfigRegister{configs: map[string]interface{}{}}
|
return &ConfigRegister{configs: map[string]interface{}{}}
|
||||||
}
|
}
|
||||||
|
@ -11,3 +16,18 @@ func NewConfigRegister() *ConfigRegister {
|
||||||
func (r *ConfigRegister) Register(name string, config interface{}) {
|
func (r *ConfigRegister) Register(name string, config interface{}) {
|
||||||
r.configs[name] = config
|
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)
|
binaryService := app.NewBinaryFileService(binRepo)
|
||||||
authorService := app.NewAuthorService(authorRepo, siteConfigRepo)
|
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,
|
binService *app.BinaryService,
|
||||||
authorService *app.AuthorService,
|
authorService *app.AuthorService,
|
||||||
configRepo repository.ConfigRepository,
|
configRepo repository.ConfigRepository,
|
||||||
|
configRegister *app.ConfigRegister,
|
||||||
) *WebApp {
|
) *WebApp {
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
|
@ -45,6 +46,14 @@ func NewWebApp(
|
||||||
app.Get("/auth/login", loginHandler.HandleGet)
|
app.Get("/auth/login", loginHandler.HandleGet)
|
||||||
app.Post("/auth/login", loginHandler.HandlePost)
|
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
|
||||||
editor := app.Group("/editor")
|
editor := app.Group("/editor")
|
||||||
editor.Use(middleware.NewAuthMiddleware(authorService).Handle)
|
editor.Use(middleware.NewAuthMiddleware(authorService).Handle)
|
||||||
|
@ -92,6 +101,7 @@ func NewWebApp(
|
||||||
|
|
||||||
// ActivityPub
|
// ActivityPub
|
||||||
// activityPubServer := NewActivityPubServer(configRepo)
|
// activityPubServer := NewActivityPubServer(configRepo)
|
||||||
|
configRegister.Register(ACT_PUB_CONF_NAME, &ActivityPubConfig{})
|
||||||
// app.Get("/.well-known/webfinger", activityPubServer.HandleWebfinger)
|
// app.Get("/.well-known/webfinger", activityPubServer.HandleWebfinger)
|
||||||
// app.Route("/activitypub", activityPubServer.Router)
|
// 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