Lots of changes to web package

Use go-bindata, html/templates, and lots of other changes to the web
package.
This commit is contained in:
2016-07-17 16:16:59 +01:00
parent 2e84c00707
commit 68807898da
12 changed files with 567 additions and 107 deletions

331
web/bindata.go Normal file
View File

@@ -0,0 +1,331 @@
// Code generated by go-bindata.
// sources:
// static/main.css
// templates/_foot.html
// templates/_head.html
// templates/index.html
// templates/redirect.html
// DO NOT EDIT!
package web
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
}
return buf.Bytes(), nil
}
type asset struct {
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _staticMainCss = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\x52\x50\x48\xcb\xcf\x2b\xd1\x4d\x4b\xcc\xcd\xcc\xa9\xb4\x52\x50\xf2\x48\xcd\x29\x4b\x2d\xc9\x4c\x4e\x54\xf0\x4b\x2d\x4d\x55\xd2\x51\x80\x0b\xe8\x28\x28\x05\xa7\xa6\xe7\xa7\x2a\x84\x7a\x02\x85\x1d\x8b\x32\x13\x73\x74\x14\xd2\x8a\x52\x53\x8b\x13\xf3\x8a\x75\x14\x40\xa4\x6e\x71\x6a\x51\x66\x1a\x50\xa1\x63\x41\x41\x4e\xaa\x82\x73\x7e\x4e\x7e\x91\x82\x6b\x6e\x7e\x56\xa6\x12\x92\x6e\x2c\x22\xc1\x95\xb9\x49\xf9\x39\x4a\xd6\x5c\xb5\x5c\x80\x00\x00\x00\xff\xff\x3e\xe1\x83\x30\x98\x00\x00\x00")
func staticMainCssBytes() ([]byte, error) {
return bindataRead(
_staticMainCss,
"static/main.css",
)
}
func staticMainCss() (*asset, error) {
bytes, err := staticMainCssBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "static/main.css", size: 152, mode: os.FileMode(420), modTime: time.Unix(1468518898, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _templates_footHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x52\x50\xb0\xd1\x4f\xca\x4f\xa9\xb4\xe3\xb2\xd1\xcf\x28\xc9\xcd\xb1\xe3\x02\x04\x00\x00\xff\xff\xd2\x42\x65\xbd\x12\x00\x00\x00")
func templates_footHtmlBytes() ([]byte, error) {
return bindataRead(
_templates_footHtml,
"templates/_foot.html",
)
}
func templates_footHtml() (*asset, error) {
bytes, err := templates_footHtmlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "templates/_foot.html", size: 18, mode: os.FileMode(420), modTime: time.Unix(1468764419, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _templates_headHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x64\x90\xc1\x6a\xf3\x30\x10\x84\xef\x79\x8a\xfd\x75\xfe\x15\xd3\x5b\x0f\x56\x20\x24\x39\x14\x0a\x2d\x6d\x03\xed\x51\x91\x27\xd5\x12\x59\x72\xa5\x75\x42\xfa\xf4\x75\x62\x02\x2e\x3d\x69\x06\xe9\xd3\xcc\x6e\xfd\x6f\xfd\xb4\x7a\xfb\x78\xde\x90\x97\x36\x2c\x66\xf5\x78\x10\xd5\x1e\xb6\xb9\x88\x41\xb6\x10\x4b\xce\xdb\x5c\x20\x46\xf5\xb2\xd7\xf7\x6a\x7a\xe5\x45\x3a\x8d\xaf\x9e\x8f\x46\xbd\xeb\xed\x52\xaf\x52\xdb\x59\xe1\x5d\x80\x22\x97\xa2\x20\x0e\xdc\xc3\xc6\xa0\xf9\xc4\x2f\x32\xda\x16\x46\x1d\x19\xa7\x2e\x65\x99\x3c\x3e\x71\x23\xde\x34\x38\xb2\x83\xbe\x9a\xff\xc4\x91\x85\x6d\xd0\xc5\xd9\x00\x73\x77\xfb\x48\x58\x02\x16\xe9\xbb\x9f\x73\xaa\xab\xd1\xfd\x89\x68\x50\x5c\xe6\x4e\x38\xc5\x49\xca\x08\x91\xa6\x25\xad\x73\x1f\x0f\x88\xb4\x7d\x79\xa4\x57\x3f\x94\x41\x44\xbe\x45\x04\x8e\x07\xca\x08\x46\x15\x39\x07\x14\x0f\x0c\x65\x7d\xc6\xde\xa8\xaa\xc8\x30\xaa\xab\x5a\xcb\x71\xee\x4a\xb9\x32\x75\x35\xae\xef\x22\x77\xa9\x39\x2f\x66\x3f\x01\x00\x00\xff\xff\x55\xce\x24\xf7\x69\x01\x00\x00")
func templates_headHtmlBytes() ([]byte, error) {
return bindataRead(
_templates_headHtml,
"templates/_head.html",
)
}
func templates_headHtml() (*asset, error) {
bytes, err := templates_headHtmlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "templates/_head.html", size: 361, mode: os.FileMode(420), modTime: time.Unix(1468764427, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _templatesIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xaa\xae\x2e\x49\xcd\x2d\xc8\x49\x2c\x49\x55\x50\x8a\xcf\x48\x4d\x4c\xd1\xcb\x28\xc9\xcd\x51\xaa\xad\xe5\x52\x00\x82\xf0\xd4\x9c\xe4\xfc\xdc\x54\x85\x92\x7c\x85\xfc\xaa\x52\xbd\xcc\x7c\x1d\x85\x44\x85\x94\xa2\xd2\xbc\xec\xd4\x3c\x85\xd0\x20\x1f\x85\xe2\x8c\xfc\xa2\x92\xd4\xbc\xd4\x22\x3d\x2e\x14\x93\xd2\xf2\xf3\x4b\xe0\x26\x01\x02\x00\x00\xff\xff\x93\x8b\x7b\xb5\x64\x00\x00\x00")
func templatesIndexHtmlBytes() ([]byte, error) {
return bindataRead(
_templatesIndexHtml,
"templates/index.html",
)
}
func templatesIndexHtml() (*asset, error) {
bytes, err := templatesIndexHtmlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "templates/index.html", size: 100, mode: os.FileMode(420), modTime: time.Unix(1468764414, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _templatesRedirectHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x6c\x4f\x41\xcb\x82\x50\x10\xbc\xfb\x2b\x96\xf7\xdd\xf5\xeb\xbe\x3e\xd0\x7c\xf6\x02\xc5\x90\x3d\xd4\x51\x72\xc9\x43\x2a\x98\x05\x21\xfe\xf7\xde\xdb\xea\xd6\x9c\x66\x67\x86\x61\x16\x2d\x95\x85\x0e\x00\xd0\x9a\x24\xf3\xc4\x51\xda\x53\x61\x74\x39\x3e\xb8\x85\x03\x4f\x7d\x33\xf0\x30\x5f\x9f\x18\xbd\x0d\x9f\x8e\xbe\x71\x4c\xab\xec\x04\xe9\x6e\x5b\x15\x55\x1d\xab\xbf\x5c\xa0\x80\xcc\x91\xdc\xf9\x2f\x50\x9f\x62\xbb\xf9\xd5\xea\x54\xb1\xa9\x63\x68\xc7\xf3\xbd\x77\x3a\x74\xcd\x0d\x7a\xc9\x62\x02\xb6\x36\x79\xac\x96\x25\xa4\x66\xba\xf0\xbc\xae\x4a\x77\x3c\x31\x46\x89\x0e\x65\x8d\x1f\xa1\x03\x57\x25\xdf\xbc\x02\x00\x00\xff\xff\xf9\x4e\x03\x95\xd5\x00\x00\x00")
func templatesRedirectHtmlBytes() ([]byte, error) {
return bindataRead(
_templatesRedirectHtml,
"templates/redirect.html",
)
}
func templatesRedirectHtml() (*asset, error) {
bytes, err := templatesRedirectHtmlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "templates/redirect.html", size: 213, mode: os.FileMode(420), modTime: time.Unix(1468764854, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"static/main.css": staticMainCss,
"templates/_foot.html": templates_footHtml,
"templates/_head.html": templates_headHtml,
"templates/index.html": templatesIndexHtml,
"templates/redirect.html": templatesRedirectHtml,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"static": &bintree{nil, map[string]*bintree{
"main.css": &bintree{staticMainCss, map[string]*bintree{}},
}},
"templates": &bintree{nil, map[string]*bintree{
"_foot.html": &bintree{templates_footHtml, map[string]*bintree{}},
"_head.html": &bintree{templates_headHtml, map[string]*bintree{}},
"index.html": &bintree{templatesIndexHtml, map[string]*bintree{}},
"redirect.html": &bintree{templatesRedirectHtml, map[string]*bintree{}},
}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

176
web/handler.go Normal file
View File

@@ -0,0 +1,176 @@
package web
import (
"encoding/json"
"html/template"
"mime"
"net/url"
"path"
"time"
"github.com/jimeh/ozu.io/shortener"
"github.com/qiangxue/fasthttp-routing"
"github.com/valyala/fasthttp"
)
//go:generate go-bindata -pkg web static/... templates/...
// NewHandler creates a new Handler object.
func NewHandler(s shortener.Shortener) *Handler {
t := template.New("base")
files, err := AssetDir("templates")
if err != nil {
panic(err)
}
for _, f := range files {
content, err := Asset("templates/" + f)
if err != nil {
panic(err)
}
t.New(f).Parse(string(content))
}
return &Handler{s, t}
}
// Handler handle HTTP requests.
type Handler struct {
s shortener.Shortener
t *template.Template
}
// Index handles requests for root.
func (h *Handler) Index(c *routing.Context) error {
h.template(c, "index.html", nil)
return nil
}
// NotFound returns a 404 error page.
func (h *Handler) NotFound(c *routing.Context) error {
c.NotFound()
return nil
}
// Static returns assets serialized via go-bindata
func (h *Handler) Static(c *routing.Context) error {
p := string(c.Path())[1:]
data, err := Asset(p)
if err != nil {
h.NotFound(c)
return nil
}
info, _ := AssetInfo(p)
contentType := mime.TypeByExtension(path.Ext(p))
modTime := info.ModTime().In(time.FixedZone("GMT", 0))
c.SetContentType(contentType)
c.Response.Header.Set("Last-Modified", modTime.Format(time.RFC1123))
c.Write(data)
return nil
}
// Shorten shortens given URL.
func (h *Handler) Shorten(c *routing.Context) error {
uid, url, err := h.s.Shorten(c.FormValue("url"))
if err != nil {
return h.respondWithError(c, err)
}
r := h.makeURLResponse(c, uid, url)
return h.respond(c, &r)
}
// Lookup shortened UID.
func (h *Handler) Lookup(c *routing.Context) error {
uid := c.FormValue("uid")
url, err := h.s.Lookup(uid)
if err != nil {
return h.respondWithError(c, err)
}
r := h.makeURLResponse(c, uid, url)
return h.respond(c, &r)
}
// LookupAndRedirect looks up given UID and redirects to it's URL.
func (h *Handler) LookupAndRedirect(c *routing.Context) error {
uid := []byte(c.Param("uid"))
url, err := h.s.Lookup(uid)
if err != nil {
h.NotFound(c)
return nil
}
r := h.makeURLResponse(c, uid, url)
c.Response.Header.Set("Pragma", "no-cache")
c.Response.Header.Set("Expires", "Mon, 01 Jan 1990 00:00:00 GMT")
c.Response.Header.Set("X-XSS-Protection", "1; mode=block")
c.Response.Header.Set("Cache-Control",
"no-cache, no-store, max-age=0, must-revalidate")
c.Redirect(string(url), fasthttp.StatusMovedPermanently)
c.Response.Header.Set("Connection", "close")
c.Response.Header.Set("X-Content-Type-Options", "nosniff")
c.Response.Header.Set("Accept-Ranges", "none")
c.Response.Header.Set("X-Frame-Options", "SAMEORIGIN")
c.Response.Header.Set("Vary", "Accept-Encoding")
h.template(c, "redirect.html", r)
return nil
}
func (h *Handler) template(c *routing.Context, name string, data interface{}) {
c.SetContentType("text/html; charset=UTF-8")
h.t.ExecuteTemplate(c, name, data)
}
func (h *Handler) respond(c *routing.Context, r *URLResponse) error {
resp, err := json.Marshal(r)
if err != nil {
return err
}
c.SetContentType("application/json")
c.Write(resp)
return nil
}
func (h *Handler) respondWithError(c *routing.Context, err error) error {
r := ErrorResponse{
Error: err.Error(),
}
resp, err := json.Marshal(r)
if err != nil {
return err
}
c.SetStatusCode(fasthttp.StatusNotFound)
c.SetContentType("application/json")
c.Write(resp)
return nil
}
func (h *Handler) makeURLResponse(c *routing.Context, uid []byte, url []byte) URLResponse {
return URLResponse{
UID: string(uid),
URL: h.makeShortURL(c, uid),
Target: string(url),
}
}
func (h *Handler) makeShortURL(c *routing.Context, uid []byte) string {
shortURL := &url.URL{
Scheme: "http",
Host: string(c.Host()),
Path: "/" + string(uid),
}
return shortURL.String()
}

View File

@@ -1,90 +0,0 @@
package web
import (
"encoding/json"
"fmt"
"net/url"
"github.com/jimeh/ozu.io/shortener"
"github.com/qiangxue/fasthttp-routing"
"github.com/valyala/fasthttp"
)
// Handlers handle HTTP requests.
type Handlers struct {
s *shortener.Shortener
}
// Index handles requests for root.
func (h *Handlers) Index(c *routing.Context) error {
c.WriteString("Welcome to ozu.io, a shitty URL shortener.")
return nil
}
// Shorten shortens given URL.
func (h *Handlers) Shorten(c *routing.Context) error {
c.SetContentType("application/json")
uid, url, err := h.s.Shorten(c.FormValue("url"))
if err != nil {
c.SetStatusCode(fasthttp.StatusBadRequest)
response, _ := json.Marshal(ErrorResponse{err.Error()})
c.Write(response)
return nil
}
h.respondWithShortened(c, uid, url)
return nil
}
// Lookup shortened UID.
func (h *Handlers) Lookup(c *routing.Context) error {
c.SetContentType("application/json")
uid := c.FormValue("uid")
url, err := h.s.Lookup(uid)
if err != nil {
c.SetStatusCode(fasthttp.StatusNotFound)
respBytes, _ := json.Marshal(ErrorResponse{err.Error()})
c.Write(respBytes)
return nil
}
h.respondWithShortened(c, uid, url)
return nil
}
// LookupAndRedirect looks up given UID and redirects to it's URL.
func (h *Handlers) LookupAndRedirect(c *routing.Context) error {
uid := c.Param("uid")
url, err := h.s.Lookup([]byte(uid))
if err != nil {
c.SetStatusCode(fasthttp.StatusNotFound)
fmt.Fprint(c, "404 Not Found")
return nil
}
c.Redirect(string(url), fasthttp.StatusMovedPermanently)
return nil
}
func (h *Handlers) respondWithShortened(c *routing.Context, uid []byte, url []byte) {
c.SetStatusCode(fasthttp.StatusOK)
response := ShortenedResponse{
UID: string(uid),
URL: h.makeShortURL(c, uid),
Target: string(url),
}
respBytes, _ := json.Marshal(response)
c.Write(respBytes)
}
func (h *Handlers) makeShortURL(c *routing.Context, uid []byte) string {
shortURL := &url.URL{
Scheme: "http",
Host: string(c.Host()),
Path: "/" + string(uid),
}
return shortURL.String()
}

View File

@@ -2,14 +2,14 @@ package web
//go:generate easyjson -all responses.go
// ShortenedResponse contains shortened URL info.
type ShortenedResponse struct {
// URLResponse contains shortened URL info.
type URLResponse struct {
UID string `json:"uid"`
URL string `json:"url"`
Target string `json:"target"`
}
// ErrorResponse contains error info.
// ErrorJSONResponse contains error info.
type ErrorResponse struct {
Error string `json:"error"`
}

View File

@@ -62,7 +62,7 @@ func (v *ErrorResponse) UnmarshalJSON(data []byte) error {
func (v *ErrorResponse) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ErrorResponse(l, v)
}
func easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(in *jlexer.Lexer, out *ShortenedResponse) {
func easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(in *jlexer.Lexer, out *URLResponse) {
if in.IsNull() {
in.Skip()
return
@@ -90,7 +90,7 @@ func easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(in *
}
in.Delim('}')
}
func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(out *jwriter.Writer, in ShortenedResponse) {
func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(out *jwriter.Writer, in URLResponse) {
out.RawByte('{')
first := true
_ = first
@@ -114,19 +114,19 @@ func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(out
out.String(string(in.Target))
out.RawByte('}')
}
func (v ShortenedResponse) MarshalJSON() ([]byte, error) {
func (v URLResponse) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
func (v ShortenedResponse) MarshalEasyJSON(w *jwriter.Writer) {
func (v URLResponse) MarshalEasyJSON(w *jwriter.Writer) {
easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(w, v)
}
func (v *ShortenedResponse) UnmarshalJSON(data []byte) error {
func (v *URLResponse) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(&r, v)
return r.Error()
}
func (v *ShortenedResponse) UnmarshalEasyJSON(l *jlexer.Lexer) {
func (v *URLResponse) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(l, v)
}

View File

@@ -6,14 +6,16 @@ import (
)
// NewRouter creates a new routing.Router with all handlers registered.
func NewRouter(shortener *shortener.Shortener) *routing.Router {
router := routing.New()
handlers := Handlers{shortener}
func NewRouter(s shortener.Shortener) *routing.Router {
r := routing.New()
h := NewHandler(s)
router.Get("/", handlers.Index)
router.Get("/api/shorten", handlers.Shorten)
router.Get("/api/lookup", handlers.Lookup)
router.Get("/<uid>", handlers.LookupAndRedirect)
r.Get("/", h.Index)
r.Get("/api/shorten", h.Shorten)
r.Get("/api/lookup", h.Lookup)
r.Get("/static/*", h.Static)
r.Get("/<uid>", h.LookupAndRedirect)
r.Get("/*", h.NotFound)
return router
return r
}

12
web/server.go Normal file
View File

@@ -0,0 +1,12 @@
package web
import (
"github.com/jimeh/ozu.io/shortener"
"github.com/valyala/fasthttp"
)
// NewServer returns a new fasthttp.Server with all routes configured.
func NewServer(s shortener.Shortener) *fasthttp.Server {
r := NewRouter(s)
return &fasthttp.Server{Handler: r.HandleRequest}
}

3
web/static/main.css Normal file
View File

@@ -0,0 +1,3 @@
body {
font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

2
web/templates/_foot.html Normal file
View File

@@ -0,0 +1,2 @@
</body>
</html>

12
web/templates/_head.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ozu.io</title>
<meta name="description" content="ozu.io - A Drunken URL Shortener">
<link rel="stylesheet" href="/static/main.css">
</head>
<body>

3
web/templates/index.html Normal file
View File

@@ -0,0 +1,3 @@
{{template "_head.html"}}
Welcome to ozu.io, a drunken URL shortener.
{{template "_foot.html"}}

View File

@@ -0,0 +1,9 @@
<HTML>
<HEAD>
<TITLE>Moved Permanently</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Moved Permanently</H1>
The document has moved <A HREF="{{.Target}}">here</A>.
</BODY>
</HTML>