Initial Commit
This commit is contained in:
commit
3c42d7d878
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
FROM golang:alpine
|
||||||
|
|
||||||
|
EXPOSE 8000/tcp
|
||||||
|
|
||||||
|
ENTRYPOINT ["pastebin"]
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apk add --update git && \
|
||||||
|
rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
|
RUN mkdir -p /go/src/pastebin
|
||||||
|
WORKDIR /go/src/pastebin
|
||||||
|
|
||||||
|
COPY . /go/src/pastebin
|
||||||
|
|
||||||
|
RUN go get -v -d
|
||||||
|
RUN go install -v
|
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (C) 2017 James Mills
|
||||||
|
|
||||||
|
httpfs is covered by the MIT license::
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without restriction,
|
||||||
|
including without limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
13
Makefile
Normal file
13
Makefile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.PHONY: dev build clean
|
||||||
|
|
||||||
|
all: dev
|
||||||
|
|
||||||
|
dev: build
|
||||||
|
./pastebin -bind 127.0.0.1:8000
|
||||||
|
|
||||||
|
build: clean
|
||||||
|
go get ./...
|
||||||
|
go build -o ./pastebin .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf bin pastebin
|
49
README.md
Normal file
49
README.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# pastebin
|
||||||
|
[![Build Status](https://travis-ci.org/prologic/pastebin.svg)](https://travis-ci.org/prologic/pastebin)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/prologic/pastebin?status.svg)](https://godoc.org/github.com/prologic/pastebin)
|
||||||
|
[![Wiki](https://img.shields.io/badge/docs-wiki-blue.svg)](https://github.com/prologic/pastebin/wiki)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/prologic/pastebin)](https://goreportcard.com/report/github.com/prologic/pastebin)
|
||||||
|
[![Coverage](https://coveralls.io/repos/prologic/pastebin/badge.svg)](https://coveralls.io/r/prologic/pastebin)
|
||||||
|
|
||||||
|
pastebin is a web app that allows you to create smart bookmarks, commands and aliases by pointing your web browser's default search engine at a running instance. Similar to bunny1 or yubnub.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Source
|
||||||
|
|
||||||
|
```#!bash
|
||||||
|
$ go install github.com/prologic/pastebin/...
|
||||||
|
```
|
||||||
|
|
||||||
|
### OS X Homebrew
|
||||||
|
|
||||||
|
There is a formula provided that you can tap and install from
|
||||||
|
[prologic/homebrew-pastebin](https://github.com/prologic/homebrew-pastebin):
|
||||||
|
|
||||||
|
```#!bash
|
||||||
|
$ brew tap prologic/pastebin
|
||||||
|
$ brew install pastebin
|
||||||
|
```
|
||||||
|
|
||||||
|
**NB:** This installs the latest released binary; so if you want a more
|
||||||
|
recent unreleased version from master you'll have to clone the repository
|
||||||
|
and build yourself.
|
||||||
|
|
||||||
|
pastebin is still early days so contributions, ideas and expertise are
|
||||||
|
much appreciated and highly welcome!
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Run pastebin:
|
||||||
|
|
||||||
|
```#!bash
|
||||||
|
$ pastebin -bind 127.0.0.1:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Set your browser's default pastebin engine to http://localhost:8000/?q=%s
|
||||||
|
|
||||||
|
Then type `help` to view the main help page, `g foo bar` to perform a [Google](https://google.com) search for "foo bar" or `list` to list all available commands.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
7
config.go
Normal file
7
config.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Config ...
|
||||||
|
type Config struct {
|
||||||
|
expiry int
|
||||||
|
fqdn string
|
||||||
|
}
|
23
config_test.go
Normal file
23
config_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestZeroConfig(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
cfg := Config{}
|
||||||
|
assert.Equal(cfg.expiry, 0)
|
||||||
|
assert.Equal(cfg.fqdn, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
cfg := Config{expiry: 1800, fqdn: "https://localhost"}
|
||||||
|
assert.Equal(cfg.expiry, 1800)
|
||||||
|
assert.Equal(cfg.fqdn, "https://localhost")
|
||||||
|
}
|
43
main.go
Normal file
43
main.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
"github.com/namsral/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
db *bolt.DB
|
||||||
|
cfg Config
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
config string
|
||||||
|
dbpath string
|
||||||
|
bind string
|
||||||
|
fqdn string
|
||||||
|
expiry int
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.StringVar(&config, "config", "", "config file")
|
||||||
|
flag.StringVar(&dbpath, "dbpth", "urls.db", "Database path")
|
||||||
|
flag.StringVar(&bind, "bind", "0.0.0.0:8000", "[int]:<port> to bind to")
|
||||||
|
flag.StringVar(&fqdn, "fqdn", "localhost", "FQDN for public access")
|
||||||
|
flag.IntVar(&expiry, "expiery", 5, "expiry time (mins) for passte")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// TODO: Abstract the Config and Handlers better
|
||||||
|
cfg.fqdn = fqdn
|
||||||
|
cfg.expiry = expiry
|
||||||
|
|
||||||
|
var err error
|
||||||
|
db, err = bolt.Open(dbpath, 0600, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
NewServer(bind, cfg).ListenAndServe()
|
||||||
|
}
|
43
scripts/release.sh
Executable file
43
scripts/release.sh
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo -n "Version to tag: "
|
||||||
|
read TAG
|
||||||
|
|
||||||
|
echo -n "Name of release: "
|
||||||
|
read NAME
|
||||||
|
|
||||||
|
echo -n "Desc of release: "
|
||||||
|
read DESC
|
||||||
|
|
||||||
|
git tag ${TAG}
|
||||||
|
git push --tags
|
||||||
|
|
||||||
|
if [ ! -d ./bin ]; then
|
||||||
|
mkdir bin
|
||||||
|
else
|
||||||
|
rm -rf ./bin/*
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "Building binaries ... "
|
||||||
|
|
||||||
|
rice embed-go
|
||||||
|
|
||||||
|
GOOS=linux GOARCH=amd64 go build -o ./bin/pastebin-Linux-x86_64 .
|
||||||
|
GOOS=linux GOARCH=arm64 go build -o ./bin/pastebin-Linux-x86_64 .
|
||||||
|
GOOS=darwin GOARCH=amd64 go build -o ./bin/pastebin-Darwin-x86_64 .
|
||||||
|
GOOS=windows GOARCH=amd64 go build -o ./bin/pastebin-Windows-x86_64.exe .
|
||||||
|
|
||||||
|
echo "DONE"
|
||||||
|
|
||||||
|
echo -n "Uploading binaries ... "
|
||||||
|
|
||||||
|
github-release release \
|
||||||
|
-u prologic -p -r pastebin \
|
||||||
|
-t ${TAG} -n "${NAME}" -d "${DESC}"
|
||||||
|
|
||||||
|
for file in bin/*; do
|
||||||
|
name="$(echo $file | sed -e 's|bin/||g')"
|
||||||
|
github-release upload -u prologic -r pastebin -t ${TAG} -n $name -f $file
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "DONE"
|
109
server.go
Normal file
109
server.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/GeertJohan/go.rice"
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
|
"github.com/renstrom/shortuuid"
|
||||||
|
"go.iondynamics.net/templice"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Server ...
|
||||||
|
type Server struct {
|
||||||
|
bind string
|
||||||
|
config Config
|
||||||
|
store *cache.Cache
|
||||||
|
templates *templice.Template
|
||||||
|
router *httprouter.Router
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) render(w http.ResponseWriter, tmpl string, data interface{}) {
|
||||||
|
err := s.templates.ExecuteTemplate(w, tmpl+".html", data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexHandler ...
|
||||||
|
func (s *Server) IndexHandler() httprouter.Handle {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
|
s.render(w, "index", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasteHandler ...
|
||||||
|
func (s *Server) PasteHandler() httprouter.Handle {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid := shortuuid.NewWithNamespace(s.config.fqdn)
|
||||||
|
blob := r.Form.Get("blob")
|
||||||
|
s.store.Set(uuid, blob, cache.DefaultExpiration)
|
||||||
|
|
||||||
|
u, err := url.Parse(fmt.Sprintf("./%s", uuid))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Internal Error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, r.URL.ResolveReference(u).String(), http.StatusFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewHandler ...
|
||||||
|
func (s *Server) ViewHandler() httprouter.Handle {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
|
uuid := p.ByName("uuid")
|
||||||
|
if uuid == "" {
|
||||||
|
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
blob, ok := s.store.Get(uuid)
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "Not Found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.render(w, "view", struct{ Blob string }{Blob: blob.(string)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe ...
|
||||||
|
func (s *Server) ListenAndServe() {
|
||||||
|
log.Fatal(http.ListenAndServe(s.bind, s.router))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) initRoutes() {
|
||||||
|
s.router.GET("/", s.IndexHandler())
|
||||||
|
s.router.POST("/", s.PasteHandler())
|
||||||
|
s.router.GET("/:uuid", s.ViewHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer ...
|
||||||
|
func NewServer(bind string, config Config) *Server {
|
||||||
|
server := &Server{
|
||||||
|
bind: bind,
|
||||||
|
config: config,
|
||||||
|
router: httprouter.New(),
|
||||||
|
store: cache.New(5*time.Minute, 10*time.Minute),
|
||||||
|
templates: templice.New(rice.MustFindBox("templates")),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := server.templates.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("error loading templates: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server.initRoutes()
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
14
templates/index.html
Normal file
14
templates/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>pastebin</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<p>Enter your text here:</p>
|
||||||
|
<form method="POST" action="">
|
||||||
|
<textarea name="blob" cols=80 rows=24></textarea>
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
10
templates/view.html
Normal file
10
templates/view.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>pastebin</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<pre>{{.Blob}}</pre>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
19
utils.go
Normal file
19
utils.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RandomString ...
|
||||||
|
func RandomString(length int) string {
|
||||||
|
bytes := make([]byte, length)
|
||||||
|
rand.Read(bytes)
|
||||||
|
for i, b := range bytes {
|
||||||
|
bytes[i] = alphabet[b%byte(len(alphabet))]
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user