diff --git a/.gitignore b/.gitignore index 63f6eea..a156fc1 100755 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ cmd/sub-demo/sub-demo cmd/auth-demo/auth-demo .vscode/ **/.DS_Store - +**/*.db diff --git a/cmd/auth-demo/main.go b/cmd/auth-demo/main.go index b383767..de835bb 100755 --- a/cmd/auth-demo/main.go +++ b/cmd/auth-demo/main.go @@ -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() { diff --git a/cmd/gcal2calendar/main.go b/cmd/gcal2calendar/main.go new file mode 100644 index 0000000..d5ebdb7 --- /dev/null +++ b/cmd/gcal2calendar/main.go @@ -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") +} diff --git a/cmd/jchsh/main.go b/cmd/jchsh/main.go new file mode 100644 index 0000000..e08dae3 --- /dev/null +++ b/cmd/jchsh/main.go @@ -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() +} diff --git a/cmd/now/main.go b/cmd/now/main.go index dd95ecf..9f3b79d 100755 --- a/cmd/now/main.go +++ b/cmd/now/main.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/jchenry/jchenry/neralie" + "github.com/jchenry/jchenry/pkg/neralie" ) func main() { diff --git a/cmd/sub-demo/main.go b/cmd/sub-demo/main.go index 3091e68..7d94524 100755 --- a/cmd/sub-demo/main.go +++ b/cmd/sub-demo/main.go @@ -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() { diff --git a/cmd/today/main.go b/cmd/today/main.go index ccb0f9d..a3e48c1 100755 --- a/cmd/today/main.go +++ b/cmd/today/main.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/jchenry/jchenry/arvelie" + "github.com/jchenry/jchenry/pkg/arvelie" ) func main() { diff --git a/cmd/web-tinkertoy/jch b/cmd/web-tinkertoy/jch deleted file mode 100755 index 418ef4f..0000000 Binary files a/cmd/web-tinkertoy/jch and /dev/null differ diff --git a/cmd/web-tinkertoy/server.go b/cmd/web-tinkertoy/server.go index 6cbda2e..7b4204e 100644 --- a/cmd/web-tinkertoy/server.go +++ b/cmd/web-tinkertoy/server.go @@ -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()) - } diff --git a/cmd/web-tinkertoy/time.go b/cmd/web-tinkertoy/time.go index a12d2db..9239450 100644 --- a/cmd/web-tinkertoy/time.go +++ b/cmd/web-tinkertoy/time.go @@ -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))) } } diff --git a/cmd/wiki/edit.go b/cmd/wiki/edit.go new file mode 100644 index 0000000..fb16702 --- /dev/null +++ b/cmd/wiki/edit.go @@ -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 +} diff --git a/cmd/wiki/http.go b/cmd/wiki/http.go new file mode 100644 index 0000000..340408f --- /dev/null +++ b/cmd/wiki/http.go @@ -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) + } +} diff --git a/cmd/wiki/main.go b/cmd/wiki/main.go new file mode 100644 index 0000000..e057c8f --- /dev/null +++ b/cmd/wiki/main.go @@ -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) + } + } + })))) +} diff --git a/cmd/wiki/os.go b/cmd/wiki/os.go new file mode 100644 index 0000000..5272ee8 --- /dev/null +++ b/cmd/wiki/os.go @@ -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) +} diff --git a/cmd/wiki/render.go b/cmd/wiki/render.go new file mode 100644 index 0000000..6394d12 --- /dev/null +++ b/cmd/wiki/render.go @@ -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 +} diff --git a/cmd/wiki/save.go b/cmd/wiki/save.go new file mode 100644 index 0000000..2d33fd7 --- /dev/null +++ b/cmd/wiki/save.go @@ -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 +} diff --git a/cmd/wiki/search.go b/cmd/wiki/search.go new file mode 100644 index 0000000..9f1eb91 --- /dev/null +++ b/cmd/wiki/search.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + "regexp" +) + +const resultFmt = "%s . . . . . . %s
\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 +} diff --git a/cmd/wiki/view.go b/cmd/wiki/view.go new file mode 100644 index 0000000..24bc2fa --- /dev/null +++ b/cmd/wiki/view.go @@ -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(`%s`, 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(`%s`, string(s), string(s))) } + + /*searchRegexp*/ + patterns[1], renderers[1] = regexp.MustCompile("\\[Search\\]"), func(s []byte) []byte { + return []byte(`
`) + } + /*youTubeLinkRegexp*/ patterns[2], renderers[2] = regexp.MustCompile("https://(www.)?youtube.com/watch\\?v=([-\\w]+)"), func(s []byte) []byte { + return []byte(fmt.Sprintf(``, strings.Split(string(s), "=")[1])) + } + /*isbnLinkRegexp*/ patterns[3], renderers[3] = regexp.MustCompile("ISBN:*([0-9]{10,})"), func(s []byte) []byte { + return []byte(fmt.Sprintf(`ISBN %s`, 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) +} diff --git a/go.mod b/go.mod index 1aa2e46..bf150d6 100755 --- a/go.mod +++ b/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 ) diff --git a/go.sum b/go.sum index b5fb1c6..7e46086 100755 --- a/go.sum +++ b/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= diff --git a/auth/auth.go b/internal/auth/auth.go similarity index 100% rename from auth/auth.go rename to internal/auth/auth.go diff --git a/auth/callback.go b/internal/auth/callback.go similarity index 100% rename from auth/callback.go rename to internal/auth/callback.go diff --git a/auth/config.go b/internal/auth/config.go similarity index 100% rename from auth/config.go rename to internal/auth/config.go diff --git a/auth/login.go b/internal/auth/login.go similarity index 100% rename from auth/login.go rename to internal/auth/login.go diff --git a/auth/logout.go b/internal/auth/logout.go similarity index 100% rename from auth/logout.go rename to internal/auth/logout.go diff --git a/auth/middleware.go b/internal/auth/middleware.go similarity index 100% rename from auth/middleware.go rename to internal/auth/middleware.go diff --git a/auth/service.go b/internal/auth/service.go similarity index 95% rename from auth/service.go rename to internal/auth/service.go index 6bd2c70..21afd76 100755 --- a/auth/service.go +++ b/internal/auth/service.go @@ -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" ) diff --git a/auth/session.go b/internal/auth/session.go similarity index 100% rename from auth/session.go rename to internal/auth/session.go diff --git a/auth/user.go b/internal/auth/user.go similarity index 92% rename from auth/user.go rename to internal/auth/user.go index 8700f29..98b5741 100755 --- a/auth/user.go +++ b/internal/auth/user.go @@ -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) { diff --git a/internal/crud/service.go b/internal/crud/service.go new file mode 100644 index 0000000..5618f2d --- /dev/null +++ b/internal/crud/service.go @@ -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 +} diff --git a/data/db_collection_store.go b/internal/data/db_collection_store.go similarity index 100% rename from data/db_collection_store.go rename to internal/data/db_collection_store.go diff --git a/internal/http/auth.go b/internal/http/auth.go new file mode 100644 index 0000000..f81c965 --- /dev/null +++ b/internal/http/auth.go @@ -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) + } +} diff --git a/http/julienschmidt_router.go b/internal/http/julienschmidt_router.go similarity index 100% rename from http/julienschmidt_router.go rename to internal/http/julienschmidt_router.go diff --git a/http/server.go b/internal/http/server.go similarity index 97% rename from http/server.go rename to internal/http/server.go index f3238af..5c6063f 100755 --- a/http/server.go +++ b/internal/http/server.go @@ -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 { diff --git a/http/server_test.go b/internal/http/server_test.go similarity index 100% rename from http/server_test.go rename to internal/http/server_test.go diff --git a/internal/http/service.go b/internal/http/service.go new file mode 100644 index 0000000..bf0f23c --- /dev/null +++ b/internal/http/service.go @@ -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" diff --git a/http/templates.go b/internal/http/templates.go similarity index 74% rename from http/templates.go rename to internal/http/templates.go index 1cf0e9c..0e6500c 100755 --- a/http/templates.go +++ b/internal/http/templates.go @@ -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 +} diff --git a/http/util.go b/internal/http/util.go similarity index 100% rename from http/util.go rename to internal/http/util.go diff --git a/model/database_model.go b/internal/model/database_model.go similarity index 100% rename from model/database_model.go rename to internal/model/database_model.go diff --git a/payments/config.go b/internal/payments/config.go similarity index 100% rename from payments/config.go rename to internal/payments/config.go diff --git a/payments/doc.go b/internal/payments/doc.go similarity index 100% rename from payments/doc.go rename to internal/payments/doc.go diff --git a/payments/middleware.go b/internal/payments/middleware.go similarity index 93% rename from payments/middleware.go rename to internal/payments/middleware.go index 59d5c86..64a26d5 100755 --- a/payments/middleware.go +++ b/internal/payments/middleware.go @@ -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) { diff --git a/payments/service.go b/internal/payments/service.go similarity index 96% rename from payments/service.go rename to internal/payments/service.go index 3e964bd..abd1895 100755 --- a/payments/service.go +++ b/internal/payments/service.go @@ -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" diff --git a/pic/parser.go b/internal/pic/parser.go similarity index 100% rename from pic/parser.go rename to internal/pic/parser.go diff --git a/pic/pic.y b/internal/pic/pic.y similarity index 100% rename from pic/pic.y rename to internal/pic/pic.y diff --git a/pic/y.output b/internal/pic/y.output similarity index 100% rename from pic/y.output rename to internal/pic/y.output diff --git a/rest/collection.go b/internal/rest/collection.go similarity index 99% rename from rest/collection.go rename to internal/rest/collection.go index d8bd7cc..1ef83dd 100755 --- a/rest/collection.go +++ b/internal/rest/collection.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - _http "github.com/jchenry/jchenry/http" + _http "github.com/jchenry/jchenry/internal/http" ) const ( diff --git a/rest/collection_test.go b/internal/rest/collection_test.go similarity index 100% rename from rest/collection_test.go rename to internal/rest/collection_test.go diff --git a/rest/resultset_response.go b/internal/rest/resultset_response.go similarity index 100% rename from rest/resultset_response.go rename to internal/rest/resultset_response.go diff --git a/arvelie/arvelie.go b/pkg/arvelie/arvelie.go similarity index 100% rename from arvelie/arvelie.go rename to pkg/arvelie/arvelie.go diff --git a/arvelie/arvelie_test.go b/pkg/arvelie/arvelie_test.go similarity index 100% rename from arvelie/arvelie_test.go rename to pkg/arvelie/arvelie_test.go diff --git a/arvelie/doc.go b/pkg/arvelie/doc.go similarity index 100% rename from arvelie/doc.go rename to pkg/arvelie/doc.go diff --git a/db/actor.go b/pkg/db/actor.go similarity index 100% rename from db/actor.go rename to pkg/db/actor.go diff --git a/db/doc.go b/pkg/db/doc.go similarity index 100% rename from db/doc.go rename to pkg/db/doc.go diff --git a/neralie/doc.go b/pkg/neralie/doc.go similarity index 100% rename from neralie/doc.go rename to pkg/neralie/doc.go diff --git a/neralie/neralie.go b/pkg/neralie/neralie.go similarity index 100% rename from neralie/neralie.go rename to pkg/neralie/neralie.go diff --git a/neralie/neralie_test.go b/pkg/neralie/neralie_test.go similarity index 100% rename from neralie/neralie_test.go rename to pkg/neralie/neralie_test.go diff --git a/pkg/snowflake/README.md b/pkg/snowflake/README.md new file mode 100644 index 0000000..7fcf124 --- /dev/null +++ b/pkg/snowflake/README.md @@ -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 diff --git a/pkg/snowflake/snowflake.go b/pkg/snowflake/snowflake.go new file mode 100755 index 0000000..b606028 --- /dev/null +++ b/pkg/snowflake/snowflake.go @@ -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 +} diff --git a/pkg/snowflake/snowflake_test.go b/pkg/snowflake/snowflake_test.go new file mode 100755 index 0000000..a6ddcb4 --- /dev/null +++ b/pkg/snowflake/snowflake_test.go @@ -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() + } +} diff --git a/tablatal/doc.go b/pkg/tablatal/doc.go similarity index 100% rename from tablatal/doc.go rename to pkg/tablatal/doc.go diff --git a/tablatal/tabatal.go b/pkg/tablatal/tabatal.go similarity index 100% rename from tablatal/tabatal.go rename to pkg/tablatal/tabatal.go diff --git a/scripts/bin/openapigen.bash b/scripts/bin/openapigen.bash new file mode 100755 index 0000000..a4ea025 --- /dev/null +++ b/scripts/bin/openapigen.bash @@ -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 \ No newline at end of file diff --git a/scripts/bin/tel.bash b/scripts/bin/tel.bash new file mode 100755 index 0000000..40d1246 --- /dev/null +++ b/scripts/bin/tel.bash @@ -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 + +