Make things pretty

This commit is contained in:
2016-07-18 01:27:22 +01:00
parent c53b088f19
commit 10873ae9ae
12 changed files with 312 additions and 178 deletions

View File

@@ -25,7 +25,7 @@ func (h *APIHandler) Shorten(c *routing.Context) error {
return h.respondWithError(c, err)
}
r := makeURLResponse(c, uid, url)
r := makeResponse(c, uid, url)
return h.respond(c, &r)
}
@@ -37,11 +37,11 @@ func (h *APIHandler) Lookup(c *routing.Context) error {
return h.respondWithError(c, err)
}
r := makeURLResponse(c, uid, url)
r := makeResponse(c, uid, url)
return h.respond(c, &r)
}
func (h *APIHandler) respond(c *routing.Context, r *URLResponse) error {
func (h *APIHandler) respond(c *routing.Context, r *Response) error {
resp, err := json.Marshal(r)
if err != nil {
return err
@@ -53,10 +53,7 @@ func (h *APIHandler) respond(c *routing.Context, r *URLResponse) error {
}
func (h *APIHandler) respondWithError(c *routing.Context, err error) error {
r := ErrorResponse{
Error: err.Error(),
}
r := Response{Error: err.Error()}
resp, err := json.Marshal(r)
if err != nil {
return err

File diff suppressed because one or more lines are too long

View File

@@ -22,6 +22,20 @@ func NewHandler(s shortener.Shortener) *Handler {
func newHandlerTemplate() *template.Template {
t := template.New("base")
funcMap := template.FuncMap{
"truncate": func(s string) string {
var numRunes = 0
for index := range s {
numRunes++
if numRunes > 50 {
return s[:index] + "..."
}
}
return s
},
}
t.Funcs(funcMap)
files, err := AssetDir("templates")
if err != nil {
panic(err)
@@ -45,26 +59,37 @@ type Handler struct {
template *template.Template
}
// Index handles requests for root.
func (h *Handler) Index(c *routing.Context) error {
h.respond(c, "index.html", nil)
return nil
}
// NotFound returns a 404 error page.
func (h *Handler) NotFound(c *routing.Context) error {
c.NotFound()
return nil
}
// Index handles requests for root.
func (h *Handler) Index(c *routing.Context) error {
template := "index.html"
rawURL := c.FormValue("url")
if len(rawURL) > 0 {
uid, url, err := h.shortener.Shorten(rawURL)
if err != nil {
return h.respond(c, template, makeErrResponse(err))
}
r := makeResponse(c, uid, url)
return h.respond(c, template, r)
}
return h.respond(c, template, 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
return h.NotFound(c)
}
info, _ := AssetInfo(p)
@@ -83,11 +108,10 @@ func (h *Handler) LookupAndRedirect(c *routing.Context) error {
url, err := h.shortener.Lookup(uid)
if err != nil {
h.NotFound(c)
return nil
return h.NotFound(c)
}
r := makeURLResponse(c, uid, url)
r := makeResponse(c, uid, url)
c.Response.Header.Set("Pragma", "no-cache")
c.Response.Header.Set("Expires", "Mon, 01 Jan 1990 00:00:00 GMT")
@@ -101,11 +125,11 @@ func (h *Handler) LookupAndRedirect(c *routing.Context) error {
c.Response.Header.Set("X-Frame-Options", "SAMEORIGIN")
c.Response.Header.Set("Vary", "Accept-Encoding")
h.respond(c, "redirect.html", r)
return nil
return h.respond(c, "redirect.html", r)
}
func (h *Handler) respond(c *routing.Context, name string, data interface{}) {
func (h *Handler) respond(c *routing.Context, name string, data interface{}) error {
c.SetContentType("text/html; charset=UTF-8")
h.template.ExecuteTemplate(c, name, data)
return nil
}

View File

@@ -6,14 +6,18 @@ import (
"github.com/qiangxue/fasthttp-routing"
)
func makeURLResponse(c *routing.Context, uid []byte, url []byte) URLResponse {
return URLResponse{
func makeResponse(c *routing.Context, uid []byte, url []byte) Response {
return Response{
UID: string(uid),
URL: makeShortURL(c, uid),
Target: string(url),
}
}
func makeErrResponse(err error) Response {
return Response{Error: err.Error()}
}
func makeShortURL(c *routing.Context, uid []byte) string {
shortURL := &url.URL{
Scheme: "http",

11
web/response.go Normal file
View File

@@ -0,0 +1,11 @@
package web
//go:generate easyjson -all response.go
// Response contains shortened URL info.
type Response struct {
UID string `json:"uid"`
URL string `json:"url"`
Target string `json:"target"`
Error string `json:"error"`
}

88
web/response_easyjson.go Normal file
View File

@@ -0,0 +1,88 @@
// AUTOGENERATED FILE: easyjson marshaller/unmarshallers.
package web
import (
json "encoding/json"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
var _ = json.RawMessage{} // suppress unused package warning
func easyjson_6ff3ac1d_decode_github_com_jimeh_ozu_io_web_Response(in *jlexer.Lexer, out *Response) {
if in.IsNull() {
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "uid":
out.UID = string(in.String())
case "url":
out.URL = string(in.String())
case "target":
out.Target = string(in.String())
case "error":
out.Error = string(in.String())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
}
func easyjson_6ff3ac1d_encode_github_com_jimeh_ozu_io_web_Response(out *jwriter.Writer, in Response) {
out.RawByte('{')
first := true
_ = first
if !first {
out.RawByte(',')
}
first = false
out.RawString("\"uid\":")
out.String(string(in.UID))
if !first {
out.RawByte(',')
}
first = false
out.RawString("\"url\":")
out.String(string(in.URL))
if !first {
out.RawByte(',')
}
first = false
out.RawString("\"target\":")
out.String(string(in.Target))
if !first {
out.RawByte(',')
}
first = false
out.RawString("\"error\":")
out.String(string(in.Error))
out.RawByte('}')
}
func (v Response) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson_6ff3ac1d_encode_github_com_jimeh_ozu_io_web_Response(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
func (v Response) MarshalEasyJSON(w *jwriter.Writer) {
easyjson_6ff3ac1d_encode_github_com_jimeh_ozu_io_web_Response(w, v)
}
func (v *Response) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson_6ff3ac1d_decode_github_com_jimeh_ozu_io_web_Response(&r, v)
return r.Error()
}
func (v *Response) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson_6ff3ac1d_decode_github_com_jimeh_ozu_io_web_Response(l, v)
}

View File

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

View File

@@ -1,132 +0,0 @@
// AUTOGENERATED FILE: easyjson marshaller/unmarshallers.
package web
import (
json "encoding/json"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
var _ = json.RawMessage{} // suppress unused package warning
func easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ErrorResponse(in *jlexer.Lexer, out *ErrorResponse) {
if in.IsNull() {
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "error":
out.Error = string(in.String())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
}
func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ErrorResponse(out *jwriter.Writer, in ErrorResponse) {
out.RawByte('{')
first := true
_ = first
if !first {
out.RawByte(',')
}
first = false
out.RawString("\"error\":")
out.String(string(in.Error))
out.RawByte('}')
}
func (v ErrorResponse) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ErrorResponse(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
func (v ErrorResponse) MarshalEasyJSON(w *jwriter.Writer) {
easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ErrorResponse(w, v)
}
func (v *ErrorResponse) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ErrorResponse(&r, v)
return r.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_URLResponse(in *jlexer.Lexer, out *URLResponse) {
if in.IsNull() {
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "uid":
out.UID = string(in.String())
case "url":
out.URL = string(in.String())
case "target":
out.Target = string(in.String())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
}
func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_URLResponse(out *jwriter.Writer, in URLResponse) {
out.RawByte('{')
first := true
_ = first
if !first {
out.RawByte(',')
}
first = false
out.RawString("\"uid\":")
out.String(string(in.UID))
if !first {
out.RawByte(',')
}
first = false
out.RawString("\"url\":")
out.String(string(in.URL))
if !first {
out.RawByte(',')
}
first = false
out.RawString("\"target\":")
out.String(string(in.Target))
out.RawByte('}')
}
func (v URLResponse) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_URLResponse(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
func (v URLResponse) MarshalEasyJSON(w *jwriter.Writer) {
easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_URLResponse(w, v)
}
func (v *URLResponse) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_URLResponse(&r, v)
return r.Error()
}
func (v *URLResponse) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_URLResponse(l, v)
}

1
web/static/furtive.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,100 @@
body {
font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-family: "Open Sans", Helvetica, Arial, sans-serif;
font-weight: normal;
line-height: 1.5;
}
.world {
position: fixed;
top: 0px;
text-align: center;
bottom: 0px;
width: 100%;
}
.world .content {
width: 584px;
position: absolute;
top: 50%;
left: 50%;
-moz-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.logo-wrapper {
font-family: "Open Sans Condensed", Helvetica, Arial, sans-serif;
}
.logo {
font-size: 64px;
height: 70px;
margin-top: -25px;
}
.slogan {
font-size: 15px;
}
.message {
margin-top: 20px;
}
.message.error {
color: #e74c3c;
}
.message input {
background-color: transparent;
border: none;
color: #333;
font-family: "Open Sans", Helvetica, Arial, sans-serif;
font-size: 21px;
font-weight: normal;
padding: 0;
margin: 0;
text-align: center;
}
.message .info {
color: #666;
font-style: italic;
font-size: 12px;
}
.form {
margin-top: 10px;
}
.form input {
font-family: "Open Sans", Helvetica, Arial, sans-serif;
font-size: 21px;
font-weight: normal;
width: 100%;
}
.form input:focus { outline: none; }
.form button {
color: #fff;
font-family: "Open Sans", Helvetica, Arial, sans-serif;
font-size: 18px;
font-weight: bold;
}
::-webkit-input-placeholder { /* WebKit, Blink, Edge */
color: #ccc;
}
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: #ccc;
opacity: 1;
}
::-moz-placeholder { /* Mozilla Firefox 19+ */
color: #ccc;
opacity: 1;
}
:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: #ccc;
}

View File

@@ -6,6 +6,9 @@
<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="//maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans+Condensed:700|Open+Sans:400italic,400,700" type="text/css">
<link rel="stylesheet" href="/static/furtive.min.css">
<link rel="stylesheet" href="/static/main.css">
</head>

View File

@@ -1,3 +1,36 @@
{{template "_head.html"}}
Welcome to ozu.io, a drunken URL shortener.
<div class="world">
<div class="content">
<div class="logo-wrapper">
<div class="logo">ozu.io</div>
<div class="slogan">a drunken URL shortener</div>
</div>
{{if .URL}}
<script type="text/javascript">
window.onload = function(){
document.getElementById('shortURL').focus();
}
</script>
<div class="message">
<input id="shortURL" onfocus="this.select()" readonly="readonly" value="{{.URL}}">
<div class="info">
is short for <a href="{{.Target | html}}">{{.Target | truncate | html}}</a>
</div>
</div>
{{end}}
{{if .Error}}
<div class="message error">
{{.Error | html}}
</div>
{{end}}
<div class="form">
<form>
<input type="text" name="url" placeholder="http://">
<button type="submit" class="btn--blue">
Shorten <i class="fa fa-arrow-right" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
{{template "_foot.html"}}