fix style, implement config load/save
This commit is contained in:
parent
8fdc9ddb46
commit
37d184b3e2
9 changed files with 333 additions and 54 deletions
|
@ -5,13 +5,18 @@ type BuildStatus struct {
|
||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConfigOption struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
type Adapter interface {
|
type Adapter interface {
|
||||||
Init(cfg *Config)
|
Init(cfg *Config)
|
||||||
Name() string
|
Name() string
|
||||||
EditableSlugs() bool
|
EditableSlugs() bool
|
||||||
BuildOptions() []string
|
BuildOptions() []string
|
||||||
GetConfig(key string) (interface{}, error)
|
GetConfig() map[ConfigOption]string
|
||||||
SetConfig(key string, value interface{}) error
|
SetConfig(map[ConfigOption]string) error
|
||||||
ListPages() map[string]string
|
ListPages() map[string]string
|
||||||
GetPage(string) (Page, error)
|
GetPage(string) (Page, error)
|
||||||
FormatPage(string) string
|
FormatPage(string) string
|
||||||
|
|
|
@ -6,12 +6,13 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EurekaAdapter struct {
|
type EurekaAdapter struct {
|
||||||
Root string
|
Root string
|
||||||
Config map[string]interface{}
|
Config map[ConfigOption]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *EurekaAdapter) Init(cfg *Config) {
|
func (self *EurekaAdapter) Init(cfg *Config) {
|
||||||
|
@ -23,8 +24,12 @@ func (self *EurekaAdapter) Init(cfg *Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.Root = cfg.Root
|
self.Root = cfg.Root
|
||||||
|
self.Config = make(map[ConfigOption]string)
|
||||||
// TODO: read config.h and build self.Config
|
// TODO: read config.h and build self.Config
|
||||||
|
err = self.readCfg()
|
||||||
|
if err != nil {
|
||||||
|
panic("config.h is malformed!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *EurekaAdapter) Name() string {
|
func (self *EurekaAdapter) Name() string {
|
||||||
|
@ -39,12 +44,13 @@ func (self *EurekaAdapter) BuildOptions() []string {
|
||||||
return []string{"twtxt"}
|
return []string{"twtxt"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *EurekaAdapter) GetConfig(key string) (interface{}, error) {
|
func (self *EurekaAdapter) GetConfig() map[ConfigOption]string {
|
||||||
return nil, nil
|
return self.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *EurekaAdapter) SetConfig(key string, value interface{}) error {
|
func (self *EurekaAdapter) SetConfig(cfg map[ConfigOption]string) error {
|
||||||
return nil
|
self.Config = cfg
|
||||||
|
return self.writeCfg()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *EurekaAdapter) ListPages() map[string]string {
|
func (self *EurekaAdapter) ListPages() map[string]string {
|
||||||
|
@ -85,7 +91,6 @@ func (self *EurekaAdapter) GetPage(filename string) (Page, error) {
|
||||||
|
|
||||||
return Page{
|
return Page{
|
||||||
Title: title,
|
Title: title,
|
||||||
Slug: filename,
|
|
||||||
Content: content,
|
Content: content,
|
||||||
Edited: fileInfo.ModTime(),
|
Edited: fileInfo.ModTime(),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -129,7 +134,15 @@ func (self *EurekaAdapter) SavePage(oldSlug, newSlug, title, content string) err
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
if oldSlug != newSlug {
|
if oldSlug != newSlug {
|
||||||
// TODO: delete old html as well
|
siteRoot := self.Config[ConfigOption{
|
||||||
|
Name: "SITEROOT",
|
||||||
|
Type: "string",
|
||||||
|
}]
|
||||||
|
htmlFile := filepath.Join(self.Root, siteRoot, oldSlug+"l")
|
||||||
|
_, err := os.Stat(htmlFile)
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
os.Remove(htmlFile)
|
||||||
|
}
|
||||||
os.Remove(filepath.Join(self.Root, "inc", oldSlug))
|
os.Remove(filepath.Join(self.Root, "inc", oldSlug))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +151,15 @@ func (self *EurekaAdapter) SavePage(oldSlug, newSlug, title, content string) err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *EurekaAdapter) DeletePage(slug string) error {
|
func (self *EurekaAdapter) DeletePage(slug string) error {
|
||||||
// TODO: delete old html as well
|
siteRoot := self.Config[ConfigOption{
|
||||||
|
Name: "SITEROOT",
|
||||||
|
Type: "string",
|
||||||
|
}]
|
||||||
|
htmlFile := filepath.Join(self.Root, siteRoot, slug+"l")
|
||||||
|
_, err := os.Stat(htmlFile)
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
os.Remove(htmlFile)
|
||||||
|
}
|
||||||
return os.Remove(filepath.Join(self.Root, "inc", slug))
|
return os.Remove(filepath.Join(self.Root, "inc", slug))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +168,7 @@ func (self *EurekaAdapter) Build(buildOptions map[string][]string) BuildStatus {
|
||||||
twtxt := buildOptions["twtxt"][0]
|
twtxt := buildOptions["twtxt"][0]
|
||||||
cmdArgs := ""
|
cmdArgs := ""
|
||||||
if twtxt != "" {
|
if twtxt != "" {
|
||||||
cmdArgs += " -t " + twtxt
|
cmdArgs += "-t " + twtxt
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("./build.sh", cmdArgs)
|
cmd := exec.Command("./build.sh", cmdArgs)
|
||||||
|
@ -159,3 +180,111 @@ func (self *EurekaAdapter) Build(buildOptions map[string][]string) BuildStatus {
|
||||||
Message: string(out),
|
Message: string(out),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *EurekaAdapter) readCfg() error {
|
||||||
|
configPath := filepath.Join(self.Root, "config.h")
|
||||||
|
_, err := os.Stat(filepath.Join(self.Root, "config.h"))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
configPath = filepath.Join(self.Root, "config.def.h")
|
||||||
|
}
|
||||||
|
f, err := os.ReadFile(configPath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileData := string(f[:])
|
||||||
|
|
||||||
|
macros := strings.Split(fileData, "#define ")[1:]
|
||||||
|
for _, macro := range macros {
|
||||||
|
tokens := strings.Split(strings.TrimSpace(macro), " ")
|
||||||
|
k := tokens[0]
|
||||||
|
v := strings.TrimSpace(strings.Join(tokens[1:], " "))
|
||||||
|
|
||||||
|
if strings.Contains(v, "\"") {
|
||||||
|
if strings.Contains(v, "\\\r\n") || strings.Contains(v, "\\\n") {
|
||||||
|
// process multiline string
|
||||||
|
lines := strings.Split(v, "\n")
|
||||||
|
cleanedString := ""
|
||||||
|
for _, l := range lines {
|
||||||
|
l = strings.TrimSuffix(l, "\r")
|
||||||
|
l = strings.TrimSuffix(l, "\\")
|
||||||
|
l = strings.TrimSpace(l)
|
||||||
|
l = strings.TrimPrefix(l, "\"")
|
||||||
|
l = strings.TrimSuffix(l, "\"")
|
||||||
|
l = strings.ReplaceAll(l, "\\\"", "\"")
|
||||||
|
l = strings.ReplaceAll(l, "\\n", "\n")
|
||||||
|
cleanedString += l
|
||||||
|
}
|
||||||
|
self.Config[ConfigOption{
|
||||||
|
Name: k,
|
||||||
|
Type: "multilinestring",
|
||||||
|
}] = cleanedString
|
||||||
|
} else {
|
||||||
|
cleanedString := strings.TrimSuffix(strings.TrimPrefix(v, "\""), "\"")
|
||||||
|
cleanedString = strings.ReplaceAll(cleanedString, "\\n", "\n")
|
||||||
|
cleanedString = strings.ReplaceAll(cleanedString, "\r", "")
|
||||||
|
cleanedString = strings.ReplaceAll(cleanedString, "\\\"", "\"")
|
||||||
|
self.Config[ConfigOption{
|
||||||
|
Name: k,
|
||||||
|
Type: "string",
|
||||||
|
}] = cleanedString
|
||||||
|
}
|
||||||
|
} else if strings.Contains(v, ".") {
|
||||||
|
_, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.Config[ConfigOption{
|
||||||
|
Name: k,
|
||||||
|
Type: "float",
|
||||||
|
}] = v
|
||||||
|
} else {
|
||||||
|
_, err := strconv.ParseInt(v, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.Config[ConfigOption{
|
||||||
|
Name: k,
|
||||||
|
Type: "int",
|
||||||
|
}] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *EurekaAdapter) writeCfg() error {
|
||||||
|
f, err := os.Create(filepath.Join(self.Root, "config.h"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
for k, v := range self.Config {
|
||||||
|
switch k.Type {
|
||||||
|
case "int":
|
||||||
|
_, err := strconv.ParseInt(v, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.WriteString("#define " + k.Name + " " + v + "\n")
|
||||||
|
case "float":
|
||||||
|
_, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.WriteString("#define " + k.Name + " " + v + "\n")
|
||||||
|
case "string":
|
||||||
|
fallthrough
|
||||||
|
case "multilinestring":
|
||||||
|
v = strings.ReplaceAll(v, "\"", "\\\"")
|
||||||
|
v = strings.ReplaceAll(v, "\n", "\\n\" \\\n\"")
|
||||||
|
v = strings.ReplaceAll(v, "\r", "")
|
||||||
|
f.WriteString("#define " + k.Name + " \"" + v + "\"\n")
|
||||||
|
default:
|
||||||
|
return errors.New("Unsupported config value type: " + k.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
Title string
|
Title string
|
||||||
Slug string
|
|
||||||
Content string
|
Content string
|
||||||
Edited time.Time
|
Edited time.Time
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,3 +36,32 @@ func EnsurePageData(next http.Handler, adapter core.Adapter) http.Handler {
|
||||||
|
|
||||||
return http.HandlerFunc(handlerFunc)
|
return http.HandlerFunc(handlerFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SanitizeFormMap(next http.Handler) http.Handler {
|
||||||
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
delete(req.PostForm, "csrfToken")
|
||||||
|
next.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(handlerFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormMapToAdapterConfig(next http.Handler, adapter core.Adapter) http.Handler {
|
||||||
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
cfg := make(map[core.ConfigOption]string)
|
||||||
|
for k, arr := range req.PostForm {
|
||||||
|
v := strings.Join(arr, "")
|
||||||
|
optNameAndType := strings.Split(k, ":")
|
||||||
|
optName := optNameAndType[0]
|
||||||
|
optType := optNameAndType[1]
|
||||||
|
cfg[core.ConfigOption{
|
||||||
|
Name: optName,
|
||||||
|
Type: optType,
|
||||||
|
}] = v
|
||||||
|
}
|
||||||
|
*req = *req.WithContext(context.WithValue(req.Context(), "config", cfg))
|
||||||
|
next.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(handlerFunc)
|
||||||
|
}
|
||||||
|
|
97
nirvash.go
97
nirvash.go
|
@ -27,14 +27,18 @@ func main() {
|
||||||
|
|
||||||
cfg.Adapter.Init(cfg)
|
cfg.Adapter.Init(cfg)
|
||||||
|
|
||||||
|
pathConcat := filepath.Join
|
||||||
|
|
||||||
rtr := &router.Router{
|
rtr := &router.Router{
|
||||||
StaticPaths: map[string]string{
|
StaticPaths: map[string]string{
|
||||||
"/static": cfg.AssetRoot,
|
"/static": filepath.Join(cfg.AssetRoot, "static"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
templateRoot := pathConcat(cfg.AssetRoot, "templates")
|
||||||
|
|
||||||
rtr.Get("/login", renderer.Template(
|
rtr.Get("/login", renderer.Template(
|
||||||
"templates/login.html"))
|
pathConcat(templateRoot, "login.html")))
|
||||||
|
|
||||||
rtr.Post("/login", middleware.Authorize("/", udb, "/login?tryagain=1"))
|
rtr.Post("/login", middleware.Authorize("/", udb, "/login?tryagain=1"))
|
||||||
|
|
||||||
|
@ -45,9 +49,9 @@ func main() {
|
||||||
middleware.Protected(
|
middleware.Protected(
|
||||||
shell.WithAdapter(
|
shell.WithAdapter(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
"templates/cms_list.html",
|
pathConcat(templateRoot, "cms_list.html"),
|
||||||
"templates/header.html",
|
pathConcat(templateRoot, "header.html"),
|
||||||
"templates/footer.html"),
|
pathConcat(templateRoot, "footer.html")),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
udb,
|
udb,
|
||||||
|
@ -59,9 +63,9 @@ func main() {
|
||||||
middleware.Protected(
|
middleware.Protected(
|
||||||
shell.WithAdapter(
|
shell.WithAdapter(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
"templates/cms_edit.html",
|
pathConcat(templateRoot, "cms_edit.html"),
|
||||||
"templates/header.html",
|
pathConcat(templateRoot, "header.html"),
|
||||||
"templates/footer.html"),
|
pathConcat(templateRoot, "footer.html")),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
udb,
|
udb,
|
||||||
|
@ -74,9 +78,9 @@ func main() {
|
||||||
shell.WithAdapter(
|
shell.WithAdapter(
|
||||||
shell.EnsurePageData(
|
shell.EnsurePageData(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
"templates/cms_save.html",
|
pathConcat(templateRoot, "cms_save.html"),
|
||||||
"templates/header.html",
|
pathConcat(templateRoot, "header.html"),
|
||||||
"templates/footer.html"),
|
pathConcat(templateRoot, "footer.html")),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
|
@ -91,9 +95,9 @@ func main() {
|
||||||
middleware.Protected(
|
middleware.Protected(
|
||||||
shell.WithAdapter(
|
shell.WithAdapter(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
"templates/cms_new.html",
|
pathConcat(templateRoot, "cms_new.html"),
|
||||||
"templates/header.html",
|
pathConcat(templateRoot, "header.html"),
|
||||||
"templates/footer.html"),
|
pathConcat(templateRoot, "footer.html")),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
udb,
|
udb,
|
||||||
|
@ -106,9 +110,9 @@ func main() {
|
||||||
shell.WithAdapter(
|
shell.WithAdapter(
|
||||||
shell.EnsurePageData(
|
shell.EnsurePageData(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
"templates/cms_create.html",
|
pathConcat(templateRoot, "cms_create.html"),
|
||||||
"templates/header.html",
|
pathConcat(templateRoot, "header.html"),
|
||||||
"templates/footer.html"),
|
pathConcat(templateRoot, "footer.html")),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
|
@ -123,9 +127,9 @@ func main() {
|
||||||
middleware.Protected(
|
middleware.Protected(
|
||||||
shell.WithAdapter(
|
shell.WithAdapter(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
"templates/build.html",
|
pathConcat(templateRoot, "build.html"),
|
||||||
"templates/header.html",
|
pathConcat(templateRoot, "header.html"),
|
||||||
"templates/footer.html"),
|
pathConcat(templateRoot, "footer.html")),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
udb,
|
udb,
|
||||||
|
@ -135,12 +139,13 @@ func main() {
|
||||||
`/build-run`,
|
`/build-run`,
|
||||||
middleware.Defend(
|
middleware.Defend(
|
||||||
middleware.Protected(
|
middleware.Protected(
|
||||||
shell.WithAdapter(
|
shell.SanitizeFormMap(
|
||||||
renderer.Template(
|
shell.WithAdapter(
|
||||||
"templates/build_run.html",
|
renderer.Template(
|
||||||
"templates/header.html",
|
pathConcat(templateRoot, "build_run.html"),
|
||||||
"templates/footer.html"),
|
pathConcat(templateRoot, "header.html"),
|
||||||
cfg.Adapter),
|
pathConcat(templateRoot, "footer.html")),
|
||||||
|
cfg.Adapter)),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
udb,
|
udb,
|
||||||
"/login"),
|
"/login"),
|
||||||
|
@ -153,14 +158,48 @@ func main() {
|
||||||
middleware.Protected(
|
middleware.Protected(
|
||||||
shell.WithAdapter(
|
shell.WithAdapter(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
"templates/delete.html",
|
pathConcat(templateRoot, "delete.html"),
|
||||||
"templates/header.html",
|
pathConcat(templateRoot, "header.html"),
|
||||||
"templates/footer.html"),
|
pathConcat(templateRoot, "footer.html")),
|
||||||
cfg.Adapter),
|
cfg.Adapter),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
udb,
|
udb,
|
||||||
"/login"),
|
"/login"),
|
||||||
udb,
|
udb,
|
||||||
"/"))
|
"/"))
|
||||||
|
|
||||||
|
rtr.Get(
|
||||||
|
`/config`,
|
||||||
|
middleware.Fortify(
|
||||||
|
middleware.Protected(
|
||||||
|
shell.WithAdapter(
|
||||||
|
renderer.Template(
|
||||||
|
pathConcat(templateRoot, "config.html"),
|
||||||
|
pathConcat(templateRoot, "header.html"),
|
||||||
|
pathConcat(templateRoot, "footer.html")),
|
||||||
|
cfg.Adapter),
|
||||||
|
http.MethodGet,
|
||||||
|
udb,
|
||||||
|
"/login")))
|
||||||
|
|
||||||
|
rtr.Post(
|
||||||
|
`/config-set`,
|
||||||
|
middleware.Defend(
|
||||||
|
middleware.Protected(
|
||||||
|
shell.SanitizeFormMap(
|
||||||
|
shell.FormMapToAdapterConfig(
|
||||||
|
shell.WithAdapter(
|
||||||
|
renderer.Template(
|
||||||
|
pathConcat(templateRoot, "config_set.html"),
|
||||||
|
pathConcat(templateRoot, "header.html"),
|
||||||
|
pathConcat(templateRoot, "footer.html")),
|
||||||
|
cfg.Adapter),
|
||||||
|
cfg.Adapter)),
|
||||||
|
http.MethodGet,
|
||||||
|
udb,
|
||||||
|
"/login"),
|
||||||
|
udb,
|
||||||
|
"/"))
|
||||||
|
|
||||||
http.ListenAndServe(":8080", rtr)
|
http.ListenAndServe(":8080", rtr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
body {
|
body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: sans;
|
font-family: sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: black;
|
background: black;
|
||||||
|
@ -37,7 +37,18 @@ body {
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login form input {
|
.login label {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
font-size: 0;
|
||||||
|
display: inline;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.login form input, .login form input:-internal-autofill-selected {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -52,6 +63,7 @@ body {
|
||||||
.login form input[type="text"], login form input[type="password"] {
|
.login form input[type="text"], login form input[type="password"] {
|
||||||
transition: border 1s;
|
transition: border 1s;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
margin-bottom: -17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login form input:focus {
|
.login form input:focus {
|
||||||
|
@ -61,6 +73,7 @@ body {
|
||||||
|
|
||||||
|
|
||||||
.login form input[type="submit"] {
|
.login form input[type="submit"] {
|
||||||
|
margin-top: -17px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
transition: background 1s, color 1s;
|
transition: background 1s, color 1s;
|
||||||
}
|
}
|
||||||
|
@ -155,7 +168,9 @@ h2 {
|
||||||
top: 3em;
|
top: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-list, form.editor, form.build, span.adapter-error, span.adapter-success, .danger-zone {
|
.page-list, form.editor, form.build, form.configurator, span.adapter-error, span.adapter-success, .danger-zone {
|
||||||
|
display: block;
|
||||||
|
overflow-x: hidden;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
background: rgba(0,0,0,0.8);
|
background: rgba(0,0,0,0.8);
|
||||||
|
@ -166,13 +181,17 @@ h2 {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor label, form.build label {
|
span.adapter-error {
|
||||||
|
border-bottom: 2px solid crimson;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.editor label, form.build label, .danger-zone label, form.configurator label {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor input, form.build input, form.editor textarea {
|
form.editor input, form.build input, form.editor textarea, form.configurator input, form.configurator textarea, .danger-zone input[type="submit"] {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-top: 0.2em;
|
margin-top: 0.2em;
|
||||||
|
@ -185,24 +204,42 @@ form.editor input, form.build input, form.editor textarea {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor input.title-input {
|
form.editor input[type="text"], form.configurator input[type="text"], form.configurator input[type="number"] {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
form.editor input.title-input {
|
||||||
font-size: 150%;
|
font-size: 150%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form input:focus, form textarea:focus {
|
||||||
|
border: 2px solid cyan;
|
||||||
|
}
|
||||||
|
|
||||||
form.editor, .danger-zone {
|
form.editor, .danger-zone {
|
||||||
max-width: 80em;
|
max-width: 80em;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor textarea {
|
form.editor textarea {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 80em;
|
width: 100%;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
height: 25em;
|
height: 25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor input[type="submit"], form.build input[type="submit"] {
|
form.configurator textarea {
|
||||||
|
marign: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.configurator input, form.configurator textarea {
|
||||||
|
font-size: 125%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.editor input[type="submit"], form.build input[type="submit"], .danger-zone input[type="submit"], form.configurator input[type="submit"] {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
font-size: 150%;
|
font-size: 150%;
|
||||||
|
@ -210,7 +247,7 @@ form.editor input[type="submit"], form.build input[type="submit"] {
|
||||||
transition: background 1s, color 1s;
|
transition: background 1s, color 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor input[type="submit"]:hover {
|
form.editor input[type="submit"]:hover,form.build input[type="submit"]:hover, .danger-zone input[type="submit"]:hover, form.configurator input[type="submit"]:hover {
|
||||||
background: lightgray;
|
background: lightgray;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
25
templates/config.html
Normal file
25
templates/config.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{{ $config := ((.Context).Value "adapter").GetConfig }}
|
||||||
|
{{ $csrfToken := (.Context).Value "csrfToken" }}
|
||||||
|
|
||||||
|
{{ template "header" . }}
|
||||||
|
|
||||||
|
<h2>Configuration</h2>
|
||||||
|
|
||||||
|
<form class="configurator" method="POST" action="/config-set">
|
||||||
|
<input hidden type="text" name="csrfToken" value="{{$csrfToken}}"/>
|
||||||
|
{{ range $opt, $val := $config }}
|
||||||
|
{{ if eq ($opt).Type "int" }}
|
||||||
|
<label>{{($opt).Name}} <input type="number" step="1" name="{{($opt).Name}}:{{($opt).Type}}" value="{{$val}}"/></label><br/>
|
||||||
|
{{ else if eq ($opt).Type "float" }}
|
||||||
|
<label>{{($opt).Name}} <input type="number" step="0.00000001" name="{{($opt).Name}}:{{($opt).Type}}" value="{{$val}}"/></label><br/>
|
||||||
|
{{ else if eq ($opt).Type "string" }}
|
||||||
|
<label>{{($opt).Name}} <input type="text" name="{{($opt).Name}}:{{($opt).Type}}" value="{{$val}}"/></label><br/>
|
||||||
|
{{ else if eq ($opt).Type "multilinestring" }}
|
||||||
|
<label>{{($opt).Name}} <textarea name="{{($opt).Name}}:{{($opt).Type}}">{{$val}}</textarea></label><br/>
|
||||||
|
{{ end}}
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
<input type="submit" value="Save"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{{ template "footer" . }}
|
14
templates/config_set.html
Normal file
14
templates/config_set.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{{ $config := (.Context).Value "config" }}
|
||||||
|
{{ $cfgError := ((.Context).Value "adapter").SetConfig $config }}
|
||||||
|
|
||||||
|
{{ template "header" . }}
|
||||||
|
|
||||||
|
{{ if $cfgError }}
|
||||||
|
<h2>Configuration Error</h2>
|
||||||
|
<span class="adapter-error">{{($cfgError).Error}}</span>
|
||||||
|
{{ else }}
|
||||||
|
<h2>Configuration Saved</h2>
|
||||||
|
<span class="adapter-success">The adapter configuration has been saved</span>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ template "footer" . }}
|
|
@ -17,8 +17,10 @@
|
||||||
<span class="error">Incorrect credentials; please try again.</span>
|
<span class="error">Incorrect credentials; please try again.</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<form action='/login' method='post'>
|
<form action='/login' method='post'>
|
||||||
<input type="text" name="user" placeholder="user">
|
<label for="user-input">Username</label>
|
||||||
<input type="password" name="password" placeholder="password">
|
<input type="text" id="user-input" name="user" placeholder="user"><br/>
|
||||||
|
<label for="password-input">Password</label>
|
||||||
|
<input type="password" id="password-input" name="password" placeholder="password"><br/>
|
||||||
<input type="submit" value="Login">
|
<input type="submit" value="Login">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue