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
|
||||
}
|
||||
|
||||
type ConfigOption struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
type Adapter interface {
|
||||
Init(cfg *Config)
|
||||
Name() string
|
||||
EditableSlugs() bool
|
||||
BuildOptions() []string
|
||||
GetConfig(key string) (interface{}, error)
|
||||
SetConfig(key string, value interface{}) error
|
||||
GetConfig() map[ConfigOption]string
|
||||
SetConfig(map[ConfigOption]string) error
|
||||
ListPages() map[string]string
|
||||
GetPage(string) (Page, error)
|
||||
FormatPage(string) string
|
||||
|
|
|
@ -6,12 +6,13 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type EurekaAdapter struct {
|
||||
Root string
|
||||
Config map[string]interface{}
|
||||
Config map[ConfigOption]string
|
||||
}
|
||||
|
||||
func (self *EurekaAdapter) Init(cfg *Config) {
|
||||
|
@ -23,8 +24,12 @@ func (self *EurekaAdapter) Init(cfg *Config) {
|
|||
}
|
||||
|
||||
self.Root = cfg.Root
|
||||
|
||||
self.Config = make(map[ConfigOption]string)
|
||||
// TODO: read config.h and build self.Config
|
||||
err = self.readCfg()
|
||||
if err != nil {
|
||||
panic("config.h is malformed!")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *EurekaAdapter) Name() string {
|
||||
|
@ -39,12 +44,13 @@ func (self *EurekaAdapter) BuildOptions() []string {
|
|||
return []string{"twtxt"}
|
||||
}
|
||||
|
||||
func (self *EurekaAdapter) GetConfig(key string) (interface{}, error) {
|
||||
return nil, nil
|
||||
func (self *EurekaAdapter) GetConfig() map[ConfigOption]string {
|
||||
return self.Config
|
||||
}
|
||||
|
||||
func (self *EurekaAdapter) SetConfig(key string, value interface{}) error {
|
||||
return nil
|
||||
func (self *EurekaAdapter) SetConfig(cfg map[ConfigOption]string) error {
|
||||
self.Config = cfg
|
||||
return self.writeCfg()
|
||||
}
|
||||
|
||||
func (self *EurekaAdapter) ListPages() map[string]string {
|
||||
|
@ -85,7 +91,6 @@ func (self *EurekaAdapter) GetPage(filename string) (Page, error) {
|
|||
|
||||
return Page{
|
||||
Title: title,
|
||||
Slug: filename,
|
||||
Content: content,
|
||||
Edited: fileInfo.ModTime(),
|
||||
}, nil
|
||||
|
@ -129,7 +134,15 @@ func (self *EurekaAdapter) SavePage(oldSlug, newSlug, title, content string) err
|
|||
defer f.Close()
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -138,7 +151,15 @@ func (self *EurekaAdapter) SavePage(oldSlug, newSlug, title, content string) err
|
|||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -147,7 +168,7 @@ func (self *EurekaAdapter) Build(buildOptions map[string][]string) BuildStatus {
|
|||
twtxt := buildOptions["twtxt"][0]
|
||||
cmdArgs := ""
|
||||
if twtxt != "" {
|
||||
cmdArgs += " -t " + twtxt
|
||||
cmdArgs += "-t " + twtxt
|
||||
}
|
||||
|
||||
cmd := exec.Command("./build.sh", cmdArgs)
|
||||
|
@ -159,3 +180,111 @@ func (self *EurekaAdapter) Build(buildOptions map[string][]string) BuildStatus {
|
|||
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 {
|
||||
Title string
|
||||
Slug string
|
||||
Content string
|
||||
Edited time.Time
|
||||
}
|
||||
|
|
|
@ -36,3 +36,32 @@ func EnsurePageData(next http.Handler, adapter core.Adapter) http.Handler {
|
|||
|
||||
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)
|
||||
|
||||
pathConcat := filepath.Join
|
||||
|
||||
rtr := &router.Router{
|
||||
StaticPaths: map[string]string{
|
||||
"/static": cfg.AssetRoot,
|
||||
"/static": filepath.Join(cfg.AssetRoot, "static"),
|
||||
},
|
||||
}
|
||||
|
||||
templateRoot := pathConcat(cfg.AssetRoot, "templates")
|
||||
|
||||
rtr.Get("/login", renderer.Template(
|
||||
"templates/login.html"))
|
||||
pathConcat(templateRoot, "login.html")))
|
||||
|
||||
rtr.Post("/login", middleware.Authorize("/", udb, "/login?tryagain=1"))
|
||||
|
||||
|
@ -45,9 +49,9 @@ func main() {
|
|||
middleware.Protected(
|
||||
shell.WithAdapter(
|
||||
renderer.Template(
|
||||
"templates/cms_list.html",
|
||||
"templates/header.html",
|
||||
"templates/footer.html"),
|
||||
pathConcat(templateRoot, "cms_list.html"),
|
||||
pathConcat(templateRoot, "header.html"),
|
||||
pathConcat(templateRoot, "footer.html")),
|
||||
cfg.Adapter),
|
||||
http.MethodGet,
|
||||
udb,
|
||||
|
@ -59,9 +63,9 @@ func main() {
|
|||
middleware.Protected(
|
||||
shell.WithAdapter(
|
||||
renderer.Template(
|
||||
"templates/cms_edit.html",
|
||||
"templates/header.html",
|
||||
"templates/footer.html"),
|
||||
pathConcat(templateRoot, "cms_edit.html"),
|
||||
pathConcat(templateRoot, "header.html"),
|
||||
pathConcat(templateRoot, "footer.html")),
|
||||
cfg.Adapter),
|
||||
http.MethodGet,
|
||||
udb,
|
||||
|
@ -74,9 +78,9 @@ func main() {
|
|||
shell.WithAdapter(
|
||||
shell.EnsurePageData(
|
||||
renderer.Template(
|
||||
"templates/cms_save.html",
|
||||
"templates/header.html",
|
||||
"templates/footer.html"),
|
||||
pathConcat(templateRoot, "cms_save.html"),
|
||||
pathConcat(templateRoot, "header.html"),
|
||||
pathConcat(templateRoot, "footer.html")),
|
||||
cfg.Adapter),
|
||||
cfg.Adapter),
|
||||
http.MethodGet,
|
||||
|
@ -91,9 +95,9 @@ func main() {
|
|||
middleware.Protected(
|
||||
shell.WithAdapter(
|
||||
renderer.Template(
|
||||
"templates/cms_new.html",
|
||||
"templates/header.html",
|
||||
"templates/footer.html"),
|
||||
pathConcat(templateRoot, "cms_new.html"),
|
||||
pathConcat(templateRoot, "header.html"),
|
||||
pathConcat(templateRoot, "footer.html")),
|
||||
cfg.Adapter),
|
||||
http.MethodGet,
|
||||
udb,
|
||||
|
@ -106,9 +110,9 @@ func main() {
|
|||
shell.WithAdapter(
|
||||
shell.EnsurePageData(
|
||||
renderer.Template(
|
||||
"templates/cms_create.html",
|
||||
"templates/header.html",
|
||||
"templates/footer.html"),
|
||||
pathConcat(templateRoot, "cms_create.html"),
|
||||
pathConcat(templateRoot, "header.html"),
|
||||
pathConcat(templateRoot, "footer.html")),
|
||||
cfg.Adapter),
|
||||
cfg.Adapter),
|
||||
http.MethodGet,
|
||||
|
@ -123,9 +127,9 @@ func main() {
|
|||
middleware.Protected(
|
||||
shell.WithAdapter(
|
||||
renderer.Template(
|
||||
"templates/build.html",
|
||||
"templates/header.html",
|
||||
"templates/footer.html"),
|
||||
pathConcat(templateRoot, "build.html"),
|
||||
pathConcat(templateRoot, "header.html"),
|
||||
pathConcat(templateRoot, "footer.html")),
|
||||
cfg.Adapter),
|
||||
http.MethodGet,
|
||||
udb,
|
||||
|
@ -135,12 +139,13 @@ func main() {
|
|||
`/build-run`,
|
||||
middleware.Defend(
|
||||
middleware.Protected(
|
||||
shell.WithAdapter(
|
||||
renderer.Template(
|
||||
"templates/build_run.html",
|
||||
"templates/header.html",
|
||||
"templates/footer.html"),
|
||||
cfg.Adapter),
|
||||
shell.SanitizeFormMap(
|
||||
shell.WithAdapter(
|
||||
renderer.Template(
|
||||
pathConcat(templateRoot, "build_run.html"),
|
||||
pathConcat(templateRoot, "header.html"),
|
||||
pathConcat(templateRoot, "footer.html")),
|
||||
cfg.Adapter)),
|
||||
http.MethodGet,
|
||||
udb,
|
||||
"/login"),
|
||||
|
@ -153,14 +158,48 @@ func main() {
|
|||
middleware.Protected(
|
||||
shell.WithAdapter(
|
||||
renderer.Template(
|
||||
"templates/delete.html",
|
||||
"templates/header.html",
|
||||
"templates/footer.html"),
|
||||
pathConcat(templateRoot, "delete.html"),
|
||||
pathConcat(templateRoot, "header.html"),
|
||||
pathConcat(templateRoot, "footer.html")),
|
||||
cfg.Adapter),
|
||||
http.MethodGet,
|
||||
udb,
|
||||
"/login"),
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: sans;
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
height: 100vh;
|
||||
background: black;
|
||||
|
@ -37,7 +37,18 @@ body {
|
|||
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;
|
||||
margin: 1em;
|
||||
margin-left: auto;
|
||||
|
@ -52,6 +63,7 @@ body {
|
|||
.login form input[type="text"], login form input[type="password"] {
|
||||
transition: border 1s;
|
||||
outline: none;
|
||||
margin-bottom: -17px;
|
||||
}
|
||||
|
||||
.login form input:focus {
|
||||
|
@ -61,6 +73,7 @@ body {
|
|||
|
||||
|
||||
.login form input[type="submit"] {
|
||||
margin-top: -17px;
|
||||
text-transform: uppercase;
|
||||
transition: background 1s, color 1s;
|
||||
}
|
||||
|
@ -155,7 +168,9 @@ h2 {
|
|||
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%;
|
||||
max-width: 500px;
|
||||
background: rgba(0,0,0,0.8);
|
||||
|
@ -166,13 +181,17 @@ h2 {
|
|||
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%;
|
||||
color: lightgray;
|
||||
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;
|
||||
margin: 0;
|
||||
margin-top: 0.2em;
|
||||
|
@ -185,24 +204,42 @@ form.editor input, form.build input, form.editor textarea {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
form.editor input.title-input {
|
||||
form.editor input[type="text"], form.configurator input[type="text"], form.configurator input[type="number"] {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
form.editor input.title-input {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
form input:focus, form textarea:focus {
|
||||
border: 2px solid cyan;
|
||||
}
|
||||
|
||||
form.editor, .danger-zone {
|
||||
max-width: 80em;
|
||||
}
|
||||
|
||||
form.editor textarea {
|
||||
margin: 0;
|
||||
width: 80em;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
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-right: 0;
|
||||
font-size: 150%;
|
||||
|
@ -210,7 +247,7 @@ form.editor input[type="submit"], form.build input[type="submit"] {
|
|||
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;
|
||||
color: black;
|
||||
}
|
||||
|
@ -234,4 +271,4 @@ form.editor input[type="submit"]:hover {
|
|||
|
||||
form input[hidden] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
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>
|
||||
{{ end }}
|
||||
<form action='/login' method='post'>
|
||||
<input type="text" name="user" placeholder="user">
|
||||
<input type="password" name="password" placeholder="password">
|
||||
<label for="user-input">Username</label>
|
||||
<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">
|
||||
</form>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue