Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b546145220 | ||
|
|
e9edaf13ee | ||
|
|
3c2003767e | ||
|
|
09f4fef613 | ||
|
|
85296b4e1b | ||
|
|
04fa2f73a7 | ||
|
|
7c191f8a69 | ||
|
|
a6494ae0a1 | ||
|
|
a926bdc955 | ||
|
|
d76a9bff9f | ||
|
|
4a981bf69c | ||
|
|
5914f93881 | ||
|
|
8096c76b1e | ||
|
|
d98ef7a4a9 | ||
|
|
e5e9e5f494 | ||
|
|
31cd542929 |
+34
-9
@@ -1,15 +1,40 @@
|
||||
package main
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"mcquay.me/web"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.Handle(
|
||||
"/",
|
||||
http.FileServer(
|
||||
http.Dir("."),
|
||||
),
|
||||
)
|
||||
if err := http.ListenAndServe(":8000", nil); err != nil {
|
||||
panic(err)
|
||||
port := 8000
|
||||
if os.Getenv("PORT") != "" {
|
||||
p, err := strconv.Atoi(os.Getenv("PORT"))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "couldn't parse port: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
port = p
|
||||
}
|
||||
|
||||
static := ""
|
||||
if s := os.Getenv("STATIC"); s != "" {
|
||||
static = s
|
||||
}
|
||||
|
||||
tmpl := ""
|
||||
if t := os.Getenv("TEMPLATES"); t != "" {
|
||||
tmpl = t
|
||||
}
|
||||
|
||||
sm := http.NewServeMux()
|
||||
|
||||
web.NewSite(sm, static, tmpl)
|
||||
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), sm); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "problem serving: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2015, smcquay
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of web nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -50,3 +50,7 @@ a {
|
||||
padding-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.tiny {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<title>smcquay</title>
|
||||
<link rel="stylesheet" type="text/css" href="/css/sm.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div id="title">
|
||||
<a href="https://sec.mcquay.me">smcquay</a>
|
||||
</div>
|
||||
<div id="pic">
|
||||
<a href="https://sec.mcquay.me"><img src="/img/smcquay.png"></a>
|
||||
</div>
|
||||
<div id="info">
|
||||
<p>
|
||||
My name is <a href="http://stephen.mcquay.me">Stephen McQuay</a>,
|
||||
<a href="https://twitter.com/smcquay">smcquay</a> on the internet.
|
||||
I speak <a href="https://golang.org">gopher</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-2869147-4', 'auto'); ga('send', 'pageview'); </script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
{{ define "base" }}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<title>ωτφ</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/sm.css">
|
||||
</head>
|
||||
<body>
|
||||
{{ template "body" . }}
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,18 @@
|
||||
{{ define "body" }}
|
||||
<div id="content">
|
||||
<div id="title">
|
||||
<a href="https://s.mcquay.me">smcquay</a>
|
||||
</div>
|
||||
<div id="pic">
|
||||
<a href="https://s.mcquay.me"><img src="/static/img/smcquay.png"></a>
|
||||
</div>
|
||||
<div id="info">
|
||||
<p>
|
||||
My name is <a href="http://stephen.mcquay.me">Stephen McQuay</a>.
|
||||
</p>
|
||||
<p class="tiny">
|
||||
Your visit occured {{ .time }}.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,76 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TemplateGetter defines what needs to be implemented to be able to fetch
|
||||
// templates for use by a Site.
|
||||
type TemplateGetter interface {
|
||||
Get(name string) (*template.Template, error)
|
||||
}
|
||||
|
||||
// Disk keeps track of the location of the root directory of the template
|
||||
// directory.
|
||||
type Disk struct {
|
||||
Root string
|
||||
}
|
||||
|
||||
// Get attempts to merge the requested name+.html with base.html found at root.
|
||||
func (d Disk) Get(name string) (*template.Template, error) {
|
||||
p := filepath.Join(d.Root, name+".html")
|
||||
return template.New("").ParseFiles(p, filepath.Join(d.Root, "base.html"))
|
||||
}
|
||||
|
||||
// AssetTemplate pulls templates from an assetfs.
|
||||
type AssetTemplate struct {
|
||||
sync.RWMutex
|
||||
Asset func(name string) ([]byte, error)
|
||||
|
||||
cache map[string]*template.Template
|
||||
}
|
||||
|
||||
// NewAssetTemplate returns a ready-to-use AssetTemplate.
|
||||
func NewAssetTemplate(asset func(string) ([]byte, error)) *AssetTemplate {
|
||||
return &AssetTemplate{
|
||||
Asset: asset,
|
||||
cache: map[string]*template.Template{},
|
||||
}
|
||||
}
|
||||
|
||||
// Get attempts to merge the requested name+.html with base.html found at root.
|
||||
func (at *AssetTemplate) Get(name string) (*template.Template, error) {
|
||||
at.RLock()
|
||||
if t, ok := at.cache[name]; ok {
|
||||
at.RUnlock()
|
||||
return t, nil
|
||||
}
|
||||
at.RUnlock()
|
||||
|
||||
base, err := at.Asset("templates/base.html")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get base contents: %v", err)
|
||||
}
|
||||
page, err := at.Asset(fmt.Sprintf("templates/%s.html", name))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get base contents: %v", err)
|
||||
}
|
||||
|
||||
t, err := template.New(name).Parse(string(base))
|
||||
if err != nil {
|
||||
return t, fmt.Errorf("parsing base: %v", err)
|
||||
}
|
||||
t, err = t.Parse(string(page))
|
||||
if err != nil {
|
||||
return t, fmt.Errorf("parsing specific page %s: %v", name, err)
|
||||
}
|
||||
|
||||
at.Lock()
|
||||
at.cache[name] = t
|
||||
at.Unlock()
|
||||
|
||||
return t, nil
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
)
|
||||
|
||||
//go:generate go get github.com/jteeuwen/go-bindata/...
|
||||
//go:generate go get github.com/elazarl/go-bindata-assetfs/...
|
||||
//go:generate rm -f static.go
|
||||
//go:generate go-bindata -o static.go -pkg=web static/... templates/...
|
||||
|
||||
type Site struct {
|
||||
tmpl TemplateGetter
|
||||
}
|
||||
|
||||
func NewSite(sm *http.ServeMux, static, templates string) *Site {
|
||||
s := &Site{}
|
||||
|
||||
var fs http.FileSystem
|
||||
if p, d := filepath.Split(static); d == "static" {
|
||||
static = p
|
||||
}
|
||||
fs = http.Dir(static)
|
||||
if static == "" {
|
||||
fs = &assetfs.AssetFS{
|
||||
Asset: Asset,
|
||||
AssetDir: AssetDir,
|
||||
AssetInfo: AssetInfo,
|
||||
}
|
||||
}
|
||||
|
||||
s.tmpl = NewAssetTemplate(Asset)
|
||||
if templates != "" {
|
||||
s.tmpl = Disk{templates}
|
||||
}
|
||||
|
||||
sm.HandleFunc("/", s.Home)
|
||||
sm.Handle(
|
||||
"/static/",
|
||||
http.FileServer(fs),
|
||||
)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Site) Home(w http.ResponseWriter, req *http.Request) {
|
||||
c := map[string]string{
|
||||
"time": time.Now().Format(time.Stamp),
|
||||
}
|
||||
tmpl, err := s.tmpl.Get("home")
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("not found: %v", err), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
tmpl.ExecuteTemplate(w, "base", c)
|
||||
}
|
||||
Reference in New Issue
Block a user