package reorg
This commit is contained in:
parent
b7b8f7a60b
commit
f24e8c90df
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,4 +2,4 @@ cmd/sub-demo/sub-demo
|
||||
cmd/auth-demo/auth-demo
|
||||
.vscode/
|
||||
**/.DS_Store
|
||||
|
||||
**/*.db
|
||||
|
@ -5,8 +5,8 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/negroni"
|
||||
"github.com/jchenry/jchenry/auth"
|
||||
_http "github.com/jchenry/jchenry/http"
|
||||
"github.com/jchenry/jchenry/internal/auth"
|
||||
_http "github.com/jchenry/jchenry/internal/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
49
cmd/gcal2calendar/main.go
Normal file
49
cmd/gcal2calendar/main.go
Normal file
@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/PuloV/ics-golang"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// calendarFile = flag.String("f", os.Env, "the calendar to convert")
|
||||
help := flag.Bool("help", false, "this help.")
|
||||
flag.Parse()
|
||||
if *help {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
parser := ics.New()
|
||||
parserChan := parser.GetInputChan()
|
||||
outputChan := parser.GetOutputChan()
|
||||
go func() {
|
||||
nowYear := time.Now().Year()
|
||||
for event := range outputChan {
|
||||
if event.GetStart().Year() == nowYear {
|
||||
printEvent(event)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
parserChan <- "https://calendar.google.com/calendar/ical/colin%40jchenry.me/private-ff5ffa18eb856032d166c7f410fe33c0/basic.ics"
|
||||
|
||||
parser.Wait()
|
||||
}
|
||||
|
||||
func printEvent(evt *ics.Event) {
|
||||
fmt.Printf("%s - %s : %s (%s)\n", fmtTime(evt.GetStart()), fmtTime(evt.GetEnd()), evt.GetSummary(), evt.GetLocation())
|
||||
}
|
||||
|
||||
func fmtTime(t time.Time) string {
|
||||
loc, err := time.LoadLocation("America/Los_Angeles")
|
||||
if err != nil {
|
||||
panic("bad timezone")
|
||||
}
|
||||
|
||||
return t.In(loc).Format("Jan 02\t2006 15:04 MST")
|
||||
}
|
64
cmd/jchsh/main.go
Normal file
64
cmd/jchsh/main.go
Normal file
@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jchenry/jchenry/pkg/arvelie"
|
||||
"github.com/jchenry/jchenry/pkg/neralie"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run() (err error) {
|
||||
PS1 := "[%]: "
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
for {
|
||||
fmt.Print(PS1)
|
||||
if input, err := reader.ReadString('\n'); err == nil {
|
||||
if err = execute(input); err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func execute(input string) error {
|
||||
input = strings.TrimSuffix(input, "\n")
|
||||
args := strings.Split(input, " ")
|
||||
|
||||
switch args[0] {
|
||||
case "cd":
|
||||
if len(args) < 2 {
|
||||
return errors.New("path required")
|
||||
}
|
||||
return os.Chdir(args[1])
|
||||
case "now":
|
||||
t := time.Now()
|
||||
fmt.Printf("%s %s\n", arvelie.FromDate(t), neralie.FromTime(t))
|
||||
return nil
|
||||
case "exit":
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
return cmd.Run()
|
||||
}
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jchenry/jchenry/neralie"
|
||||
"github.com/jchenry/jchenry/pkg/neralie"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -5,9 +5,9 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/negroni"
|
||||
"github.com/jchenry/jchenry/auth"
|
||||
_http "github.com/jchenry/jchenry/http"
|
||||
"github.com/jchenry/jchenry/payments"
|
||||
"github.com/jchenry/jchenry/internal/auth"
|
||||
_http "github.com/jchenry/jchenry/internal/http"
|
||||
"github.com/jchenry/jchenry/internal/payments"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jchenry/jchenry/arvelie"
|
||||
"github.com/jchenry/jchenry/pkg/arvelie"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
Binary file not shown.
@ -15,5 +15,4 @@ func (s *server) routes() {
|
||||
s.router.HandleFunc("/time", s.handleTime())
|
||||
s.router.HandleFunc("/echo", s.handleEcho())
|
||||
s.router.HandleFunc("/fortune", s.handleFortune())
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/jchenry/jchenry/arvelie"
|
||||
"github.com/jchenry/jchenry/neralie"
|
||||
)
|
||||
|
||||
func (s *server) handleTime() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
t := time.Now()
|
||||
w.WriteHeader(200)
|
||||
io.WriteString(w, time.Now().String())
|
||||
io.WriteString(w, t.String())
|
||||
io.WriteString(w, fmt.Sprintf("\n%s %s",
|
||||
arvelie.FromDate(t),
|
||||
neralie.FromTime(t)))
|
||||
}
|
||||
}
|
||||
|
13
cmd/wiki/edit.go
Normal file
13
cmd/wiki/edit.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func edit(pageName string, w http.ResponseWriter, r *http.Request) (err error) {
|
||||
if body, err := getFile(pageName, os.O_RDWR|os.O_CREATE); err == nil {
|
||||
return render(pageName, "edit", body, w)
|
||||
}
|
||||
return
|
||||
}
|
18
cmd/wiki/http.go
Normal file
18
cmd/wiki/http.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func auth(fn http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user, pass, _ := r.BasicAuth()
|
||||
if !(user == os.Getenv("WIKI_USERNAME") && pass == os.Getenv("WIKI_PASSWORD")) {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="wiki"`)
|
||||
http.Error(w, "Unauthorized.", 401)
|
||||
return
|
||||
}
|
||||
fn(w, r)
|
||||
}
|
||||
}
|
47
cmd/wiki/main.go
Normal file
47
cmd/wiki/main.go
Normal file
@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type actionFunc func(s string, w http.ResponseWriter, r *http.Request) error
|
||||
|
||||
var pageDir *string
|
||||
|
||||
func main() {
|
||||
p, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pageDir = flag.String("pageDir", path.Join(p, "pages"), "the directory in which pages exist")
|
||||
httpAddr := flag.String("http", "127.0.0.1:8080", " HTTP service address")
|
||||
help := flag.Bool("help", false, "this help.")
|
||||
flag.Parse()
|
||||
if *help {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
for path, action := range map[string]actionFunc{"/wiki/": view, "/edit/": edit, "/save/": save, "/search/": search} {
|
||||
register(path, action)
|
||||
}
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
http.Handle("/", auth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { view("WelcomeVisitors", w, r) })))
|
||||
log.Printf("using log/pass: %s/%s", os.Getenv("WIKI_USERNAME"), os.Getenv("WIKI_PASSWORD"))
|
||||
log.Printf("wiki has started listening at %s", *httpAddr)
|
||||
log.Fatal(http.ListenAndServe(*httpAddr, nil))
|
||||
}
|
||||
|
||||
func register(path string, action actionFunc) {
|
||||
http.Handle(path, http.StripPrefix(path, auth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "" {
|
||||
if err := action(r.URL.Path, w, r); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}))))
|
||||
}
|
18
cmd/wiki/os.go
Normal file
18
cmd/wiki/os.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func getFile(pageName string, flags int) (file []byte, err error) {
|
||||
if f, err := os.OpenFile(path.Join(*pageDir, pageName), flags, 0755); err == nil {
|
||||
return ioutil.ReadAll(f)
|
||||
}
|
||||
return file, err
|
||||
}
|
||||
|
||||
func saveFile(pageName string, contents []byte) error {
|
||||
return ioutil.WriteFile(path.Join(*pageDir, pageName), contents, 0700)
|
||||
}
|
17
cmd/wiki/render.go
Normal file
17
cmd/wiki/render.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func render(p string, m string, body []byte, w http.ResponseWriter) (err error) {
|
||||
if tmpl, err := template.ParseFiles("page.tmpl.html"); err == nil {
|
||||
return tmpl.Execute(w, struct {
|
||||
Mode string
|
||||
Body string
|
||||
Page string
|
||||
}{m, string(body), p})
|
||||
}
|
||||
return err
|
||||
}
|
14
cmd/wiki/save.go
Normal file
14
cmd/wiki/save.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func save(pageName string, w http.ResponseWriter, r *http.Request) (err error) {
|
||||
r.ParseForm()
|
||||
if err = saveFile(pageName, []byte(r.Form.Get("Text"))); err == nil {
|
||||
http.Redirect(w, r, fmt.Sprintf("/wiki/%s", pageName), http.StatusTemporaryRedirect)
|
||||
}
|
||||
return
|
||||
}
|
32
cmd/wiki/search.go
Normal file
32
cmd/wiki/search.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const resultFmt = "<a href=/wiki/%s>%s</a> . . . . . . %s<br>\n"
|
||||
|
||||
func search(keyword string, w http.ResponseWriter, r *http.Request) (err error) {
|
||||
var results string
|
||||
if files, err := ioutil.ReadDir(*pageDir); err == nil {
|
||||
re := regexp.MustCompile(keyword)
|
||||
for _, f := range files {
|
||||
if f.Name() == keyword {
|
||||
results += fmt.Sprintf(resultFmt, f.Name(), f.Name(), f.Name())
|
||||
}
|
||||
if body, err := getFile(f.Name(), os.O_RDWR); err == nil {
|
||||
for _, occur := range re.FindSubmatch(body) {
|
||||
results += fmt.Sprintf(resultFmt, f.Name(), f.Name(), occur)
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
render("search", "view", []byte(results), w)
|
||||
}
|
||||
return err
|
||||
}
|
47
cmd/wiki/view.go
Normal file
47
cmd/wiki/view.go
Normal file
@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
var patterns [5]*regexp.Regexp
|
||||
var renderers [5]func([]byte) []byte
|
||||
|
||||
func init() {
|
||||
// /*autoLinkRegexp*/ patterns[0], renderers[0] = regexp.MustCompile("[A-Z][a-z0-9]+([A-Z][a-z0-9]+)+"), func(s []byte) []byte { return []byte(fmt.Sprintf(`<a href="/wiki/%s/">%s</a>`, string(s), string(s))) }
|
||||
/*BracketedAutoLinkRegexp*/
|
||||
patterns[0], renderers[0] = regexp.MustCompile("\\[\\[[A-Za-z0-9 ]+([A-Za-z0-9 ]+)+\\]\\]"), func(s []byte) []byte { return []byte(fmt.Sprintf(`<a href="/wiki/%s/">%s</a>`, string(s), string(s))) }
|
||||
|
||||
/*searchRegexp*/
|
||||
patterns[1], renderers[1] = regexp.MustCompile("\\[Search\\]"), func(s []byte) []byte {
|
||||
return []byte(`<form id="search_form" action="/search" onsubmit="searchHelper()"><input type="text" size="40" name="search" value=""><input type="submit" value="Search"></form>`)
|
||||
}
|
||||
/*youTubeLinkRegexp*/ patterns[2], renderers[2] = regexp.MustCompile("https://(www.)?youtube.com/watch\\?v=([-\\w]+)"), func(s []byte) []byte {
|
||||
return []byte(fmt.Sprintf(`<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/%s?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>`, strings.Split(string(s), "=")[1]))
|
||||
}
|
||||
/*isbnLinkRegexp*/ patterns[3], renderers[3] = regexp.MustCompile("ISBN:*([0-9]{10,})"), func(s []byte) []byte {
|
||||
return []byte(fmt.Sprintf(`<a href="http://www.amazon.com/exec/obidos/ISBN=%s" rel="nofollow">ISBN %s</a>`, bytes.Replace(bytes.Split(s, []byte(":"))[1], []byte("-"), []byte(""), -1), bytes.Split(s, []byte(":"))[1]))
|
||||
}
|
||||
/*alltextRegexp*/ patterns[4], renderers[4] = regexp.MustCompile(".*"), func(s []byte) []byte {
|
||||
return blackfriday.Run(s, blackfriday.WithExtensions(blackfriday.CommonExtensions))
|
||||
}
|
||||
}
|
||||
|
||||
func view(pageName string, w http.ResponseWriter, r *http.Request) (err error) {
|
||||
var body []byte
|
||||
if body, err = getFile(pageName, os.O_RDWR); os.IsNotExist(err) {
|
||||
http.Redirect(w, r, fmt.Sprintf("/edit/%s", pageName), http.StatusTemporaryRedirect) // no page? redirect to edit/create it.
|
||||
return nil
|
||||
}
|
||||
for i := range renderers {
|
||||
body = patterns[i].ReplaceAllFunc(body, renderers[i])
|
||||
}
|
||||
return render(pageName, "view", body, w)
|
||||
}
|
6
go.mod
6
go.mod
@ -3,11 +3,16 @@ module github.com/jchenry/jchenry
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/PuloV/ics-golang v0.0.0-20190808201353-a3394d3bcade
|
||||
github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61 // indirect
|
||||
github.com/codegangsta/negroni v1.0.0
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible
|
||||
github.com/gorilla/sessions v1.2.0
|
||||
github.com/julienschmidt/httprouter v1.2.0
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/rsc/rsc v0.0.0-20180427141835-fc6202590229
|
||||
github.com/russross/blackfriday/v2 v2.0.1
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.4.0 // indirect
|
||||
github.com/stripe/stripe-go v63.4.0+incompatible
|
||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad // indirect
|
||||
@ -15,4 +20,5 @@ require (
|
||||
gopkg.in/auth0.v1 v1.2.7
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 // indirect
|
||||
rsc.io/dbstore v0.1.1
|
||||
rsc.io/rsc v0.0.0-20180427141835-fc6202590229 // indirect
|
||||
)
|
||||
|
13
go.sum
13
go.sum
@ -1,6 +1,10 @@
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/PuerkitoBio/rehttp v0.0.0-20180310210549-11cf6ea5d3e9 h1:VE0eMvNSQI72dADsq4gm5KpNPmt97WgqneTfaS5MWrs=
|
||||
github.com/PuerkitoBio/rehttp v0.0.0-20180310210549-11cf6ea5d3e9/go.mod h1:ItsOiHl4XeMOV3rzbZqQRjLc3QQxbE6391/9iNG7rE8=
|
||||
github.com/PuloV/ics-golang v0.0.0-20190808201353-a3394d3bcade h1:odEkSCl2gLWPtvraEdCyBZbeYyMMTysWPLMurnB8sUY=
|
||||
github.com/PuloV/ics-golang v0.0.0-20190808201353-a3394d3bcade/go.mod h1:f1P3hjG+t54/IrnXMnnw+gRmFCDR/ryj9xSQ7MPMkQw=
|
||||
github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61 h1:o64h9XF42kVEUuhuer2ehqrlX8rZmvQSU0+Vpj1rF6Q=
|
||||
github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61/go.mod h1:Rp8e0DCtEKwXFOC6JPJQVTz8tuGoGvw6Xfexggh/ed0=
|
||||
github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY=
|
||||
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
|
||||
@ -19,6 +23,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/rsc/rsc v0.0.0-20180427141835-fc6202590229 h1:s5M0EEh5JyTx0PrhLGlog+CegHIkmiCd07ht20coRtA=
|
||||
github.com/rsc/rsc v0.0.0-20180427141835-fc6202590229/go.mod h1:TJRSe/n0/H37q9TsEwBtcOz32UX+UWqgapLwsXTV4jE=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@ -53,5 +64,7 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
rsc.io/dbstore v0.1.1 h1:LI4gBJUwbejn0wHJWe0KTwgCM33zUVP3BsNz5y2fkEE=
|
||||
rsc.io/dbstore v0.1.1/go.mod h1:zI7k1PCSLg9r/T2rBM4E/SctbGmqdtt3kjQSemVh1Rs=
|
||||
rsc.io/rsc v0.0.0-20180427141835-fc6202590229 h1:6s5zUknxnRp4D3GlNb7uDzlcfFVq9G2ficO+k4Bcb6w=
|
||||
rsc.io/rsc v0.0.0-20180427141835-fc6202590229/go.mod h1:nHU4RAWoD9u1Hr+vTW0mktVbANmwCPkTwT2xNpVs/70=
|
||||
rsc.io/sqlite v0.5.0 h1:HG63YxeP0eALjqorwnJ9ENxUUOUR6NYJ4FHEKFJ7aVk=
|
||||
rsc.io/sqlite v0.5.0/go.mod h1:fqHuveM9iIqMzjD0WiZIvKYMty/WqTo2bxE9+zC54WE=
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/codegangsta/negroni"
|
||||
_http "github.com/jchenry/jchenry/http"
|
||||
_http "github.com/jchenry/jchenry/internal/http"
|
||||
"gopkg.in/auth0.v1/management"
|
||||
)
|
||||
|
@ -3,7 +3,7 @@ package auth
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
jchenry_http "github.com/jchenry/jchenry/http"
|
||||
jchenry_http "github.com/jchenry/jchenry/internal/http"
|
||||
)
|
||||
|
||||
func UserHandler(w http.ResponseWriter, r *http.Request) {
|
44
internal/crud/service.go
Normal file
44
internal/crud/service.go
Normal file
@ -0,0 +1,44 @@
|
||||
package crud
|
||||
|
||||
import (
|
||||
"github.com/jchenry/jchenry/pkg/db"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
// Find returns a pointer to an array of the results found based on params
|
||||
// or an error
|
||||
Find(entityArrPtr interface{}, params map[string]interface{}) (err error)
|
||||
// Create returns the identifier for the newly accepted entity, or error
|
||||
Create(entityPtr interface{}) (err error)
|
||||
// Update returns the id of the newly updated entity, or error
|
||||
Update(entityPtr interface{}) (err error)
|
||||
// Delete returns whether the entity, specified by id, was successfully deleted
|
||||
// or error
|
||||
Delete(entityPtr interface{}) error
|
||||
}
|
||||
|
||||
type Storage struct {
|
||||
Actor db.Actor
|
||||
FindOp func(entityArrPtr interface{}, params map[string]interface{}) db.Func
|
||||
CreateOp func(entityPtr interface{}) db.Func
|
||||
UpdateOp func(entityPtr interface{}) db.Func
|
||||
DeleteOp func(entityPtr interface{}) db.Func
|
||||
}
|
||||
|
||||
func (s *Storage) Find(entityArrPtr interface{}, params map[string]interface{}) (err error) {
|
||||
s.Actor.ActionChan <- s.FindOp(entityArrPtr, params)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Storage) Create(entityPtr interface{}) (err error) {
|
||||
s.Actor.ActionChan <- s.CreateOp(entityPtr)
|
||||
return nil
|
||||
}
|
||||
func (s *Storage) Update(entityPtr interface{}) (err error) {
|
||||
s.Actor.ActionChan <- s.UpdateOp(entityPtr)
|
||||
return nil
|
||||
}
|
||||
func (s *Storage) Delete(entityPtr interface{}) error {
|
||||
s.Actor.ActionChan <- s.DeleteOp(entityPtr)
|
||||
return nil
|
||||
}
|
18
internal/http/auth.go
Normal file
18
internal/http/auth.go
Normal file
@ -0,0 +1,18 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func BasicAuth(fn http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user, pass, _ := r.BasicAuth()
|
||||
if !(user == os.Getenv("WIKI_USERNAME") && pass == os.Getenv("WIKI_PASSWORD")) {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="wiki"`)
|
||||
http.Error(w, "Unauthorized.", 401)
|
||||
return
|
||||
}
|
||||
fn(w, r)
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@ import (
|
||||
)
|
||||
|
||||
type Middleware interface {
|
||||
go_http.Handler
|
||||
UseHandler(handler http.Handler)
|
||||
ServeHTTP(w go_http.ResponseWriter, req *go_http.Request)
|
||||
}
|
||||
|
||||
type Router interface {
|
28
internal/http/service.go
Normal file
28
internal/http/service.go
Normal file
@ -0,0 +1,28 @@
|
||||
package http
|
||||
|
||||
// import "net/http"
|
||||
|
||||
// type Service interface {
|
||||
// Register(s Server)
|
||||
// }
|
||||
|
||||
// type Mux interface {
|
||||
// Head(pattern string, handler http.Handler)
|
||||
// Post(pattern string, handler http.Handler)
|
||||
// Put(pattern string, handler http.Handler)
|
||||
// Patch(pattern string, handler http.Handler)
|
||||
// Delete(pattern string, handler http.Handler)
|
||||
// Connect(pattern string, handler http.Handler)
|
||||
// Options(pattern string, handler http.Handler)
|
||||
// Trace(pattern string, handler http.Handler)
|
||||
// }
|
||||
|
||||
// MethodGet = "GET"
|
||||
// MethodHead = "HEAD"
|
||||
// MethodPost = "POST"
|
||||
// MethodPut = "PUT"
|
||||
// MethodPatch = "PATCH" // RFC 5789
|
||||
// MethodDelete = "DELETE"
|
||||
// MethodConnect = "CONNECT"
|
||||
// MethodOptions = "OPTIONS"
|
||||
// MethodTrace = "TRACE"
|
@ -21,3 +21,10 @@ func RenderTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func render(w http.ResponseWriter, tmpl string, data interface{}) (err error) {
|
||||
if t, err := template.ParseFiles(tmpl); err == nil {
|
||||
return t.Execute(w, data)
|
||||
}
|
||||
return err
|
||||
}
|
@ -3,7 +3,7 @@ package payments
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/jchenry/jchenry/auth"
|
||||
"github.com/jchenry/jchenry/internal/auth"
|
||||
)
|
||||
|
||||
func HasTenantAndSubscription(productID string) func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
@ -5,8 +5,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/codegangsta/negroni"
|
||||
"github.com/jchenry/jchenry/auth"
|
||||
_http "github.com/jchenry/jchenry/http"
|
||||
"github.com/jchenry/jchenry/internal/auth"
|
||||
_http "github.com/jchenry/jchenry/internal/http"
|
||||
"github.com/stripe/stripe-go"
|
||||
"github.com/stripe/stripe-go/client"
|
||||
"github.com/stripe/stripe-go/customer"
|
@ -7,7 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_http "github.com/jchenry/jchenry/http"
|
||||
_http "github.com/jchenry/jchenry/internal/http"
|
||||
)
|
||||
|
||||
const (
|
27
pkg/snowflake/README.md
Normal file
27
pkg/snowflake/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# snowflake
|
||||
|
||||
A snowflake ID generator based on instagram and twitter's snowflake concept
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
go get github.com/jchenry/snowflake
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
import "github.com/jchenry/snowflake"
|
||||
|
||||
func main(){
|
||||
snowflake.Next()
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
PRs accepted.
|
||||
|
||||
## License
|
||||
|
||||
MIT © Colin Henry
|
91
pkg/snowflake/snowflake.go
Executable file
91
pkg/snowflake/snowflake.go
Executable file
@ -0,0 +1,91 @@
|
||||
package snowflake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"math"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
totalBits = 64
|
||||
epochBits = 32
|
||||
nodeIDBits = 10
|
||||
sequenceBits = 12
|
||||
|
||||
// Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z)
|
||||
customEpoch uint64 = 1420070400000
|
||||
)
|
||||
|
||||
var maxNodeID uint64
|
||||
var maxSequence uint64
|
||||
|
||||
var nodeID uint64
|
||||
var lastTimestamp uint64 = 0
|
||||
var sequence uint64
|
||||
|
||||
func init() {
|
||||
maxNodeID = uint64(math.Pow(2, nodeIDBits) - 1)
|
||||
maxSequence = uint64(math.Pow(2, sequenceBits) - 1)
|
||||
nodeID = generateNodeID()
|
||||
}
|
||||
|
||||
func generateNodeID() uint64 {
|
||||
var nodeID uint64
|
||||
if interfaces, err := net.Interfaces(); err == nil {
|
||||
h := fnv.New32a()
|
||||
for _, i := range interfaces {
|
||||
h.Write(i.HardwareAddr)
|
||||
}
|
||||
nodeID = uint64(h.Sum32())
|
||||
} else {
|
||||
panic("interfaces not available")
|
||||
}
|
||||
nodeID = nodeID & maxNodeID
|
||||
return nodeID
|
||||
}
|
||||
|
||||
var timestampMutex sync.Mutex
|
||||
var sequenceMutex sync.Mutex
|
||||
|
||||
// Next returns the next logical snowflake
|
||||
func Next() uint64 {
|
||||
timestampMutex.Lock()
|
||||
currentTimestamp := ts()
|
||||
timestampMutex.Unlock()
|
||||
|
||||
sequenceMutex.Lock()
|
||||
if currentTimestamp == lastTimestamp {
|
||||
sequence = (sequence + 1) & maxSequence
|
||||
if sequence == 0 {
|
||||
// Sequence Exhausted, wait till next millisecond.
|
||||
currentTimestamp = waitNextMillis(currentTimestamp)
|
||||
}
|
||||
} else {
|
||||
sequence = 0
|
||||
}
|
||||
sequenceMutex.Unlock()
|
||||
|
||||
lastTimestamp = currentTimestamp
|
||||
id := currentTimestamp << (totalBits - epochBits)
|
||||
fmt.Printf("%b\n", id)
|
||||
id |= (nodeID << (totalBits - epochBits - nodeIDBits))
|
||||
fmt.Printf("%b\n", id)
|
||||
|
||||
id |= sequence
|
||||
fmt.Printf("%b\n", id)
|
||||
return id
|
||||
}
|
||||
|
||||
func ts() uint64 {
|
||||
return uint64(time.Now().UnixNano()/1000000) - customEpoch
|
||||
}
|
||||
|
||||
func waitNextMillis(currentTimestamp uint64) uint64 {
|
||||
for currentTimestamp == lastTimestamp {
|
||||
currentTimestamp = ts()
|
||||
}
|
||||
return currentTimestamp
|
||||
}
|
25
pkg/snowflake/snowflake_test.go
Executable file
25
pkg/snowflake/snowflake_test.go
Executable file
@ -0,0 +1,25 @@
|
||||
package snowflake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNext(t *testing.T) {
|
||||
fmt.Printf("node id: %b\n", generateNodeID())
|
||||
fmt.Printf("timestamp: %b\n", ts())
|
||||
fmt.Printf("full token: %b\n", Next())
|
||||
// t.Fail()
|
||||
}
|
||||
|
||||
func BenchmarkNext(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
Next()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextParallel(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
go Next()
|
||||
}
|
||||
}
|
5
scripts/bin/openapigen.bash
Executable file
5
scripts/bin/openapigen.bash
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli generate \
|
||||
-i $1 \ #https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/2_0/petstore.yaml \
|
||||
-g go \
|
||||
-o /local/out/go
|
15
scripts/bin/tel.bash
Executable file
15
scripts/bin/tel.bash
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# A port of plan9 'tel' program
|
||||
|
||||
for var in "$@"
|
||||
do
|
||||
if test -f "$HOME/.tel"; then
|
||||
grep -i $1 $HOME/.tel
|
||||
fi
|
||||
|
||||
grep -hi $1 /usr/lib/tel /usr/lib/areacodes
|
||||
done
|
||||
|
||||
exit
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user