summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve <nilslice@gmail.com>2016-12-28 16:23:44 -0800
committerGitHub <noreply@github.com>2016-12-28 16:23:44 -0800
commit806fdbe1e8839feb1bcc4e5e07aa7c144a429901 (patch)
treedacaed91abb57bc6891c1b2cb58a82fc0c9610fc
parent1f7a5b46b906da00ceccb03e7ff26627bb29a3dd (diff)
parent5c340ca57e876a556a5b57e5a7dd32b0ae288440 (diff)
Merge pull request #26 from ponzu-cms/ponzu-dev
[tooling] Add Go command flag, auto-generate self-signed SSL for development
-rw-r--r--cmd/ponzu/main.go162
-rw-r--r--cmd/ponzu/options.go15
-rw-r--r--cmd/ponzu/usage.go129
-rw-r--r--cmd/ponzu/vendor/golang.org/x/net/context/context_test.go40
-rw-r--r--management/editor/dom.go258
-rw-r--r--management/editor/editor.go54
-rw-r--r--management/editor/elements.go34
-rw-r--r--management/editor/repeaters.go94
-rw-r--r--system/api/handlers.go14
-rw-r--r--system/db/config.go5
-rw-r--r--system/tls/devcerts.go148
-rw-r--r--system/tls/enable.go6
-rw-r--r--system/tls/enabledev.go29
13 files changed, 722 insertions, 266 deletions
diff --git a/cmd/ponzu/main.go b/cmd/ponzu/main.go
index d2e8b97..440ce70 100644
--- a/cmd/ponzu/main.go
+++ b/cmd/ponzu/main.go
@@ -8,7 +8,6 @@ import (
"os"
"os/exec"
"strings"
- "time"
"github.com/ponzu-cms/ponzu/system/admin"
"github.com/ponzu-cms/ponzu/system/api"
@@ -20,150 +19,29 @@ import (
_ "github.com/ponzu-cms/ponzu/content"
)
-var year = fmt.Sprintf("%d", time.Now().Year())
-
-var usageHeader = `
-$ ponzu [flags] command <params>
-
-Ponzu is a powerful and efficient open-source "Content-as-a-Service" system
-framework and CMS. It provides automatic, free, and secure HTTP/2 over TLS
-(certificates obtained via Let's Encrypt - https://letsencrypt.org), a useful
-CMS and scaffolding to generate content editors, and a fast HTTP API on which
-to build modern applications.
-
-Ponzu is released under the BSD-3-Clause license (see LICENSE).
-(c) ` + year + ` Boss Sauce Creative, LLC
-
-COMMANDS
-
-`
-
-var usageHelp = `
-help, h (command):
-
- Help command will print the usage for Ponzu, or if a command is entered, it
- will show only the usage for that specific command.
-
- Example:
- $ ponzu help generate
-
-
-`
-
-var usageNew = `
-new <directory>:
-
- Creates a 'ponzu' directory, or one by the name supplied as a parameter
- immediately following the 'new' option in the $GOPATH/src directory. Note:
- 'new' depends on the program 'git' and possibly a network connection. If
- there is no local repository to clone from at the local machine's $GOPATH,
- 'new' will attempt to clone the 'github.com/ponzu-cms/ponzu' package from
- over the network.
-
- Example:
- $ ponzu new myProject
- > New ponzu project created at $GOPATH/src/myProject
-
- Errors will be reported, but successful commands retrun nothing.
-
-
-`
-
-var usageGenerate = `
-generate, gen, g <type (,...fields)>:
-
- Generate a content type file with boilerplate code to implement
- the editor.Editable interface. Must be given one (1) parameter of
- the name of the type for the new content. The fields following a
- type determine the field names and types of the content struct to
- be generated. These must be in the following format:
- fieldName:"T"
-
- Example:
- $ ponzu gen review title:"string" body:"string" rating:"int" tags:"[]string"
-
- The command above will generate a file 'content/review.go' with boilerplate
- methods, as well as struct definition, and cooresponding field tags like:
-
- type Review struct {
- Title string ` + "`json:" + `"title"` + "`" + `
- Body string ` + "`json:" + `"body"` + "`" + `
- Rating int ` + "`json:" + `"rating"` + "`" + `
- Tags []string ` + "`json:" + `"tags"` + "`" + `
- }
-
- The generate command will intelligently parse more sophisticated field names
- such as 'field_name' and convert it to 'FieldName' and vice versa, only where
- appropriate as per common Go idioms. Errors will be reported, but successful
- generate commands retrun nothing.
-
-
-`
-
-var usageBuild = `
-build
-
- From within your Ponzu project directory, running build will copy and move
- the necessary files from your workspace into the vendored directory, and
- will build/compile the project to then be run.
-
- Example:
- $ ponzu build
-
- Errors will be reported, but successful build commands return nothing.
-
-`
-
-var usageRun = `
-[[--port=8080] [--https]] run <service(,service)>:
-
- Starts the 'ponzu' HTTP server for the JSON API, Admin System, or both.
- The segments, separated by a comma, describe which services to start, either
- 'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally,
- if the server should utilize TLS encryption - served over HTTPS, which is
- automatically managed using Let's Encrypt (https://letsencrypt.org)
-
- Example:
- $ ponzu run
- (or)
- $ ponzu --port=8080 --https run admin,api
- (or)
- $ ponzu run admin
- (or)
- $ ponzu --port=8888 run api
-
- Defaults to '--port=8080 run admin,api' (running Admin & API on port 8080, without TLS)
-
- Note:
- Admin and API cannot run on separate processes unless you use a copy of the
- database, since the first process to open it receives a lock. If you intend
- to run the Admin and API on separate processes, you must call them with the
- 'ponzu' command independently.
-
-
-`
-
var (
- usage = usageHeader + usageNew + usageGenerate + usageBuild + usageRun
- port int
- https bool
+ usage = usageHeader + usageNew + usageGenerate + usageBuild + usageRun
+ port int
+ https bool
+ devhttps bool
// for ponzu internal / core development
- dev bool
- fork string
+ dev bool
+ fork string
+ gocmd string
)
-func init() {
+func main() {
flag.Usage = func() {
fmt.Println(usage)
}
-}
-func main() {
flag.IntVar(&port, "port", 8080, "port for ponzu to bind its listener")
flag.BoolVar(&https, "https", false, "enable automatic TLS/SSL certificate management")
+ flag.BoolVar(&devhttps, "devhttps", false, "[dev environment] enable automatic TLS/SSL certificate management")
flag.BoolVar(&dev, "dev", false, "modify environment for Ponzu core development")
flag.StringVar(&fork, "fork", "", "modify repo source for Ponzu core development")
+ flag.StringVar(&gocmd, "gocmd", "go", "custom go command if using beta or new release of Go")
flag.Parse()
args := flag.Args()
@@ -238,6 +116,10 @@ func main() {
addTLS = "--https=false"
}
+ if devhttps {
+ addTLS = "--devhttps"
+ }
+
var services string
if len(args) > 1 {
services = args[1]
@@ -289,9 +171,21 @@ func main() {
}
}
- if https {
+ // cannot run production HTTPS and development HTTPS together
+ if devhttps {
+ fmt.Println("Enabling self-signed HTTPS... [DEV]")
+
+ go tls.EnableDev()
+ fmt.Println("Server listening on https://localhost:10443 for requests... [DEV]")
+ fmt.Println("----")
+ fmt.Println("If your browser rejects HTTPS requests, try allowing insecure connections on localhost.")
+ fmt.Println("on Chrome, visit chrome://flags/#allow-insecure-localhost")
+
+ } else if https {
fmt.Println("Enabling HTTPS...")
- tls.Enable()
+
+ go tls.Enable()
+ fmt.Println("Server listening on :443 for HTTPS requests...")
}
// save the port the system is listening on so internal system can make
diff --git a/cmd/ponzu/options.go b/cmd/ponzu/options.go
index 7e37255..1316a67 100644
--- a/cmd/ponzu/options.go
+++ b/cmd/ponzu/options.go
@@ -271,10 +271,17 @@ func buildPonzuServer(args []string) error {
}
// execute go build -o ponzu-cms cmd/ponzu/*.go
- mainPath := filepath.Join(pwd, "cmd", "ponzu", "main.go")
- optsPath := filepath.Join(pwd, "cmd", "ponzu", "options.go")
- genPath := filepath.Join(pwd, "cmd", "ponzu", "generate.go")
- build := exec.Command("go", "build", "-o", "ponzu-server", mainPath, optsPath, genPath)
+ buildOptions := []string{"build", "-o", "ponzu-server"}
+ cmdBuildFiles := []string{"main.go", "options.go", "generate.go", "usage.go"}
+ var cmdBuildFilePaths []string
+ for _, file := range cmdBuildFiles {
+ p := filepath.Join(pwd, "cmd", "ponzu", file)
+ cmdBuildFilePaths = append(cmdBuildFilePaths, p)
+ }
+ // mainPath := filepath.Join(pwd, "cmd", "ponzu", "main.go")
+ // optsPath := filepath.Join(pwd, "cmd", "ponzu", "options.go")
+ // genPath := filepath.Join(pwd, "cmd", "ponzu", "generate.go")
+ build := exec.Command(gocmd, append(buildOptions, cmdBuildFilePaths...)...)
build.Stderr = os.Stderr
build.Stdout = os.Stdout
diff --git a/cmd/ponzu/usage.go b/cmd/ponzu/usage.go
new file mode 100644
index 0000000..2dca46d
--- /dev/null
+++ b/cmd/ponzu/usage.go
@@ -0,0 +1,129 @@
+package main
+
+import (
+ "fmt"
+ "time"
+)
+
+var year = fmt.Sprintf("%d", time.Now().Year())
+
+var usageHeader = `
+$ ponzu [flags] command <params>
+
+Ponzu is a powerful and efficient open-source "Content-as-a-Service" system
+framework and CMS. It provides automatic, free, and secure HTTP/2 over TLS
+(certificates obtained via Let's Encrypt - https://letsencrypt.org), a useful
+CMS and scaffolding to generate content editors, and a fast HTTP API on which
+to build modern applications.
+
+Ponzu is released under the BSD-3-Clause license (see LICENSE).
+(c) ` + year + ` Boss Sauce Creative, LLC
+
+COMMANDS
+
+`
+
+var usageHelp = `
+help, h (command):
+
+ Help command will print the usage for Ponzu, or if a command is entered, it
+ will show only the usage for that specific command.
+
+ Example:
+ $ ponzu help generate
+
+
+`
+
+var usageNew = `
+new <directory>:
+
+ Creates a 'ponzu' directory, or one by the name supplied as a parameter
+ immediately following the 'new' option in the $GOPATH/src directory. Note:
+ 'new' depends on the program 'git' and possibly a network connection. If
+ there is no local repository to clone from at the local machine's $GOPATH,
+ 'new' will attempt to clone the 'github.com/ponzu-cms/ponzu' package from
+ over the network.
+
+ Example:
+ $ ponzu new myProject
+ > New ponzu project created at $GOPATH/src/myProject
+
+ Errors will be reported, but successful commands retrun nothing.
+
+
+`
+
+var usageGenerate = `
+generate, gen, g <type (,...fields)>:
+
+ Generate a content type file with boilerplate code to implement
+ the editor.Editable interface. Must be given one (1) parameter of
+ the name of the type for the new content. The fields following a
+ type determine the field names and types of the content struct to
+ be generated. These must be in the following format:
+ fieldName:"T"
+
+ Example:
+ $ ponzu gen review title:"string" body:"string" rating:"int" tags:"[]string"
+
+ The command above will generate a file 'content/review.go' with boilerplate
+ methods, as well as struct definition, and cooresponding field tags like:
+
+ type Review struct {
+ Title string ` + "`json:" + `"title"` + "`" + `
+ Body string ` + "`json:" + `"body"` + "`" + `
+ Rating int ` + "`json:" + `"rating"` + "`" + `
+ Tags []string ` + "`json:" + `"tags"` + "`" + `
+ }
+
+ The generate command will intelligently parse more sophisticated field names
+ such as 'field_name' and convert it to 'FieldName' and vice versa, only where
+ appropriate as per common Go idioms. Errors will be reported, but successful
+ generate commands retrun nothing.
+
+
+`
+
+var usageBuild = `
+build
+
+ From within your Ponzu project directory, running build will copy and move
+ the necessary files from your workspace into the vendored directory, and
+ will build/compile the project to then be run.
+
+ Example:
+ $ ponzu build
+
+ Errors will be reported, but successful build commands return nothing.
+
+`
+
+var usageRun = `
+[[--port=8080] [--https]] run <service(,service)>:
+
+ Starts the 'ponzu' HTTP server for the JSON API, Admin System, or both.
+ The segments, separated by a comma, describe which services to start, either
+ 'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally,
+ if the server should utilize TLS encryption - served over HTTPS, which is
+ automatically managed using Let's Encrypt (https://letsencrypt.org)
+
+ Example:
+ $ ponzu run
+ (or)
+ $ ponzu --port=8080 --https run admin,api
+ (or)
+ $ ponzu run admin
+ (or)
+ $ ponzu --port=8888 run api
+
+ Defaults to '--port=8080 run admin,api' (running Admin & API on port 8080, without TLS)
+
+ Note:
+ Admin and API cannot run on separate processes unless you use a copy of the
+ database, since the first process to open it receives a lock. If you intend
+ to run the Admin and API on separate processes, you must call them with the
+ 'ponzu' command independently.
+
+
+`
diff --git a/cmd/ponzu/vendor/golang.org/x/net/context/context_test.go b/cmd/ponzu/vendor/golang.org/x/net/context/context_test.go
index 9554dcf..6284413 100644
--- a/cmd/ponzu/vendor/golang.org/x/net/context/context_test.go
+++ b/cmd/ponzu/vendor/golang.org/x/net/context/context_test.go
@@ -243,45 +243,51 @@ func testDeadline(c Context, wait time.Duration, t *testing.T) {
}
func TestDeadline(t *testing.T) {
- c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ t.Parallel()
+ const timeUnit = 500 * time.Millisecond
+ c, _ := WithDeadline(Background(), time.Now().Add(1*timeUnit))
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
t.Errorf("c.String() = %q want prefix %q", got, prefix)
}
- testDeadline(c, 200*time.Millisecond, t)
+ testDeadline(c, 2*timeUnit, t)
- c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit))
o := otherContext{c}
- testDeadline(o, 200*time.Millisecond, t)
+ testDeadline(o, 2*timeUnit, t)
- c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit))
o = otherContext{c}
- c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond))
- testDeadline(c, 200*time.Millisecond, t)
+ c, _ = WithDeadline(o, time.Now().Add(3*timeUnit))
+ testDeadline(c, 2*timeUnit, t)
}
func TestTimeout(t *testing.T) {
- c, _ := WithTimeout(Background(), 100*time.Millisecond)
+ t.Parallel()
+ const timeUnit = 500 * time.Millisecond
+ c, _ := WithTimeout(Background(), 1*timeUnit)
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
t.Errorf("c.String() = %q want prefix %q", got, prefix)
}
- testDeadline(c, 200*time.Millisecond, t)
+ testDeadline(c, 2*timeUnit, t)
- c, _ = WithTimeout(Background(), 100*time.Millisecond)
+ c, _ = WithTimeout(Background(), 1*timeUnit)
o := otherContext{c}
- testDeadline(o, 200*time.Millisecond, t)
+ testDeadline(o, 2*timeUnit, t)
- c, _ = WithTimeout(Background(), 100*time.Millisecond)
+ c, _ = WithTimeout(Background(), 1*timeUnit)
o = otherContext{c}
- c, _ = WithTimeout(o, 300*time.Millisecond)
- testDeadline(c, 200*time.Millisecond, t)
+ c, _ = WithTimeout(o, 3*timeUnit)
+ testDeadline(c, 2*timeUnit, t)
}
func TestCanceledTimeout(t *testing.T) {
- c, _ := WithTimeout(Background(), 200*time.Millisecond)
+ t.Parallel()
+ const timeUnit = 500 * time.Millisecond
+ c, _ := WithTimeout(Background(), 2*timeUnit)
o := otherContext{c}
- c, cancel := WithTimeout(o, 400*time.Millisecond)
+ c, cancel := WithTimeout(o, 4*timeUnit)
cancel()
- time.Sleep(100 * time.Millisecond) // let cancelation propagate
+ time.Sleep(1 * timeUnit) // let cancelation propagate
select {
case <-c.Done():
default:
diff --git a/management/editor/dom.go b/management/editor/dom.go
index cf36ad9..41aafa7 100644
--- a/management/editor/dom.go
+++ b/management/editor/dom.go
@@ -3,13 +3,14 @@ package editor
import (
"bytes"
"html"
+ "log"
"strings"
)
type element struct {
- TagName string
- Attrs map[string]string
- Name string
+ tagName string
+ attrs map[string]string
+ name string
label string
data string
viewBuf *bytes.Buffer
@@ -17,9 +18,9 @@ type element struct {
func newElement(tagName, label, fieldName string, p interface{}, attrs map[string]string) *element {
return &element{
- TagName: tagName,
- Attrs: attrs,
- Name: tagNameFromStructField(fieldName, p),
+ tagName: tagName,
+ attrs: attrs,
+ name: tagNameFromStructField(fieldName, p),
label: label,
data: valueFromStructField(fieldName, p),
viewBuf: &bytes.Buffer{},
@@ -29,108 +30,265 @@ func newElement(tagName, label, fieldName string, p interface{}, attrs map[strin
// domElementSelfClose is a special DOM element which is parsed as a
// self-closing tag and thus needs to be created differently
func domElementSelfClose(e *element) []byte {
- e.viewBuf.Write([]byte(`<div class="input-field col s12">`))
+ _, err := e.viewBuf.WriteString(`<div class="input-field col s12">`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementSelfClose")
+ return nil
+ }
+
if e.label != "" {
- e.viewBuf.Write([]byte(`<label class="active" for="` + strings.Join(strings.Split(e.label, " "), "-") + `">` + e.label + `</label>`))
+ _, err = e.viewBuf.WriteString(
+ `<label class="active" for="` +
+ strings.Join(strings.Split(e.label, " "), "-") + `">` + e.label +
+ `</label>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementSelfClose")
+ return nil
+ }
+ }
+
+ _, err = e.viewBuf.WriteString(`<` + e.tagName + ` value="`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementSelfClose")
+ return nil
+ }
+
+ _, err = e.viewBuf.WriteString(html.EscapeString(e.data) + `" `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementSelfClose")
+ return nil
+ }
+
+ for attr, value := range e.attrs {
+ _, err := e.viewBuf.WriteString(attr + `="` + value + `" `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementSelfClose")
+ return nil
+ }
+ }
+ _, err = e.viewBuf.WriteString(` name="` + e.name + `" />`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementSelfClose")
+ return nil
}
- e.viewBuf.Write([]byte(`<` + e.TagName + ` value="`))
- e.viewBuf.Write([]byte(html.EscapeString(e.data) + `" `))
- for attr, value := range e.Attrs {
- e.viewBuf.Write([]byte(attr + `="` + value + `" `))
+ _, err = e.viewBuf.WriteString(`</div>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementSelfClose")
+ return nil
}
- e.viewBuf.Write([]byte(` name="` + e.Name + `"`))
- e.viewBuf.Write([]byte(` />`))
- e.viewBuf.Write([]byte(`</div>`))
return e.viewBuf.Bytes()
}
// domElementCheckbox is a special DOM element which is parsed as a
// checkbox input tag and thus needs to be created differently
func domElementCheckbox(e *element) []byte {
- e.viewBuf.Write([]byte(`<p class="col s6">`))
- e.viewBuf.Write([]byte(`<` + e.TagName + ` `))
+ _, err := e.viewBuf.WriteString(`<p class="col s6">`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementCheckbox")
+ return nil
+ }
+
+ _, err = e.viewBuf.WriteString(`<` + e.tagName + ` `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementCheckbox")
+ return nil
+ }
- for attr, value := range e.Attrs {
- e.viewBuf.Write([]byte(attr + `="` + value + `" `))
+ for attr, value := range e.attrs {
+ _, err := e.viewBuf.WriteString(attr + `="` + value + `" `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementCheckbox")
+ return nil
+ }
}
- e.viewBuf.Write([]byte(` name="` + e.Name + `"`))
- e.viewBuf.Write([]byte(` /> `))
+ _, err = e.viewBuf.WriteString(` name="` + e.name + `" />`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementCheckbox")
+ return nil
+ }
+
if e.label != "" {
- e.viewBuf.Write([]byte(`<label for="` + strings.Join(strings.Split(e.label, " "), "-") + `">` + e.label + `</label>`))
+ _, err = e.viewBuf.WriteString(
+ `<label for="` +
+ strings.Join(strings.Split(e.label, " "), "-") + `">` +
+ e.label + `</label>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementCheckbox")
+ return nil
+ }
}
- e.viewBuf.Write([]byte(`</p>`))
+
+ _, err = e.viewBuf.WriteString(`</p>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementCheckbox")
+ return nil
+ }
+
return e.viewBuf.Bytes()
}
// domElement creates a DOM element
func domElement(e *element) []byte {
- e.viewBuf.Write([]byte(`<div class="input-field col s12">`))
+ _, err := e.viewBuf.WriteString(`<div class="input-field col s12">`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElement")
+ return nil
+ }
if e.label != "" {
- e.viewBuf.Write([]byte(`<label class="active" for="` + strings.Join(strings.Split(e.label, " "), "-") + `">` + e.label + `</label>`))
+ _, err = e.viewBuf.WriteString(
+ `<label class="active" for="` +
+ strings.Join(strings.Split(e.label, " "), "-") + `">` + e.label +
+ `</label>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElement")
+ return nil
+ }
}
- e.viewBuf.Write([]byte(`<` + e.TagName + ` `))
- for attr, value := range e.Attrs {
- e.viewBuf.Write([]byte(attr + `="` + string(value) + `" `))
+ _, err = e.viewBuf.WriteString(`<` + e.tagName + ` `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElement")
+ return nil
}
- e.viewBuf.Write([]byte(` name="` + e.Name + `"`))
- e.viewBuf.Write([]byte(` >`))
- e.viewBuf.Write([]byte(html.EscapeString(e.data)))
- e.viewBuf.Write([]byte(`</` + e.TagName + `>`))
+ for attr, value := range e.attrs {
+ _, err = e.viewBuf.WriteString(attr + `="` + string(value) + `" `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElement")
+ return nil
+ }
+ }
+ _, err = e.viewBuf.WriteString(` name="` + e.name + `" > `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElement")
+ return nil
+ }
+
+ _, err = e.viewBuf.WriteString(html.EscapeString(e.data))
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElement")
+ return nil
+ }
+
+ _, err = e.viewBuf.WriteString(`</` + e.tagName + `>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElement")
+ return nil
+ }
+
+ _, err = e.viewBuf.WriteString(`</div>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElement")
+ return nil
+ }
- e.viewBuf.Write([]byte(`</div>`))
return e.viewBuf.Bytes()
}
func domElementWithChildrenSelect(e *element, children []*element) []byte {
- e.viewBuf.Write([]byte(`<div class="input-field col s6">`))
+ _, err := e.viewBuf.WriteString(`<div class="input-field col s6">`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenSelect")
+ return nil
+ }
- e.viewBuf.Write([]byte(`<` + e.TagName + ` `))
+ _, err = e.viewBuf.WriteString(`<` + e.tagName + ` `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenSelect")
+ return nil
+ }
- for attr, value := range e.Attrs {
- e.viewBuf.Write([]byte(attr + `="` + string(value) + `" `))
+ for attr, value := range e.attrs {
+ _, err = e.viewBuf.WriteString(attr + `="` + value + `" `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenSelect")
+ return nil
+ }
+ }
+ _, err = e.viewBuf.WriteString(` name="` + e.name + `" >`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenSelect")
+ return nil
}
- e.viewBuf.Write([]byte(` name="` + e.Name + `"`))
- e.viewBuf.Write([]byte(` >`))
// loop over children and create domElement for each child
for _, child := range children {
- e.viewBuf.Write(domElement(child))
+ _, err = e.viewBuf.Write(domElement(child))
+ if err != nil {
+ log.Println("Error writing HTML domElement to buffer: domElementWithChildrenSelect")
+ return nil
+ }
}
- e.viewBuf.Write([]byte(`</` + e.TagName + `>`))
+ _, err = e.viewBuf.WriteString(`</` + e.tagName + `>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenSelect")
+ return nil
+ }
if e.label != "" {
- e.viewBuf.Write([]byte(`<label class="active">` + e.label + `</label>`))
+ _, err = e.viewBuf.WriteString(`<label class="active">` + e.label + `</label>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenSelect")
+ return nil
+ }
+ }
+
+ _, err = e.viewBuf.WriteString(`</div>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenSelect")
+ return nil
}
- e.viewBuf.Write([]byte(`</div>`))
return e.viewBuf.Bytes()
}
func domElementWithChildrenCheckbox(e *element, children []*element) []byte {
- e.viewBuf.Write([]byte(`<` + e.TagName + ` `))
+ _, err := e.viewBuf.WriteString(`<` + e.tagName + ` `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenCheckbox")
+ return nil
+ }
- for attr, value := range e.Attrs {
- e.viewBuf.Write([]byte(attr + `="` + value + `" `))
+ for attr, value := range e.attrs {
+ _, err = e.viewBuf.WriteString(attr + `="` + value + `" `)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenCheckbox")
+ return nil
+ }
}
- e.viewBuf.Write([]byte(` >`))
+ _, err = e.viewBuf.WriteString(` >`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenCheckbox")
+ return nil
+ }
if e.label != "" {
- e.viewBuf.Write([]byte(`<label class="active">` + e.label + `</label>`))
+ _, err = e.viewBuf.WriteString(`<label class="active">` + e.label + `</label>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenCheckbox")
+ return nil
+ }
}
// loop over children and create domElement for each child
for _, child := range children {
- e.viewBuf.Write(domElementCheckbox(child))
+ _, err = e.viewBuf.Write(domElementCheckbox(child))
+ if err != nil {
+ log.Println("Error writing HTML domElementCheckbox to buffer: domElementWithChildrenCheckbox")
+ return nil
+ }
}
- e.viewBuf.Write([]byte(`</` + e.TagName + `><div class="clear padding">&nbsp;</div>`))
+ _, err = e.viewBuf.WriteString(`</` + e.tagName + `><div class="clear padding">&nbsp;</div>`)
+ if err != nil {
+ log.Println("Error writing HTML string to buffer: domElementWithChildrenCheckbox")
+ return nil
+ }
return e.viewBuf.Bytes()
}
diff --git a/management/editor/editor.go b/management/editor/editor.go
index 7194c27..511edb2 100644
--- a/management/editor/editor.go
+++ b/management/editor/editor.go
@@ -4,6 +4,7 @@ package editor
import (
"bytes"
+ "log"
"net/http"
)
@@ -38,16 +39,28 @@ func Form(post Editable, fields ...Field) ([]byte, error) {
editor := post.Editor()
editor.ViewBuf = &bytes.Buffer{}
- editor.ViewBuf.Write([]byte(`<table><tbody class="row"><tr class="col s8"><td>`))
+ _, err := editor.ViewBuf.WriteString(`<table><tbody class="row"><tr class="col s8"><td>`)
+ if err != nil {
+ log.Println("Error writing HTML string to editor Form buffer")
+ return nil, err
+ }
for _, f := range fields {
addFieldToEditorView(editor, f)
}
- editor.ViewBuf.Write([]byte(`</td></tr>`))
+ _, err = editor.ViewBuf.WriteString(`</td></tr>`)
+ if err != nil {
+ log.Println("Error writing HTML string to editor Form buffer")
+ return nil, err
+ }
// content items with Item embedded have some default fields we need to render
- editor.ViewBuf.Write([]byte(`<tr class="col s4 default-fields"><td>`))
+ _, err = editor.ViewBuf.WriteString(`<tr class="col s4 default-fields"><td>`)
+ if err != nil {
+ log.Println("Error writing HTML string to editor Form buffer")
+ return nil, err
+ }
publishTime := `
<div class="row content-only __ponzu">
@@ -98,9 +111,16 @@ func Form(post Editable, fields ...Field) ([]byte, error) {
</div>
`
- editor.ViewBuf.Write([]byte(publishTime))
+ _, err = editor.ViewBuf.WriteString(publishTime)
+ if err != nil {
+ log.Println("Error writing HTML string to editor Form buffer")
+ return nil, err
+ }
- addPostDefaultFieldsToEditorView(post, editor)
+ err = addPostDefaultFieldsToEditorView(post, editor)
+ if err != nil {
+ return nil, err
+ }
submit := `
<div class="input-field post-controls">
@@ -186,16 +206,26 @@ func Form(post Editable, fields ...Field) ([]byte, error) {
});
</script>
`
- editor.ViewBuf.Write([]byte(submit + script + `</td></tr></tbody></table>`))
+ _, err = editor.ViewBuf.WriteString(submit + script + `</td></tr></tbody></table>`)
+ if err != nil {
+ log.Println("Error writing HTML string to editor Form buffer")
+ return nil, err
+ }
return editor.ViewBuf.Bytes(), nil
}
-func addFieldToEditorView(e *Editor, f Field) {
- e.ViewBuf.Write(f.View)
+func addFieldToEditorView(e *Editor, f Field) error {
+ _, err := e.ViewBuf.Write(f.View)
+ if err != nil {
+ log.Println("Error writing field view to editor view buffer")
+ return err
+ }
+
+ return nil
}
-func addPostDefaultFieldsToEditorView(p Editable, e *Editor) {
+func addPostDefaultFieldsToEditorView(p Editable, e *Editor) error {
defaults := []Field{
Field{
View: Input("Slug", p, map[string]string{
@@ -220,7 +250,11 @@ func addPostDefaultFieldsToEditorView(p Editable, e *Editor) {
}
for _, f := range defaults {
- addFieldToEditorView(e, f)
+ err := addFieldToEditorView(e, f)
+ if err != nil {
+ return err
+ }
}
+ return nil
}
diff --git a/management/editor/elements.go b/management/editor/elements.go
index 873e81c..b82b220 100644
--- a/management/editor/elements.go
+++ b/management/editor/elements.go
@@ -11,7 +11,11 @@ import (
// The `fieldName` argument will cause a panic if it is not exactly the string
// form of the struct field that this editor input is representing
// type Person struct {
+// item.Item
+// editor editor.Editor
+//
// Name string `json:"name"`
+// //...
// }
//
// func (p *Person) MarshalEditor() ([]byte, error) {
@@ -64,9 +68,9 @@ func Timestamp(fieldName string, p interface{}, attrs map[string]string) []byte
}
e := &element{
- TagName: "input",
- Attrs: attrs,
- Name: tagNameFromStructField(fieldName, p),
+ tagName: "input",
+ attrs: attrs,
+ name: tagNameFromStructField(fieldName, p),
label: attrs["label"],
data: data,
viewBuf: &bytes.Buffer{},
@@ -161,9 +165,9 @@ func Richtext(fieldName string, p interface{}, attrs map[string]string) []byte {
attrs["class"] = "richtext " + fieldName
attrs["id"] = "richtext-" + fieldName
div := &element{
- TagName: "div",
- Attrs: attrs,
- Name: "",
+ tagName: "div",
+ attrs: attrs,
+ name: "",
label: "",
data: "",
viewBuf: &bytes.Buffer{},
@@ -261,16 +265,16 @@ func Select(fieldName string, p interface{}, attrs, options map[string]string) [
// provide a call to action for the select element
cta := &element{
- TagName: "option",
- Attrs: map[string]string{"disabled": "true", "selected": "true"},
+ tagName: "option",
+ attrs: map[string]string{"disabled": "true", "selected": "true"},
data: "Select an option...",
viewBuf: &bytes.Buffer{},
}
// provide a selection reset (will store empty string in db)
reset := &element{
- TagName: "option",
- Attrs: map[string]string{"value": ""},
+ tagName: "option",
+ attrs: map[string]string{"value": ""},
data: "None",
viewBuf: &bytes.Buffer{},
}
@@ -283,8 +287,8 @@ func Select(fieldName string, p interface{}, attrs, options map[string]string) [
optAttrs["selected"] = "true"
}
opt := &element{
- TagName: "option",
- Attrs: optAttrs,
+ tagName: "option",
+ attrs: optAttrs,
data: v,
viewBuf: &bytes.Buffer{},
}
@@ -328,9 +332,9 @@ func Checkbox(fieldName string, p interface{}, attrs, options map[string]string)
// create a *element manually using the modified tagNameFromStructFieldMulti
// func since this is for a multi-value name
input := &element{
- TagName: "input",
- Attrs: inputAttrs,
- Name: tagNameFromStructFieldMulti(fieldName, i, p),
+ tagName: "input",
+ attrs: inputAttrs,
+ name: tagNameFromStructFieldMulti(fieldName, i, p),
label: v,
data: "",
viewBuf: &bytes.Buffer{},
diff --git a/management/editor/repeaters.go b/management/editor/repeaters.go
index 37fb982..617caee 100644
--- a/management/editor/repeaters.go
+++ b/management/editor/repeaters.go
@@ -3,6 +3,7 @@ package editor
import (
"bytes"
"fmt"
+ "log"
"strings"
)
@@ -13,7 +14,11 @@ import (
// The `fieldName` argument will cause a panic if it is not exactly the string
// form of the struct field that this editor input is representing
// type Person struct {
+// item.Item
+// editor editor.Editor
+//
// Names []string `json:"names"`
+// //...
// }
//
// func (p *Person) MarshalEditor() ([]byte, error) {
@@ -35,12 +40,17 @@ func InputRepeater(fieldName string, p interface{}, attrs map[string]string) []b
scope := tagNameFromStructField(fieldName, p)
html := bytes.Buffer{}
- html.WriteString(`<span class="__ponzu-repeat ` + scope + `">`)
+ _, err := html.WriteString(`<span class="__ponzu-repeat ` + scope + `">`)
+ if err != nil {
+ log.Println("Error writing HTML string to InputRepeater buffer")
+ return nil
+ }
+
for i, val := range vals {
el := &element{
- TagName: "input",
- Attrs: attrs,
- Name: tagNameFromStructFieldMulti(fieldName, i, p),
+ tagName: "input",
+ attrs: attrs,
+ name: tagNameFromStructFieldMulti(fieldName, i, p),
data: val,
viewBuf: &bytes.Buffer{},
}
@@ -50,9 +60,17 @@ func InputRepeater(fieldName string, p interface{}, attrs map[string]string) []b
el.label = attrs["label"]
}
- html.Write(domElementSelfClose(el))
+ _, err := html.Write(domElementSelfClose(el))
+ if err != nil {
+ log.Println("Error writing domElementSelfClose to InputRepeater buffer")
+ return nil
+ }
+ }
+ _, err = html.WriteString(`</span>`)
+ if err != nil {
+ log.Println("Error writing HTML string to InputRepeater buffer")
+ return nil
}
- html.WriteString(`</span>`)
return append(html.Bytes(), RepeatController(fieldName, p, "input", ".input-field")...)
}
@@ -68,7 +86,11 @@ func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]s
// <option value="{map key}">{map value}</option>
scope := tagNameFromStructField(fieldName, p)
html := bytes.Buffer{}
- html.WriteString(`<span class="__ponzu-repeat ` + scope + `">`)
+ _, err := html.WriteString(`<span class="__ponzu-repeat ` + scope + `">`)
+ if err != nil {
+ log.Println("Error writing HTML string to SelectRepeater buffer")
+ return nil
+ }
// find the field values in p to determine if an option is pre-selected
fieldVals := valueFromStructField(fieldName, p)
@@ -80,9 +102,9 @@ func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]s
if len(vals) > 0 {
for i, val := range vals {
sel := &element{
- TagName: "select",
- Attrs: attrs,
- Name: tagNameFromStructFieldMulti(fieldName, i, p),
+ tagName: "select",
+ attrs: attrs,
+ name: tagNameFromStructFieldMulti(fieldName, i, p),
viewBuf: &bytes.Buffer{},
}
@@ -96,16 +118,16 @@ func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]s
// provide a call to action for the select element
cta := &element{
- TagName: "option",
- Attrs: map[string]string{"disabled": "true", "selected": "true"},
+ tagName: "option",
+ attrs: map[string]string{"disabled": "true", "selected": "true"},
data: "Select an option...",
viewBuf: &bytes.Buffer{},
}
// provide a selection reset (will store empty string in db)
reset := &element{
- TagName: "option",
- Attrs: map[string]string{"value": ""},
+ tagName: "option",
+ attrs: map[string]string{"value": ""},
data: "None",
viewBuf: &bytes.Buffer{},
}
@@ -118,8 +140,8 @@ func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]s
optAttrs["selected"] = "true"
}
opt := &element{
- TagName: "option",
- Attrs: optAttrs,
+ tagName: "option",
+ attrs: optAttrs,
data: v,
viewBuf: &bytes.Buffer{},
}
@@ -127,11 +149,20 @@ func SelectRepeater(fieldName string, p interface{}, attrs, options map[string]s
opts = append(opts, opt)
}
- html.Write(domElementWithChildrenSelect(sel, opts))
+ _, err := html.Write(domElementWithChildrenSelect(sel, opts))
+ if err != nil {
+ log.Println("Error writing domElementWithChildrenSelect to SelectRepeater buffer")
+ return nil
+ }
}
}
- html.WriteString(`</span>`)
+ _, err = html.WriteString(`</span>`)
+ if err != nil {
+ log.Println("Error writing HTML string to SelectRepeater buffer")
+ return nil
+ }
+
return append(html.Bytes(), RepeatController(fieldName, p, "select", ".input-field")...)
}
@@ -223,14 +254,33 @@ func FileRepeater(fieldName string, p interface{}, attrs map[string]string) []by
name := tagNameFromStructField(fieldName, p)
html := bytes.Buffer{}
- html.WriteString(`<span class="__ponzu-repeat ` + name + `">`)
+ _, err := html.WriteString(`<span class="__ponzu-repeat ` + name + `">`)
+ if err != nil {
+ log.Println("Error writing HTML string to FileRepeater buffer")
+ return nil
+ }
+
for i, val := range vals {
className := fmt.Sprintf("%s-%d", name, i)
nameidx := tagNameFromStructFieldMulti(fieldName, i, p)
- html.WriteString(fmt.Sprintf(tmpl, nameidx, addLabelFirst(i, attrs["label"]), val, className, fieldName))
- html.WriteString(fmt.Sprintf(script, nameidx, className))
+
+ _, err := html.WriteString(fmt.Sprintf(tmpl, nameidx, addLabelFirst(i, attrs["label"]), val, className, fieldName))
+ if err != nil {
+ log.Println("Error writing HTML string to FileRepeater buffer")
+ return nil
+ }
+
+ _, err = html.WriteString(fmt.Sprintf(script, nameidx, className))
+ if err != nil {
+ log.Println("Error writing HTML string to FileRepeater buffer")
+ return nil
+ }
+ }
+ _, err = html.WriteString(`</span>`)
+ if err != nil {
+ log.Println("Error writing HTML string to FileRepeater buffer")
+ return nil
}
- html.WriteString(`</span>`)
return append(html.Bytes(), RepeatController(fieldName, p, "input.upload", "div.file-input."+fieldName)...)
}
diff --git a/system/api/handlers.go b/system/api/handlers.go
index 788b2a0..7b59dbd 100644
--- a/system/api/handlers.go
+++ b/system/api/handlers.go
@@ -180,15 +180,6 @@ func toJSON(data []string) ([]byte, error) {
return buf.Bytes(), nil
}
-func wrapJSON(json []byte) []byte {
- var buf = &bytes.Buffer{}
- buf.Write([]byte(`{"data":`))
- buf.Write(json)
- buf.Write([]byte(`}`))
-
- return buf.Bytes()
-}
-
// sendData() should be used any time you want to communicate
// data back to a foreign client
func sendData(res http.ResponseWriter, data []byte, code int) {
@@ -196,7 +187,10 @@ func sendData(res http.ResponseWriter, data []byte, code int) {
res.Header().Set("Access-Control-Allow-Origin", "*")
res.Header().Set("Content-Type", "application/json")
res.WriteHeader(code)
- res.Write(data)
+ _, err := res.Write(data)
+ if err != nil {
+ log.Println("Error writing to response in sendData")
+ }
}
// SendPreflight is used to respond to a cross-origin "OPTIONS" request
diff --git a/system/db/config.go b/system/db/config.go
index ce76021..45b3952 100644
--- a/system/db/config.go
+++ b/system/db/config.go
@@ -108,7 +108,10 @@ func ConfigAll() ([]byte, error) {
val := &bytes.Buffer{}
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__config"))
- val.Write(b.Get([]byte("settings")))
+ _, err := val.Write(b.Get([]byte("settings")))
+ if err != nil {
+ return err
+ }
return nil
})
diff --git a/system/tls/devcerts.go b/system/tls/devcerts.go
new file mode 100644
index 0000000..b41f099
--- /dev/null
+++ b/system/tls/devcerts.go
@@ -0,0 +1,148 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Modified 2016 by Steve Manuel, Boss Sauce Creative, LLC
+// All modifications are relicensed under the same BSD license
+// found in the LICENSE file.
+
+// Generate a self-signed X.509 certificate for a TLS server. Outputs to
+// 'devcerts/cert.pem' and 'devcerts/key.pem' and will overwrite existing files.
+
+package tls
+
+import (
+ "crypto/ecdsa"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/pem"
+ "fmt"
+ "log"
+ "math/big"
+ "net"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/ponzu-cms/ponzu/system/db"
+)
+
+func publicKey(priv interface{}) interface{} {
+ switch k := priv.(type) {
+ case *rsa.PrivateKey:
+ return &k.PublicKey
+ case *ecdsa.PrivateKey:
+ return &k.PublicKey
+ default:
+ return nil
+ }
+}
+
+func pemBlockForKey(priv interface{}) *pem.Block {
+ switch k := priv.(type) {
+ case *rsa.PrivateKey:
+ return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
+ case *ecdsa.PrivateKey:
+ b, err := x509.MarshalECPrivateKey(k)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
+ os.Exit(2)
+ }
+ return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
+ default:
+ return nil
+ }
+}
+
+func setupDev() {
+ var priv interface{}
+ var err error
+
+ priv, err = rsa.GenerateKey(rand.Reader, 2048)
+
+ if err != nil {
+ log.Fatalf("failed to generate private key: %s", err)
+ }
+
+ notBefore := time.Now()
+ notAfter := notBefore.Add(time.Hour * 24 * 30) // valid for 30 days
+
+ serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+ serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
+ if err != nil {
+ log.Fatalf("failed to generate serial number: %s", err)
+ }
+
+ template := x509.Certificate{
+ SerialNumber: serialNumber,
+ Subject: pkix.Name{
+ Organization: []string{"Ponzu Dev Server"},
+ },
+ NotBefore: notBefore,
+ NotAfter: notAfter,
+
+ KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+ ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+ BasicConstraintsValid: true,
+ }
+
+ hosts := []string{"localhost", "0.0.0.0"}
+ domain := db.ConfigCache("domain")
+ if domain != "" {
+ hosts = append(hosts, domain)
+ }
+
+ for _, h := range hosts {
+ if ip := net.ParseIP(h); ip != nil {
+ template.IPAddresses = append(template.IPAddresses, ip)
+ } else {
+ template.DNSNames = append(template.DNSNames, h)
+ }
+ }
+
+ // make all certs CA
+ template.IsCA = true
+ template.KeyUsage |= x509.KeyUsageCertSign
+
+ derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
+ if err != nil {
+ log.Fatalln("Failed to create certificate:", err)
+ }
+
+ // overwrite/create directory for devcerts
+ pwd, err := os.Getwd()
+ if err != nil {
+ log.Fatalln("Couldn't find working directory to locate or save dev certificates:", err)
+ }
+
+ vendorTLSPath := filepath.Join(pwd, "cmd", "ponzu", "vendor", "github.com", "ponzu-cms", "ponzu", "system", "tls")
+ devcertsPath := filepath.Join(vendorTLSPath, "devcerts")
+
+ // clear all old certs if found
+ err = os.RemoveAll(devcertsPath)
+ if err != nil {
+ log.Fatalln("Failed to remove old files from dev certificate directory:", err)
+ }
+
+ err = os.Mkdir(devcertsPath, os.ModePerm|os.ModePerm)
+ if err != nil {
+ log.Fatalln("Failed to create directory to locate or save dev certificates:", err)
+ }
+
+ certOut, err := os.Create(filepath.Join(devcertsPath, "cert.pem"))
+ if err != nil {
+ log.Fatalln("Failed to open devcerts/cert.pem for writing:", err)
+ }
+ pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+ certOut.Close()
+
+ keyOut, err := os.OpenFile(filepath.Join(devcertsPath, "key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+ if err != nil {
+ log.Fatalln("Failed to open devcerts/key.pem for writing:", err)
+ return
+ }
+ pem.Encode(keyOut, pemBlockForKey(priv))
+ keyOut.Close()
+}
diff --git a/system/tls/enable.go b/system/tls/enable.go
index 04f032a..c6f65b3 100644
--- a/system/tls/enable.go
+++ b/system/tls/enable.go
@@ -65,7 +65,8 @@ func setup() {
}
-// Enable runs the setup for creating or locating certificates and starts the TLS server
+// Enable runs the setup for creating or locating production certificates and
+// starts the TLS server
func Enable() {
setup()
@@ -74,6 +75,5 @@ func Enable() {
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
}
- go log.Fatalln(server.ListenAndServeTLS("", ""))
- fmt.Println("Server listening for HTTPS requests...")
+ log.Fatalln(server.ListenAndServeTLS("", ""))
}
diff --git a/system/tls/enabledev.go b/system/tls/enabledev.go
new file mode 100644
index 0000000..3550fc0
--- /dev/null
+++ b/system/tls/enabledev.go
@@ -0,0 +1,29 @@
+package tls
+
+import (
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+)
+
+// EnableDev generates self-signed SSL certificates to use HTTPS & HTTP/2 while
+// working in a development environment. The certs are saved in a different
+// directory than the production certs (from Let's Encrypt), so that the
+// acme/autocert package doesn't mistake them for it's own.
+// Additionally, a TLS server is started using the default http mux.
+func EnableDev() {
+ setupDev()
+
+ pwd, err := os.Getwd()
+ if err != nil {
+ log.Fatalln("Couldn't find working directory to activate dev certificates:", err)
+ }
+
+ vendorPath := filepath.Join(pwd, "cmd", "ponzu", "vendor", "github.com", "ponzu-cms", "ponzu", "system", "tls")
+
+ cert := filepath.Join(vendorPath, "devcerts", "cert.pem")
+ key := filepath.Join(vendorPath, "devcerts", "key.pem")
+
+ log.Fatalln(http.ListenAndServeTLS(":10443", cert, key, nil))
+}