simplifying the repo, commands removed and proper libraries kept

This commit is contained in:
Colin Henry 2020-07-08 18:59:37 -07:00
parent cb0bc37e6b
commit 3bdd3e503e
86 changed files with 1090 additions and 2345 deletions

48
Makefile Normal file
View File

@ -0,0 +1,48 @@
.DEFAULT_GOAL := help
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
ifeq ($(origin .RECIPEPREFIX), undefined)
$(error This Make does not support .RECIPEPREFIX. Please use GNU Make 4.0 or later)
endif
.RECIPEPREFIX = >
# Default - top level rule is what gets ran when you run just `make`
build: out/image-id
.PHONY: build
# Clean up the output directories; since all the sentinel files go under tmp, this will cause everything to get rebuilt
clean:
> rm -rf tmp
> rm -rf out
.PHONY: clean
# Tests - re-ran if any file under src has been changed since tmp/.tests-passed.sentinel was last touched
tmp/.tests-passed.sentinel: $(shell find src -type f)
> mkdir -p $(@D)
> node run test
> touch $@
# Webpack - re-built if the tests have been rebuilt (and so, by proxy, whenever the source files have changed)
tmp/.packed.sentinel: tmp/.tests-passed.sentinel
> mkdir -p $(@D)
> webpack ..
> touch $@
# Docker image - re-built if the webpack output has been rebuilt
out/image-id: tmp/.packed.sentinel
> mkdir -p $(@D)
> image_id="example.com/my-app:$$(pwgen -1)"
> docker build --tag="$${image_id}
> echo "$${image_id}" > out/image-id
help:
# @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
# | sed -n 's/^\(.*\): \(.*\)##\(.*\)/\1\3/p' \
# | column -t -s ' '
.PHONY: help

View File

@ -1,22 +0,0 @@
<html>
<head>
<script src="http://code.jquery.com/jquery-3.1.0.min.js" type="text/javascript"></script>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- font awesome from BootstrapCDN -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="/public/app.css" rel="stylesheet">
</head>
<body class="home">
<div class="container">
<div class="login-page clearfix">
<div class="login-box auth0-box before">
<a id="qsLoginBtn" class="btn btn-primary btn-lg btn-block" href="/login">SignIn</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,34 +0,0 @@
package main
import (
"net/http"
"os"
"github.com/codegangsta/negroni"
"github.com/jchenry/jchenry/internal/auth"
_http "github.com/jchenry/jchenry/internal/http"
)
func main() {
auth.Init()
StartServer()
}
func StartServer() {
auth.PrintConfig()
s := _http.NewServer(negroni.New(), _http.NewJulienschmidtHTTPRouter()).
Static("/public/*filepath", http.Dir("public/")).
Service("", auth.Service(auth.FromEnv())).
Get("/", "", http.HandlerFunc(HomeHandler))
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
s.Run(":" + port)
}
func HomeHandler(w http.ResponseWriter, r *http.Request) {
_http.RenderTemplate(w, "home", nil)
}

View File

@ -1,95 +0,0 @@
body {
font-family: "proxima-nova", sans-serif;
text-align: center;
font-size: 300%;
font-weight: 100;
}
input[type=checkbox],
input[type=radio] {
position: absolute;
opacity: 0;
}
input[type=checkbox] + label,
input[type=radio] + label {
display: inline-block;
}
input[type=checkbox] + label:before,
input[type=radio] + label:before {
content: "";
display: inline-block;
vertical-align: -0.2em;
width: 1em;
height: 1em;
border: 0.15em solid #0074d9;
border-radius: 0.2em;
margin-right: 0.3em;
background-color: white;
}
input[type=radio] + label:before {
border-radius: 50%;
}
input[type=radio]:checked + label:before,
input[type=checkbox]:checked + label:before {
background-color: #0074d9;
box-shadow: inset 0 0 0 0.15em white;
}
input[type=radio]:focus + label:before,
input[type=checkbox]:focus + label:before {
outline: 0;
}
.btn {
font-size: 140%;
text-transform: uppercase;
letter-spacing: 1px;
border: 0;
background-color: #16214D;
color: white;
}
.btn:hover {
background-color: #44C7F4;
}
.btn:focus {
outline: none !important;
}
.btn.btn-lg {
padding: 20px 30px;
}
.btn:disabled {
background-color: #333;
color: #666;
}
h1,
h2,
h3 {
font-weight: 100;
}
#logo img {
width: 300px;
margin-bottom: 60px;
}
.home-description {
font-weight: 100;
margin: 100px 0;
}
h2 {
margin-top: 30px;
margin-bottom: 40px;
font-size: 200%;
}
label {
font-size: 100%;
font-weight: 300;
}
.btn-next {
margin-top: 30px;
}
.answer {
width: 70%;
margin: auto;
text-align: left;
padding-left: 10%;
margin-bottom: 20px;
}
.login-page .login-box {
padding: 100px 0;
}

View File

@ -1 +0,0 @@
AUTH_DOMAIN="https://dev-pb4s8m55.auth0.com/" AUTH_CLIENT_ID="ae1e02bTwXA35O3r3Xxk4kbRf31j5ge9" AUTH_CLIENT_SECRET="NFC5KYeM9GA2z0ptvzKPo9jmkQDRjx_WcsWyK0hzOJmr1CykS9cEmTcNh0-hKiMd" AUTH_CALLBACK_URL="http://localhost:3000/callback" ./auth-demo

View File

@ -1,25 +0,0 @@
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="http://code.jquery.com/jquery-3.1.0.min.js" type="text/javascript"></script>
<!-- font awesome from BootstrapCDN -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script src="/public/js.cookie.js"></script>
<script src="/public/user.js"> </script>
<link href="/public/app.css" rel="stylesheet">
</head>
<body class="home">
<div class="container">
<div class="login-page clearfix">
<div class="logged-in-box auth0-box logged-in">
<img class="avatar" src="{{.Picture}}"/>
<h2>Welcome {{.Nickname}}</h2>
<a id="qsLogoutBtn" class="btn btn-primary btn-lg btn-logout btn-block" href="/logout">Logout</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,78 +0,0 @@
package main
import (
"fmt"
"log"
"sort"
"github.com/rsc/rsc/google"
"github.com/rsc/rsc/imap"
)
type Pair struct {
Key string
Value uint64
}
type PairList []*Pair
func (p PairList) Len() int { return len(p) }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }
func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func main() {
acct := google.Acct("")
c, err := imap.NewClient(imap.TLS, "imap.gmail.com", acct.Email, acct.Password, "/")
if err != nil {
log.Fatal(err)
}
all := c.Inbox()
// all := c.Box("[Gmail]/All Mail")
if err := all.Check(); err != nil {
log.Fatal(err)
}
msgs := all.Msgs()
counts := make(map[string]*Pair)
labels := make(map[string]struct{})
var pairs PairList = make([]*Pair, 0)
for _, m := range msgs {
ls := m.GmailLabels
for _, l := range ls {
labels[l] = struct{}{}
}
email := m.Hdr.From[0].Email
if _, ok := counts[email]; ok {
counts[email].Value = counts[email].Value + 1
} else {
p := Pair{email, 1}
pairs = append(pairs, &p)
counts[email] = &p
}
}
keys := []string{}
for k := range counts {
keys = append(keys, k)
}
sort.Sort(pairs)
for _, e := range pairs {
fmt.Printf("%d %s\t\t\thttps://mail.google.com/mail/u/0/#search/in%%3Ainbox+from%%3A(%s)\n", e.Value, e.Key, e.Key)
}
// for k, _ := range labels {
// fmt.Printf("%s, ", k)
// }
fmt.Println()
}

View File

@ -1,49 +0,0 @@
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")
}

View File

@ -1,64 +0,0 @@
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()
}

View File

@ -1,13 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/jchenry/jchenry/pkg/neralie"
)
func main() {
a := neralie.FromTime(time.Now())
fmt.Println(a)
}

Binary file not shown.

View File

@ -1,22 +0,0 @@
<html>
<head>
<script src="http://code.jquery.com/jquery-3.1.0.min.js" type="text/javascript"></script>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- font awesome from BootstrapCDN -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="/public/app.css" rel="stylesheet">
</head>
<body class="home">
<div class="container">
<div class="login-page clearfix">
<div class="login-box auth0-box before">
<a id="qsLoginBtn" class="btn btn-primary btn-lg btn-block" href="/login">SignIn</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,39 +0,0 @@
package main
import (
"net/http"
"os"
"github.com/codegangsta/negroni"
"github.com/jchenry/jchenry/internal/auth"
_http "github.com/jchenry/jchenry/internal/http"
"github.com/jchenry/jchenry/internal/payments"
)
func main() {
auth.Init()
StartServer()
}
func StartServer() {
auth.PrintConfig()
payments.PrintConfig()
auth_service := auth.Service(auth.FromEnv())
s := _http.NewServer(negroni.New(), _http.NewJulienschmidtHTTPRouter()).
Static("/public/*filepath", http.Dir("public/")).
Service("", auth_service).
Service("", payments.Service(payments.FromEnv(), &auth_service)).
Get("/", "", http.HandlerFunc(HomeHandler))
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
s.Run(":" + port)
}
func HomeHandler(w http.ResponseWriter, r *http.Request) {
_http.RenderTemplate(w, "home", nil)
}

View File

@ -1,95 +0,0 @@
body {
font-family: "proxima-nova", sans-serif;
text-align: center;
font-size: 300%;
font-weight: 100;
}
input[type=checkbox],
input[type=radio] {
position: absolute;
opacity: 0;
}
input[type=checkbox] + label,
input[type=radio] + label {
display: inline-block;
}
input[type=checkbox] + label:before,
input[type=radio] + label:before {
content: "";
display: inline-block;
vertical-align: -0.2em;
width: 1em;
height: 1em;
border: 0.15em solid #0074d9;
border-radius: 0.2em;
margin-right: 0.3em;
background-color: white;
}
input[type=radio] + label:before {
border-radius: 50%;
}
input[type=radio]:checked + label:before,
input[type=checkbox]:checked + label:before {
background-color: #0074d9;
box-shadow: inset 0 0 0 0.15em white;
}
input[type=radio]:focus + label:before,
input[type=checkbox]:focus + label:before {
outline: 0;
}
.btn {
font-size: 140%;
text-transform: uppercase;
letter-spacing: 1px;
border: 0;
background-color: #16214D;
color: white;
}
.btn:hover {
background-color: #44C7F4;
}
.btn:focus {
outline: none !important;
}
.btn.btn-lg {
padding: 20px 30px;
}
.btn:disabled {
background-color: #333;
color: #666;
}
h1,
h2,
h3 {
font-weight: 100;
}
#logo img {
width: 300px;
margin-bottom: 60px;
}
.home-description {
font-weight: 100;
margin: 100px 0;
}
h2 {
margin-top: 30px;
margin-bottom: 40px;
font-size: 200%;
}
label {
font-size: 100%;
font-weight: 300;
}
.btn-next {
margin-top: 30px;
}
.answer {
width: 70%;
margin: auto;
text-align: left;
padding-left: 10%;
margin-bottom: 20px;
}
.login-page .login-box {
padding: 100px 0;
}

View File

@ -1,10 +0,0 @@
#!/bin/bash
AUTH_DOMAIN="https://dev-pb4s8m55.auth0.com/" \
AUTH_CLIENT_ID="ae1e02bTwXA35O3r3Xxk4kbRf31j5ge9" \
AUTH_CLIENT_SECRET="NFC5KYeM9GA2z0ptvzKPo9jmkQDRjx_WcsWyK0hzOJmr1CykS9cEmTcNh0-hKiMd" \
AUTH_CALLBACK_URL="http://localhost:3000/callback" \
AUTH_MGMT_CLIENT_ID="0SpgXDo7HleFZ6NH9ds2t2vkntEzgYqy" \
AUTH_MGMT_CLIENT_SECRET="DhOE1JqO7A2uosadjuyCluK5P3NlxOf4V9mPkEDy4gDO_lYnMffzYfVpgcINvzfr" \
STRIPE_KEY="sk_test_3yIcJl5ev3WfFZ4HNbTMqWv800B26e0c4V" \
STRIPE_PRODUCT_ID="prod_FtzugcD89mNVhp" \
./sub-demo

View File

@ -1,123 +0,0 @@
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="http://code.jquery.com/jquery-3.1.0.min.js" type="text/javascript"></script>
<!-- font awesome from BootstrapCDN -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script src="/public/js.cookie.js"></script>
<link href="/public/app.css" rel="stylesheet">
<script src="https://js.stripe.com/v3/"></script>
</head>
<body class="home">
<div class="container">
{{.Product.Name}}
<form method="POST" id="payment-form">
<div class="form-group">
{{range .Plans}}
<div class="form-check">
<input type="radio" name="plan" class="form-control" id="{{.ID}}" value="{{.ID}}">
<label class="form-check-label" for="{{.ID}}">
{{.Nickname}}({{.AmountDecimal}})
</label>
</div>
{{end}}
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</div>
</form>
</div>
<script>
// Create a Stripe client.
var stripe = Stripe('pk_test_ghSfaAtzKpBNxyrRlgGPuVBu00v5d1ptjL');
// Create an instance of Elements.
var elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// Create an instance of the card Element.
var card = elements.create('card', { style: style });
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function (event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function (event) {
event.preventDefault();
stripe.createToken(card).then(function (result) {
if (result.error) {
// Inform the user if there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
stripeTokenHandler(result.token);
}
});
});
// Submit the form with the token ID.
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
</script>
</body>
</html>

View File

@ -1,36 +0,0 @@
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="http://code.jquery.com/jquery-3.1.0.min.js" type="text/javascript"></script>
<!-- font awesome from BootstrapCDN -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script src="/public/js.cookie.js"></script>
<script src="/public/user.js"> </script>
<link href="/public/app.css" rel="stylesheet">
</head>
<body class="home">
<div class="container">
<div class="login-page clearfix">
<div class="logged-in-box auth0-box logged-in">
<img class="avatar" src="{{.Picture}}" />
<h2>Welcome {{.Nickname}}</h2>
<a id="qsLogoutBtn" class="btn btn-primary btn-lg btn-logout btn-block" href="/logout">Logout</a>
</div>
</div>
</div>
<script>
// $(document).ready(function () {
// $('.btn-logout').click(function (e) {
// Cookies.remove('auth-session');
// });
// });
</script>
</body>
</html>

View File

@ -1,13 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/jchenry/jchenry/pkg/arvelie"
)
func main() {
a := arvelie.FromDate(time.Now())
fmt.Println(a)
}

Binary file not shown.

View File

@ -1,70 +0,0 @@
package main
import (
"context"
"database/sql"
"fmt"
"github.com/jchenry/jchenry/db"
"rsc.io/dbstore"
)
type DBFunc func(db *sql.DB)
type DBActor struct {
DB *sql.DB
ActionChan chan DBFunc
}
func (a *DBActor) Run(ctx context.Context) error {
for {
select {
case f := <-a.ActionChan:
f(a.DB)
case <-ctx.Done():
return ctx.Err()
}
}
}
func DBStoreInsert(store *dbstore.Storage, e interface{}) db.Func {
return func(db *sql.DB) {
err := store.Insert(db, e)
fmt.Println(err)
}
}
func DBStoreDelete(store *dbstore.Storage, e interface{}) db.Func {
return func(db *sql.DB) {
store.Delete(db, e)
}
}
func DBStoreSelect(store *dbstore.Storage,
err chan error,
results chan interface{},
ent interface{},
query string,
args ...interface{}) db.Func {
return func(db *sql.DB) {
if e := store.Select(db, ent, query, args...); e != nil {
err <- e
} else {
results <- ent
}
}
}
func DBStoreRead(store *dbstore.Storage,
err chan error,
results chan interface{},
ent interface{},
columns ...string) db.Func {
return func(db *sql.DB) {
if e := store.Read(db, ent, columns...); e != nil {
err <- e
} else {
results <- ent
}
}
}

View File

@ -1,36 +0,0 @@
package main
import (
"net/http"
"sync"
"text/template"
)
func (s *server) handleEcho() http.HandlerFunc {
var (
init sync.Once
tmpl *template.Template
tplerr error
)
return func(w http.ResponseWriter, r *http.Request) {
init.Do(func() {
tmpl, tplerr = template.ParseFiles("echoform.tmpl")
})
if tplerr != nil {
http.Error(w, tplerr.Error(), http.StatusInternalServerError)
return
}
switch r.Method {
case http.MethodPost:
r.ParseForm()
s.echoMessage = r.Form.Get("msg")
http.Redirect(w, r, "/echo", 301)
return
case http.MethodGet:
if err := tmpl.Execute(w, map[string]string{"Message": s.echoMessage}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
}

View File

@ -1,15 +0,0 @@
<html>
<body>
{{if (eq .Message "")}}
<form action="/echo" method="post">
<input type="text" id="msg" name="msg">
<input type="submit" value="Submit">
</form>
{{else}}
{{.Message}}
{{end}}
</body>
</html>

Binary file not shown.

View File

@ -1,96 +0,0 @@
package main
import (
"bufio"
"context"
"fmt"
"log"
"net/http"
"sync"
"text/template"
"github.com/jchenry/jchenry/db"
"rsc.io/dbstore"
)
func (s *server) handleFortune() http.HandlerFunc {
var (
init sync.Once
dba *db.Actor
storage *dbstore.Storage
tmpl *template.Template
tplerr error
)
type fortuneWrapper struct {
Fortune string
}
return func(w http.ResponseWriter, r *http.Request) {
init.Do(func() {
ctx, _ := context.WithCancel(context.Background())
dba = &db.Actor{
DB: s.db,
ActionChan: make(chan db.Func),
}
go dba.Run(ctx)
storage = new(dbstore.Storage)
storage.Register(&fortuneWrapper{})
err := storage.CreateTables(dba.DB)
if err != nil {
fmt.Println(err)
}
tmpl, tplerr = template.ParseFiles("fortunesupload.tmpl")
})
if tplerr != nil {
http.Error(w, tplerr.Error(), http.StatusInternalServerError)
return
}
switch r.Method {
case http.MethodPost:
r.ParseMultipartForm(10 << 20)
file, _, err := r.FormFile("fortunes")
if err != nil {
fmt.Println("Error Retrieving the File")
fmt.Println(err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
var fortune string
for scanner.Scan() {
switch scanner.Text() {
case "%":
dba.ActionChan <- DBStoreInsert(storage, &fortuneWrapper{Fortune: fortune})
default:
fortune = scanner.Text()
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
case http.MethodGet:
f := fortuneWrapper{}
results := make(chan interface{})
err := make(chan error)
dba.ActionChan <- DBStoreSelect(storage, err, results, &f, "ORDER BY RANDOM() LIMIT 1", "*")
select {
case r := <-results:
if err := tmpl.Execute(w, map[string]string{"Message": r.(*fortuneWrapper).Fortune}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
case e := <-err:
http.Error(w, e.Error(), http.StatusInternalServerError)
}
}
}
}

View File

@ -1,15 +0,0 @@
<html>
<body>
{{if (eq .Message "")}}
<form
enctype="multipart/form-data"
method="post">
<input type="file" name="fortunes" />
<input type="submit" value="upload" />
</form>
{{else}}
{{.Message}}
{{end}}
</body>
</html>

View File

@ -1,22 +0,0 @@
module github.com/jchenry/jchenry/cmd/web-tinkertoy
go 1.14
require (
github.com/codegangsta/negroni v1.0.0 // indirect
github.com/coreos/go-oidc v2.1.0+incompatible // indirect
github.com/gorilla/sessions v1.2.0 // indirect
github.com/jchenry/jchenry v0.0.0-00010101000000-000000000000
github.com/julienschmidt/httprouter v1.2.0 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/stretchr/testify v1.4.0 // indirect
github.com/stripe/stripe-go v63.4.0+incompatible // indirect
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect
gopkg.in/auth0.v1 v1.2.7 // indirect
gopkg.in/square/go-jose.v2 v2.3.1 // indirect
rsc.io/dbstore v0.1.1
)
replace github.com/jchenry/jchenry => ../../

View File

@ -1,42 +0,0 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/PuerkitoBio/rehttp v0.0.0-20180310210549-11cf6ea5d3e9/go.mod h1:ItsOiHl4XeMOV3rzbZqQRjLc3QQxbE6391/9iNG7rE8=
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/go.mod h1:Rp8e0DCtEKwXFOC6JPJQVTz8tuGoGvw6Xfexggh/ed0=
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/jchenry/jchenry v0.0.0-20191107034549-4697006d1099 h1:qUy+BhiLM5LzN0cOGtqvuC2v7FNfmQFg1cjZbNcMnBY=
github.com/jchenry/jchenry v0.0.0-20191107034549-4697006d1099/go.mod h1:5ywsKLPV6YOTZ7oNNmQo7EQRDKhlGCD311r/ZRZgHDM=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stripe/stripe-go v63.4.0+incompatible/go.mod h1:A1dQZmO/QypXmsL0T8axYZkSN/uA/T/A64pfKdBAMiY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/auth0.v1 v1.2.7/go.mod h1:1FRtMXwYDgygZcO7Of7kj/I4mf9UjHGhMHUOqNT0d0M=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
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/sqlite v0.5.0/go.mod h1:fqHuveM9iIqMzjD0WiZIvKYMty/WqTo2bxE9+zC54WE=

View File

@ -1,34 +0,0 @@
package main
import (
"database/sql"
"fmt"
"io"
"net/http"
"os"
_ "github.com/mattn/go-sqlite3"
)
func main() {
if err := run(os.Args, os.Stdout); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
}
func run(args []string, stdout io.Writer) error {
s := server{
router: http.NewServeMux(),
}
s.routes()
if db, err := sql.Open("sqlite3", "foo.db"); err == nil {
s.db = db
} else {
return err
}
return http.ListenAndServe(":8080", s.router)
}

View File

@ -1,18 +0,0 @@
package main
import (
"database/sql"
"net/http"
)
type server struct {
db *sql.DB
router *http.ServeMux
echoMessage string
}
func (s *server) routes() {
s.router.HandleFunc("/time", s.handleTime())
s.router.HandleFunc("/echo", s.handleEcho())
s.router.HandleFunc("/fortune", s.handleFortune())
}

View File

@ -1,22 +0,0 @@
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, t.String())
io.WriteString(w, fmt.Sprintf("\n%s %s",
arvelie.FromDate(t),
neralie.FromTime(t)))
}
}

View File

@ -1,13 +0,0 @@
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
}

View File

@ -1,18 +0,0 @@
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)
}
}

View File

@ -1,47 +0,0 @@
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)
}
}
}))))
}

View File

@ -1,18 +0,0 @@
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)
}

View File

@ -1,17 +0,0 @@
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
}

View File

@ -1,14 +0,0 @@
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
}

View File

@ -1,32 +0,0 @@
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
}

View File

@ -1,47 +0,0 @@
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)
}

View File

@ -1,4 +1,4 @@
package db
package database
import (
"context"

View File

@ -1,14 +1,15 @@
package db
package database
/*
Example usage:
Example usage:
ctx, _ := context.WithCancel(context.Background())
dba = &db.Actor{
DB: s.db,
ActionChan: make(chan db.Func),
}
go dba.Run(ctx)
*/
*/

6
encoding/coder.go Normal file
View File

@ -0,0 +1,6 @@
package encoding
import "io"
type Encoder func(io.Writer, interface{}) error
type Decoder func(io.Reader, interface{}) error

23
encoding/json/json.go Normal file
View File

@ -0,0 +1,23 @@
package json
import (
"encoding/json"
"io"
)
func Encoder(w io.Writer, e interface{}) error {
return json.NewEncoder(w).Encode(e)
}
func Decoder(r io.Reader, e interface{}) error {
return json.NewDecoder(r).Decode(e)
}
// func Decoder(get func() interface{}) func(io.Reader) (interface{}, error) {
// //TODO I dont like the get() function, find a better way of dealing with this
// return func(r io.Reader) (interface{}, error) {
// e := get()
// err := json.NewDecoder(r).Decode(e)
// return e, err
// }
// }

24
encoding/xml/xml.go Normal file
View File

@ -0,0 +1,24 @@
package xml
import (
"encoding/xml"
"io"
)
func Encoder(w io.Writer, e interface{}) error {
return xml.NewEncoder(w).Encode(e)
}
func Decoder(r io.Reader, e interface{}) error {
return xml.NewDecoder(r).Decode(e)
}
// func Decoder(get func() interface{}) func(io.Reader) (interface{}, error) {
// //TODO I dont like the get() function, find a better way of dealing with this
// return func(r io.Reader) (interface{}, error) {
// e := get()
// err := xml.NewDecoder(r).Decode(e)
// return e, err
// }
// }
//

7
go.mod
View File

@ -1,21 +1,22 @@
module github.com/jchenry/jchenry
module github.com/jchenry/x
go 1.13
require (
fyne.io/fyne v1.3.0
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/jchenry/jchenry v0.0.0-20200615172632-cb0bc37e6b16
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
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
gopkg.in/auth0.v1 v1.2.7
gopkg.in/square/go-jose.v2 v2.3.1 // indirect

66
go.sum
View File

@ -1,8 +1,12 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
fyne.io/fyne v1.3.0 h1:FLlgX/JkD3Chal7tEhRL7fOONVAjQJM/yrVNA+cK/dc=
fyne.io/fyne v1.3.0/go.mod h1:AcBUeR8hetITnnfaLvuVqioWM/lT18WPeMVAobhMbg8=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
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/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
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=
@ -11,14 +15,39 @@ github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fyne-io/mobile v0.0.2 h1:eGmCR5lkFxk0PnPafGppLFRD5QODJfSVdrjhLjanOVg=
github.com/fyne-io/mobile v0.0.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
github.com/jchenry/jchenry v0.0.0-20200615172632-cb0bc37e6b16 h1:vVomaWqIbI/Vyb6uGE8ANmF8V3ktoLiOXdcKQLvwUc4=
github.com/jchenry/jchenry v0.0.0-20200615172632-cb0bc37e6b16/go.mod h1:WLNY6BKAzrUIfnkPA8WCUxkKchKZss4fRSVmbKZuhMg=
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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=
@ -30,38 +59,75 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
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/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
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=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stripe/stripe-go v63.4.0+incompatible h1:zzZR004GZ/si7nyckn4NBhoQOViUu5VJ/sA7NT7oTSs=
github.com/stripe/stripe-go v63.4.0+incompatible/go.mod h1:A1dQZmO/QypXmsL0T8axYZkSN/uA/T/A64pfKdBAMiY=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/auth0.v1 v1.2.7 h1:9UCE5rKFL60rqQENmmJaGdNu7/aby8r8wVcJ83Vj5oU=
gopkg.in/auth0.v1 v1.2.7/go.mod h1:1FRtMXwYDgygZcO7Of7kj/I4mf9UjHGhMHUOqNT0d0M=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/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=

View File

@ -1,40 +1,40 @@
package auth
import (
"context"
"log"
// import (
// "context"
// "log"
"golang.org/x/oauth2"
// "golang.org/x/oauth2"
oidc "github.com/coreos/go-oidc"
)
// oidc "github.com/coreos/go-oidc"
// )
type Authenticator struct {
Provider *oidc.Provider
Config oauth2.Config
Ctx context.Context
}
// type Authenticator struct {
// Provider *oidc.Provider
// Config oauth2.Config
// Ctx context.Context
// }
func NewAuthenticator(domain, clientID, clientSecret, callback string) (*Authenticator, error) {
ctx := context.Background()
// func NewAuthenticator(domain, clientID, clientSecret, callback string) (*Authenticator, error) {
// ctx := context.Background()
provider, err := oidc.NewProvider(ctx, domain)
if err != nil {
log.Printf("failed to get provider: %v", err)
return nil, err
}
// provider, err := oidc.NewProvider(ctx, domain)
// if err != nil {
// log.Printf("failed to get provider: %v", err)
// return nil, err
// }
conf := oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: callback,
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile"},
}
// conf := oauth2.Config{
// ClientID: clientID,
// ClientSecret: clientSecret,
// RedirectURL: callback,
// Endpoint: provider.Endpoint(),
// Scopes: []string{oidc.ScopeOpenID, "profile"},
// }
return &Authenticator{
Provider: provider,
Config: conf,
Ctx: ctx,
}, nil
}
// return &Authenticator{
// Provider: provider,
// Config: conf,
// Ctx: ctx,
// }, nil
// }

View File

@ -1,89 +1,89 @@
package auth
import (
"context"
"log"
"net/http"
// import (
// "context"
// "log"
// "net/http"
oidc "github.com/coreos/go-oidc"
)
// oidc "github.com/coreos/go-oidc"
// )
func NewCallbackHandler(c Config) http.HandlerFunc {
// func NewCallbackHandler(c Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, err := Store.Get(r, SessionName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// return func(w http.ResponseWriter, r *http.Request) {
// session, err := Store.Get(r, SessionName)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
if r.URL.Query().Get("state") != session.Values["state"] {
http.Error(w, "Invalid state parameter", http.StatusBadRequest)
return
}
// if r.URL.Query().Get("state") != session.Values["state"] {
// http.Error(w, "Invalid state parameter", http.StatusBadRequest)
// return
// }
authenticator, err := NewAuthenticator(c.Domain, c.ClientID, c.ClientSecret, c.CallbackURL)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// authenticator, err := NewAuthenticator(c.Domain, c.ClientID, c.ClientSecret, c.CallbackURL)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
token, err := authenticator.Config.Exchange(context.TODO(), r.URL.Query().Get("code"))
if err != nil {
log.Printf("no token found: %v", err)
w.WriteHeader(http.StatusUnauthorized)
return
}
// token, err := authenticator.Config.Exchange(context.TODO(), r.URL.Query().Get("code"))
// if err != nil {
// log.Printf("no token found: %v", err)
// w.WriteHeader(http.StatusUnauthorized)
// return
// }
rawIDToken, ok := token.Extra("id_token").(string)
if !ok {
http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
return
}
// rawIDToken, ok := token.Extra("id_token").(string)
// if !ok {
// http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
// return
// }
oidcConfig := &oidc.Config{
ClientID: c.ClientID,
}
// oidcConfig := &oidc.Config{
// ClientID: c.ClientID,
// }
idToken, err := authenticator.Provider.Verifier(oidcConfig).Verify(context.TODO(), rawIDToken)
// idToken, err := authenticator.Provider.Verifier(oidcConfig).Verify(context.TODO(), rawIDToken)
if err != nil {
http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
return
}
// if err != nil {
// http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
// return
// }
// Getting now the userInfo
user := User{}
if err := idToken.Claims(&user); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// // Getting now the userInfo
// user := User{}
// if err := idToken.Claims(&user); err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
session.Values["id_token"] = rawIDToken
session.Values["access_token"] = token.AccessToken
session.Values["profile"] = user
err = session.Save(r, w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// session.Values["id_token"] = rawIDToken
// session.Values["access_token"] = token.AccessToken
// session.Values["profile"] = user
// err = session.Save(r, w)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// if application ID is non existent, and therefore does not have a tenant
// Create or associate?
// Create:
// - Create Tenant
// - Specify plan
// - Specify payment info
// - Associate Tenant
// - by email address domain?
//set tenant ID on application ID in App Metadata on user
// // if application ID is non existent, and therefore does not have a tenant
// // Create or associate?
// // Create:
// // - Create Tenant
// // - Specify plan
// // - Specify payment info
// // - Associate Tenant
// // - by email address domain?
// //set tenant ID on application ID in App Metadata on user
// if c.CallbackFunc != nil {
// c.CallbackFunc(c, user)
// } else {
// Redirect to logged in page
http.Redirect(w, r, c.RedirectURL, http.StatusSeeOther)
// }
// // if c.CallbackFunc != nil {
// // c.CallbackFunc(c, user)
// // } else {
// // Redirect to logged in page
// http.Redirect(w, r, c.RedirectURL, http.StatusSeeOther)
// // }
}
}
// }
// }

View File

@ -1,36 +1,36 @@
package auth
import (
"fmt"
"os"
)
// import (
// "fmt"
// "os"
// )
type Config struct {
Domain string
ClientID string
ClientSecret string
ManagementClientID string
ManagementClientSecret string
// type Config struct {
// Domain string
// ClientID string
// ClientSecret string
// ManagementClientID string
// ManagementClientSecret string
CallbackURL string
RedirectURL string
}
// CallbackURL string
// RedirectURL string
// }
func FromEnv() Config {
return Config{
Domain: os.Getenv("AUTH_DOMAIN"),
ClientID: os.Getenv("AUTH_CLIENT_ID"),
ClientSecret: os.Getenv("AUTH_CLIENT_SECRET"),
ManagementClientID: os.Getenv("AUTH_MGMT_CLIENT_ID"),
ManagementClientSecret: os.Getenv("AUTH_MGMT_CLIENT_SECRET"),
// func FromEnv() Config {
// return Config{
// Domain: os.Getenv("AUTH_DOMAIN"),
// ClientID: os.Getenv("AUTH_CLIENT_ID"),
// ClientSecret: os.Getenv("AUTH_CLIENT_SECRET"),
// ManagementClientID: os.Getenv("AUTH_MGMT_CLIENT_ID"),
// ManagementClientSecret: os.Getenv("AUTH_MGMT_CLIENT_SECRET"),
CallbackURL: os.Getenv("AUTH_CALLBACK_URL"),
RedirectURL: "/user",
}
}
// CallbackURL: os.Getenv("AUTH_CALLBACK_URL"),
// RedirectURL: "/user",
// }
// }
func PrintConfig() {
fmt.Printf("%#v\n", FromEnv())
}
// func PrintConfig() {
// fmt.Printf("%#v\n", FromEnv())
// }
type CallbackFunc func(c Config, u User) error
// type CallbackFunc func(c Config, u User) error

View File

@ -2,42 +2,42 @@
package auth
import (
"crypto/rand"
"encoding/base64"
"net/http"
)
// import (
// "crypto/rand"
// "encoding/base64"
// "net/http"
// )
func NewLoginHandler(c Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Generate random state
b := make([]byte, 32)
_, err := rand.Read(b)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
state := base64.StdEncoding.EncodeToString(b)
// func NewLoginHandler(c Config) http.HandlerFunc {
// return func(w http.ResponseWriter, r *http.Request) {
// // Generate random state
// b := make([]byte, 32)
// _, err := rand.Read(b)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// state := base64.StdEncoding.EncodeToString(b)
session, err := Store.Get(r, SessionName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session.Values["state"] = state
err = session.Save(r, w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// session, err := Store.Get(r, SessionName)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// session.Values["state"] = state
// err = session.Save(r, w)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
authenticator, err := NewAuthenticator(c.Domain, c.ClientID, c.ClientSecret, c.CallbackURL)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// authenticator, err := NewAuthenticator(c.Domain, c.ClientID, c.ClientSecret, c.CallbackURL)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
http.Redirect(w, r, authenticator.Config.AuthCodeURL(state), http.StatusTemporaryRedirect)
}
// http.Redirect(w, r, authenticator.Config.AuthCodeURL(state), http.StatusTemporaryRedirect)
// }
}
// }

View File

@ -1,43 +1,43 @@
package auth
import (
"net/http"
"net/url"
)
// import (
// "net/http"
// "net/url"
// )
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
// func LogoutHandler(w http.ResponseWriter, r *http.Request) {
if cook, err := r.Cookie(SessionName); err == nil {
cook.MaxAge = -1
http.SetCookie(w, cook)
}
// if cook, err := r.Cookie(SessionName); err == nil {
// cook.MaxAge = -1
// http.SetCookie(w, cook)
// }
domain := "dev-pb4s8m55.auth0.com"
// domain := "dev-pb4s8m55.auth0.com"
// var Url *url.URL
URL, err := url.Parse("https://" + domain)
// // var Url *url.URL
// URL, err := url.Parse("https://" + domain)
if err != nil {
panic(err.Error())
}
// if err != nil {
// panic(err.Error())
// }
session, err := Store.Get(r, SessionName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session.Options.MaxAge = -1
// session, err := Store.Get(r, SessionName)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// session.Options.MaxAge = -1
err = session.Save(r, w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
// err = session.Save(r, w)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// }
URL.Path += "/v2/logout"
parameters := url.Values{}
parameters.Add("returnTo", "http://localhost:3000")
parameters.Add("client_id", "ae1e02bTwXA35O3r3Xxk4kbRf31j5ge9")
URL.RawQuery = parameters.Encode()
// URL.Path += "/v2/logout"
// parameters := url.Values{}
// parameters.Add("returnTo", "http://localhost:3000")
// parameters.Add("client_id", "ae1e02bTwXA35O3r3Xxk4kbRf31j5ge9")
// URL.RawQuery = parameters.Encode()
http.Redirect(w, r, URL.String(), http.StatusTemporaryRedirect)
}
// http.Redirect(w, r, URL.String(), http.StatusTemporaryRedirect)
// }

View File

@ -1,19 +1,19 @@
package auth
import "net/http"
// import "net/http"
func IsAuthenticated(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// func IsAuthenticated(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
session, err := Store.Get(r, SessionName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// session, err := Store.Get(r, SessionName)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
if _, ok := session.Values["profile"]; !ok {
//TODO allow customization of redirect
http.Redirect(w, r, "/", http.StatusSeeOther)
} else {
next(w, r)
}
}
// if _, ok := session.Values["profile"]; !ok {
// //TODO allow customization of redirect
// http.Redirect(w, r, "/", http.StatusSeeOther)
// } else {
// next(w, r)
// }
// }

View File

@ -1,39 +1,38 @@
package auth
import (
"net/http"
// import (
// "github.com/jchenry/x/internal/http"
// )
"github.com/codegangsta/negroni"
_http "github.com/jchenry/jchenry/internal/http"
"gopkg.in/auth0.v1/management"
)
// func Service(c Config) ServiceInstance {
// return ServiceInstance{c: c}
// }
func Service(c Config) ServiceInstance {
return ServiceInstance{c: c}
}
// type ServiceInstance struct {
// c Config
// }
type ServiceInstance struct {
c Config
}
// func (si ServiceInstance) Register(m *http.Mux) {
// }
func (si ServiceInstance) Register(uriBase string, s *_http.Server) {
// func (si ServiceInstance) Register(uriBase string, s *_http.Server) {
s.Get(uriBase+"/login", "login endpoint", http.HandlerFunc(NewLoginHandler(si.c)))
s.Get(uriBase+"/logout", "logout endpoint", http.HandlerFunc(LogoutHandler))
s.Get(uriBase+"/callback", "oidc callback", http.HandlerFunc(NewCallbackHandler(si.c)))
s.Get(uriBase+"/user", "user info endpoint", negroni.New(
negroni.HandlerFunc(IsAuthenticated),
negroni.Wrap(http.HandlerFunc(UserHandler)),
))
}
// s.Get(uriBase+"/login", "login endpoint", http.HandlerFunc(NewLoginHandler(si.c)))
// s.Get(uriBase+"/logout", "logout endpoint", http.HandlerFunc(LogoutHandler))
// s.Get(uriBase+"/callback", "oidc callback", http.HandlerFunc(NewCallbackHandler(si.c)))
// s.Get(uriBase+"/user", "user info endpoint", negroni.New(
// negroni.HandlerFunc(IsAuthenticated),
// negroni.Wrap(http.HandlerFunc(UserHandler)),
// ))
// }
func (si ServiceInstance) UpdateUser(u User) error {
m, err := management.New(si.c.Domain, si.c.ManagementClientID, si.c.ManagementClientSecret)
if err != nil {
return err
}
// func (si ServiceInstance) UpdateUser(u User) error {
// m, err := management.New(si.c.Domain, si.c.ManagementClientID, si.c.ManagementClientSecret)
// if err != nil {
// return err
// }
um := management.NewUserManager(m)
// um := management.NewUserManager(m)
return um.Update(u.ID, &management.User{AppMetadata: u.Apps})
}
// return um.Update(u.ID, &management.User{AppMetadata: u.Apps})
// }

View File

@ -1,19 +1,19 @@
package auth
import (
"encoding/gob"
// import (
// "encoding/gob"
"github.com/gorilla/sessions"
)
// "github.com/gorilla/sessions"
// )
const SessionName = "auth-session"
// const SessionName = "auth-session"
var (
Store *sessions.FilesystemStore
)
// var (
// Store *sessions.FilesystemStore
// )
func Init() error {
Store = sessions.NewFilesystemStore("", []byte("something-very-secret"))
gob.Register(User{})
return nil
}
// func Init() error {
// Store = sessions.NewFilesystemStore("", []byte("something-very-secret"))
// gob.Register(User{})
// return nil
// }

View File

@ -1,28 +1,28 @@
package auth
import (
"net/http"
// import (
// "net/http"
jchenry_http "github.com/jchenry/jchenry/internal/http"
)
// jchenry_http "github.com/jchenry/x/internal/http"
// )
func UserHandler(w http.ResponseWriter, r *http.Request) {
// func UserHandler(w http.ResponseWriter, r *http.Request) {
session, err := Store.Get(r, SessionName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// session, err := Store.Get(r, SessionName)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
jchenry_http.RenderTemplate(w, "user", session.Values["profile"])
}
// jchenry_http.RenderTemplate(w, "user", session.Values["profile"])
// }
type User struct {
ID string `json:"sub"`
Email string `json:"email"`
FirstName string `json:"given_name"`
LastName string `json:"family_name"`
Picture string `json:"picture"`
Nickname string `json:"nickname"`
Apps map[string]interface{} `json:"app_metadata,omitempty"`
}
// type User struct {
// ID string `json:"sub"`
// Email string `json:"email"`
// FirstName string `json:"given_name"`
// LastName string `json:"family_name"`
// Picture string `json:"picture"`
// Nickname string `json:"nickname"`
// Apps map[string]interface{} `json:"app_metadata,omitempty"`
// }

View File

@ -1,8 +1,6 @@
package crud
import (
"github.com/jchenry/jchenry/pkg/db"
)
import "github.com/jchenry/jchenry/pkg/db"
type Service interface {
// Find returns a pointer to an array of the results found based on params

79
internal/gopher/client.go Normal file
View File

@ -0,0 +1,79 @@
package gopher
import (
"bufio"
"bytes"
"net"
"sync"
)
const (
// RFC 1436 types
Text byte = '0'
Submenu byte = '1'
Nameserver byte = '2'
Error byte = '3'
Binhex byte = '4'
DOS byte = '5'
UUencode byte = '6'
Search byte = '7'
Telnet byte = '8'
Binary byte = '9'
Mirror byte = '+'
Gif byte = 'g'
Image byte = 'I'
Telnet3270 byte = 'T'
// UnRFC'd Extensions
Doc byte = 'd'
Html byte = 'h'
Info byte = 'i'
Sound byte = 's'
)
type Client struct {
Socket net.Conn
in *bufio.Reader
out *bufio.Writer
init sync.Once
}
func (c *Client) Select(selector string) (m Menu, err error) {
c.init.Do(func() {
c.in = bufio.NewReader(c.Socket)
c.out = bufio.NewWriter(c.Socket)
})
c.out.WriteString(selector)
for {
if l, _, err := c.in.ReadLine(); err == nil {
s := Selector{}
s.Type = l[0]
bs := bytes.Split(l[1:], []byte{'\t'})
s.Display = string(bs[0])
s.Path = string(bs[1])
s.Hostname = string(bytes.Join(bs[2:3], []byte{':'}))
m = append(m, s)
} else {
break
}
}
// s := Selector{
// Type: Text,
// Display: "",
// Hostname: "",
// Path: "",
// }
return Menu{}, nil
}
type Menu []Selector
type Selector struct {
Type byte
Display string
Path string
Hostname string
Port string
}

14
internal/http/error.go Normal file
View File

@ -0,0 +1,14 @@
package http
import "net/http"
// Error is an error wrapper for the standard HTTP error codes
type Error int
func (e Error) Error() string {
return http.StatusText(int(e))
}
func (e Error) Code() int {
return int(e)
}

8
internal/http/mux.go Normal file
View File

@ -0,0 +1,8 @@
package http
import "net/http"
type Mux interface {
Handle(pattern string, handler http.Handler)
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
}

View File

@ -1,7 +1,8 @@
package http
import (
"log"
// "log"
"net/http"
go_http "net/http"
)
@ -17,86 +18,87 @@ type Router interface {
AddHandler(method, path string, handler go_http.Handler)
}
type Service interface {
Register(uriBase string, restServer *Server)
}
// type Service interface {
// Register(uriBase string, restServer *Server)
// }
type ServiceFunc func(uriBase string, restServer *Server)
// type ServiceFunc func(uriBase string, restServer *Server)
func (f ServiceFunc) Register(uriBase string, restServer *Server) {
f(uriBase, restServer)
}
// func (f ServiceFunc) Register(uriBase string, restServer *Server) {
// f(uriBase, restServer)
// }
var docString = "%s \t%s\t- %s"
// var docString = "%s \t%s\t- %s"
type Server struct {
router Router
middleware Middleware
}
func NewServer(m Middleware, r Router) *Server {
s := &Server{
router: r,
middleware: m,
}
// func NewServer(m Middleware, r Router) *Server {
// s := &Server{
// router: r,
// middleware: m,
// }
s.middleware.UseHandler(s.router)
// s.middleware.UseHandler(s.router)
return s
}
// return s
// }
func (r *Server) Get(path string, documentation string, handle go_http.Handler) *Server {
r.handle("GET", path, documentation, handle)
return r
}
func (r *Server) Patch(path string, documentation string, handle go_http.Handler) *Server {
r.handle("PATCH", path, documentation, handle)
return r
}
// func (r *Server) Patch(path string, documentation string, handle go_http.Handler) *Server {
// r.handle("PATCH", path, documentation, handle)
// return r
// }
func (r *Server) Post(path string, documentation string, handle go_http.Handler) *Server {
r.handle("POST", path, documentation, handle)
return r
}
func (r *Server) Put(path string, documentation string, handle go_http.Handler) *Server {
r.handle("PUT", path, documentation, handle)
return r
}
func (r *Server) Delete(path string, documentation string, handle go_http.Handler) *Server {
r.handle("DELETE", path, documentation, handle)
// func (r *Server) Put(path string, documentation string, handle go_http.Handler) *Server {
// r.handle("PUT", path, documentation, handle)
return r
}
// return r
// }
// func (r *Server) Delete(path string, documentation string, handle go_http.Handler) *Server {
// r.handle("DELETE", path, documentation, handle)
// return r
// }
func (r *Server) handle(method, path string, documentation string, handler go_http.Handler) {
log.Printf(docString, method, path, documentation)
// log.Printf(docString, method, path, documentation)
r.router.AddHandler(method, path, handler)
}
func (r *Server) Banner(banner string) *Server {
log.Printf(banner)
return r
}
// func (r *Server) Banner(banner string) *Server {
// log.Printf(banner)
// return r
// }
func (r *Server) Service(basePath string, service Service) *Server {
service.Register(basePath, r)
return r
}
func (r *Server) Static(path string, root go_http.FileSystem) *Server {
r.router.ServeFiles(path, root)
return r
}
func (r *Server) Middleware(handler go_http.Handler) *Server {
r.middleware.UseHandler(handler)
return r
}
// func (r *Server) Service(basePath string, service Service) *Server {
// service.Register(basePath, r)
// return r
// }
// func (r *Server) Static(path string, root go_http.FileSystem) *Server {
// r.router.ServeFiles(path, root)
// return r
// }
// func (r *Server) Middleware(handler go_http.Handler) *Server {
// r.middleware.UseHandler(handler)
// return r
// }
func (r *Server) Run(addr string) {
log.Printf("listening on %s", addr)
log.Fatal(http.ListenAndServe(addr, r.middleware))
}
// func (r *Server) Run(addr string) {
// log.Printf("listening on %s", addr)
// log.Fatal(http.ListenAndServe(addr, r.middleware))
// }
func (r *Server) ServeHTTP(w go_http.ResponseWriter, req *go_http.Request) {
r.middleware.ServeHTTP(w, req)
}
// func (r *Server) ServeHTTP(w go_http.ResponseWriter, req *go_http.Request) {
// r.middleware.ServeHTTP(w, req)
// }

View File

@ -1,34 +1,34 @@
package http_test
import (
"os"
// import (
// "os"
"github.com/codegangsta/negroni"
"github.com/jchenry/jchenry/http"
"github.com/jchenry/jchenry/rest"
)
// "github.com/codegangsta/negroni"
// "github.com/jchenry/jchenry/http"
// "github.com/jchenry/jchenry/rest"
// )
func ExampleServer() {
type contact struct {
ID int64 `json:"id"`
First string `json:"firstName"`
Last string `json:"lastName"`
Email string `json:"emailAddress"`
}
// func ExampleServer() {
// type contact struct {
// ID int64 `json:"id"`
// First string `json:"firstName"`
// Last string `json:"lastName"`
// Email string `json:"emailAddress"`
// }
s := http.NewServer(
negroni.Classic(),
http.NewJulienschmidtHTTPRouter()).
Service("",
rest.Collection(new(contact),
nil,
),
)
// s := http.NewServer(
// negroni.Classic(),
// http.NewJulienschmidtHTTPRouter()).
// Service("",
// rest.Collection(new(contact),
// nil,
// ),
// )
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
// port := os.Getenv("PORT")
// if port == "" {
// port = "8080"
// }
s.Run(":" + port)
}
// s.Run(":" + port)
// }

View File

@ -26,3 +26,13 @@ package http
// MethodConnect = "CONNECT"
// MethodOptions = "OPTIONS"
// MethodTrace = "TRACE"
type Service interface {
Register(m *Mux) error
}
type ServiceFunc func(m *Mux) error
func (s ServiceFunc) Register(m *Mux) error {
return s(m)
}

View File

@ -1,25 +1,25 @@
package payments
import (
"fmt"
"os"
)
// import (
// "fmt"
// "os"
// )
type Config struct {
StripeKey string
StripeProductID string
RedirectURL string
TenantSetup func(subscriptionID, customerID string) (tenantID string)
}
// type Config struct {
// StripeKey string
// StripeProductID string
// RedirectURL string
// TenantSetup func(subscriptionID, customerID string) (tenantID string)
// }
func FromEnv() Config {
return Config{
StripeKey: os.Getenv("STRIPE_KEY"),
StripeProductID: os.Getenv("STRIPE_PRODUCT_ID"),
RedirectURL: "/",
}
}
// func FromEnv() Config {
// return Config{
// StripeKey: os.Getenv("STRIPE_KEY"),
// StripeProductID: os.Getenv("STRIPE_PRODUCT_ID"),
// RedirectURL: "/",
// }
// }
func PrintConfig() {
fmt.Printf("%#v\n", FromEnv())
}
// func PrintConfig() {
// fmt.Printf("%#v\n", FromEnv())
// }

View File

@ -1,27 +1,26 @@
package payments
import (
"net/http"
// import (
// "net/http"
"github.com/jchenry/jchenry/internal/auth"
)
// "github.com/jchenry/x/internal/auth"
// )
func HasTenantAndSubscription(productID string) func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// func HasTenantAndSubscription(productID string) func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// session, err := auth.Store.Get(r, auth.SessionName)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
session, err := auth.Store.Get(r, auth.SessionName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if u, ok := session.Values["profile"]; ok {
user := u.(auth.User)
if _, exist := user.Apps[productID]; exist {
next(w, r)
} else {
http.Redirect(w, r, "/subscription", http.StatusSeeOther)
}
}
}
}
// if u, ok := session.Values["profile"]; ok {
// user := u.(auth.User)
// if _, exist := user.Apps[productID]; exist {
// next(w, r)
// } else {
// http.Redirect(w, r, "/subscription", http.StatusSeeOther)
// }
// }
// }
// }

View File

@ -1,121 +1,121 @@
package payments
import (
"fmt"
"net/http"
// import (
// "fmt"
// "net/http"
"github.com/codegangsta/negroni"
"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"
"github.com/stripe/stripe-go/plan"
"github.com/stripe/stripe-go/product"
"github.com/stripe/stripe-go/sub"
)
// "github.com/codegangsta/negroni"
// "github.com/jchenry/x/internal/auth"
// _http "github.com/jchenry/x/internal/http"
// "github.com/stripe/stripe-go"
// "github.com/stripe/stripe-go/client"
// "github.com/stripe/stripe-go/customer"
// "github.com/stripe/stripe-go/plan"
// "github.com/stripe/stripe-go/product"
// "github.com/stripe/stripe-go/sub"
// )
func Service(c Config, auth *auth.ServiceInstance) ServiceInstance {
stripe.Key = c.StripeKey
sc := &client.API{}
sc.Init(c.StripeKey, nil)
return ServiceInstance{
c: c,
stripe: sc,
auth: auth,
}
}
// func Service(c Config, auth *auth.ServiceInstance) ServiceInstance {
// stripe.Key = c.StripeKey
// sc := &client.API{}
// sc.Init(c.StripeKey, nil)
// return ServiceInstance{
// c: c,
// stripe: sc,
// auth: auth,
// }
// }
type ServiceInstance struct {
c Config
stripe *client.API
auth *auth.ServiceInstance
}
// type ServiceInstance struct {
// c Config
// stripe *client.API
// auth *auth.ServiceInstance
// }
func (si ServiceInstance) Register(uriBase string, s *_http.Server) {
s.Get(uriBase+"/subscription", "subscription info endpoint", negroni.New(
negroni.HandlerFunc(auth.IsAuthenticated),
negroni.Wrap(http.HandlerFunc(si.subscriptionHandler)),
)).Post(uriBase+"/subscription", "subscription payment endpoint", negroni.New(
negroni.HandlerFunc(auth.IsAuthenticated),
negroni.Wrap(http.HandlerFunc(si.paymentHandler)),
))
// func (si ServiceInstance) Register(uriBase string, s *_http.Server) {
// s.Get(uriBase+"/subscription", "subscription info endpoint", negroni.New(
// negroni.HandlerFunc(auth.IsAuthenticated),
// negroni.Wrap(http.HandlerFunc(si.subscriptionHandler)),
// )).Post(uriBase+"/subscription", "subscription payment endpoint", negroni.New(
// negroni.HandlerFunc(auth.IsAuthenticated),
// negroni.Wrap(http.HandlerFunc(si.paymentHandler)),
// ))
}
// }
func (si ServiceInstance) subscriptionHandler(w http.ResponseWriter, r *http.Request) {
// func (si ServiceInstance) subscriptionHandler(w http.ResponseWriter, r *http.Request) {
prod, _ := product.Get(si.c.StripeProductID, nil)
// prod, _ := product.Get(si.c.StripeProductID, nil)
params := &stripe.PlanListParams{
Product: &si.c.StripeProductID,
}
// params := &stripe.PlanListParams{
// Product: &si.c.StripeProductID,
// }
it := plan.List(params)
var plans []stripe.Plan
for it.Next() {
plans = append(plans, *it.Plan())
}
_http.RenderTemplate(w, "subscription", offering{Product: *prod, Plans: plans})
}
// it := plan.List(params)
// var plans []stripe.Plan
// for it.Next() {
// plans = append(plans, *it.Plan())
// }
// _http.RenderTemplate(w, "subscription", offering{Product: *prod, Plans: plans})
// }
type offering struct {
Product stripe.Product
Plans []stripe.Plan
}
// type offering struct {
// Product stripe.Product
// Plans []stripe.Plan
// }
func (si ServiceInstance) paymentHandler(w http.ResponseWriter, r *http.Request) {
// func (si ServiceInstance) paymentHandler(w http.ResponseWriter, r *http.Request) {
session, err := auth.Store.Get(r, auth.SessionName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// session, err := auth.Store.Get(r, auth.SessionName)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
if u, ok := session.Values["profile"]; ok {
user := u.(auth.User)
r.ParseForm()
// if u, ok := session.Values["profile"]; ok {
// user := u.(auth.User)
// r.ParseForm()
params := &stripe.CustomerParams{
Email: stripe.String(user.Email),
Name: stripe.String(fmt.Sprintf("%s, %s", user.LastName, user.FirstName)),
}
params.SetSource(r.PostFormValue("stripeToken"))
cus, err := customer.New(params)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// params := &stripe.CustomerParams{
// Email: stripe.String(user.Email),
// Name: stripe.String(fmt.Sprintf("%s, %s", user.LastName, user.FirstName)),
// }
// params.SetSource(r.PostFormValue("stripeToken"))
// cus, err := customer.New(params)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
p := &stripe.SubscriptionParams{
Customer: stripe.String(cus.ID),
Items: []*stripe.SubscriptionItemsParams{
{
Plan: stripe.String(r.PostFormValue("plan")),
},
},
}
s, err := sub.New(p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// p := &stripe.SubscriptionParams{
// Customer: stripe.String(cus.ID),
// Items: []*stripe.SubscriptionItemsParams{
// {
// Plan: stripe.String(r.PostFormValue("plan")),
// },
// },
// }
// s, err := sub.New(p)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
if si.c.TenantSetup == nil {
panic("need code to setup the tenant")
}
// if si.c.TenantSetup == nil {
// panic("need code to setup the tenant")
// }
if user.Apps == nil {
user.Apps = map[string]interface{}{}
}
user.Apps[si.c.StripeProductID] = si.c.TenantSetup(s.ID, user.ID)
err = si.auth.UpdateUser(user)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// if user.Apps == nil {
// user.Apps = map[string]interface{}{}
// }
// user.Apps[si.c.StripeProductID] = si.c.TenantSetup(s.ID, user.ID)
// err = si.auth.UpdateUser(user)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
http.Redirect(w, r, si.c.RedirectURL, http.StatusSeeOther)
// http.Redirect(w, r, si.c.RedirectURL, http.StatusSeeOther)
}
}
// }
// }

View File

@ -1,204 +0,0 @@
package rest
import (
"fmt"
"net/http"
"reflect"
"strconv"
"strings"
_http "github.com/jchenry/jchenry/internal/http"
)
const (
//IDPathParameter represents the entity's id in the parameter map IDPathParameter = "id"
IDPathParameter = "id"
)
// Collection - A Restful Collection interface backed by crud.CrudService
type CollectionInstance struct {
basePath string
name string
instanceType reflect.Type
service CollectionStore
}
type CollectionStore 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{}) error
// Create returns the identifier for the newly accepted entity, or error
Create(entityPtr interface{}) error
// Update returns the id of the newly updated entity, or error
Update(entityPtr interface{}) error
// Delete returns whether the entity, specified by id, was successfully deleted
// or error
Delete(entityPtr interface{}) error
}
// type GetIDPathParameter func(*http.Request)
// Collection - Create a new instance of RESTCollection
func Collection(entityPtr interface{}, service CollectionStore) *CollectionInstance {
t := reflect.TypeOf(entityPtr).Elem()
return &CollectionInstance{
name: strings.ToLower(t.Name()),
instanceType: t,
service: service,
}
}
func (collection *CollectionInstance) Register(uriBase string, restServer *_http.Server) {
plural := properPlural(collection.name)
urlBase := uriBase + "/" + plural //collection.name + "s"
restServer.
Post(urlBase, "create a "+collection.name, http.HandlerFunc(collection.create)).
Put(urlBase+"/:"+IDPathParameter, "update a "+collection.name, http.HandlerFunc(collection.update)).
Delete(urlBase+"/:"+IDPathParameter, "delete a "+collection.name, http.HandlerFunc(collection.remove)).
Get(urlBase+"/:"+IDPathParameter, "get a "+collection.name+" by id", http.HandlerFunc(collection.find)).
Get(urlBase, "get "+collection.name+"s", http.HandlerFunc(collection.find))
}
func properPlural(word string) string {
if strings.HasSuffix(word, "s") {
return word
} else if strings.HasSuffix(word, "y") {
return word[:len(word)-1] + "ies"
} else {
return word + "s"
}
}
func (collection *CollectionInstance) create(response http.ResponseWriter, request *http.Request) {
entityPtr := reflect.New(collection.instanceType).Interface() //collection.instanceProviderPtr.NewInstance()
err := _http.ReadEntity(request, entityPtr)
if err != nil {
_http.WriteErrorResponse(response, http.StatusBadRequest, err.Error())
return
}
err = collection.service.Create(entityPtr)
if err != nil {
_http.WriteErrorResponse(response, http.StatusInternalServerError, err.Error())
return
}
response.WriteHeader(http.StatusCreated)
_http.WriteEntity(response, entityPtr)
}
func (collection *CollectionInstance) update(response http.ResponseWriter, request *http.Request) {
entityPtr := reflect.New(collection.instanceType).Interface() //collection.instanceProviderPtr.NewInstance()
err := _http.ReadEntity(request, entityPtr)
if err != nil {
_http.WriteErrorResponse(response, http.StatusBadRequest, err.Error())
return
}
id := request.Form.Get(IDPathParameter)
err = collection.service.Find(&[]interface{}{}, map[string]interface{}{IDPathParameter: id})
if err != nil {
if err == _http.ErrNotFound {
_http.WriteErrorResponse(response, http.StatusNotFound, fmt.Sprintf("%v with id %v not found", collection.name, id))
} else {
_http.WriteErrorResponse(response, http.StatusInternalServerError, err.Error())
}
return
}
err = collection.service.Update(entityPtr)
if err != nil {
_http.WriteErrorResponse(response, http.StatusInternalServerError, err.Error())
return
}
response.WriteHeader(http.StatusOK)
_http.WriteEntity(response, entityPtr)
}
func (collection *CollectionInstance) remove(response http.ResponseWriter, request *http.Request) {
id := request.Form.Get(IDPathParameter)
err := collection.service.Find(&[]interface{}{}, map[string]interface{}{IDPathParameter: id})
if err != nil {
if err == _http.ErrNotFound {
_http.WriteErrorResponse(response, http.StatusNotFound, fmt.Sprintf("%v with id %v not found", collection.name, id))
} else {
_http.WriteErrorResponse(response, http.StatusInternalServerError, err.Error())
}
return
}
entityPtr := reflect.New(collection.instanceType).Interface() //collection.instanceProviderPtr.NewInstance()
field := reflect.Indirect(reflect.ValueOf(entityPtr)).FieldByName(strings.ToUpper(IDPathParameter))
if !field.CanSet() {
_http.WriteErrorResponse(response, http.StatusInternalServerError, "entity does not have "+IDPathParameter+" field or field is not setable")
}
parsedID, err := strconv.ParseInt(id, 0, 64)
if err != nil {
_http.WriteErrorResponse(response, http.StatusInternalServerError, err.Error())
}
field.SetInt(parsedID)
err = collection.service.Delete(entityPtr)
if err != nil {
_http.WriteErrorResponse(response, http.StatusInternalServerError, err.Error())
return
}
response.WriteHeader(http.StatusNoContent)
}
func (collection *CollectionInstance) find(response http.ResponseWriter, request *http.Request) {
id := request.Form.Get(IDPathParameter)
arrv := reflect.New(reflect.SliceOf(reflect.PtrTo(collection.instanceType)))
arri := arrv.Interface()
err := collection.service.Find(arri, valuesToMap(request.URL.Query(), id))
if err != nil {
if err == _http.ErrNotFound {
_http.WriteErrorResponse(response, http.StatusNotFound, fmt.Sprintf("%v with id %v not found", collection.name, id))
} else {
_http.WriteErrorResponse(response, http.StatusInternalServerError, err.Error())
}
return
}
var results interface{}
if reflect.Indirect(arrv).Len() == 1 {
results = reflect.Indirect(arrv).Index(0).Interface()
fmt.Println(results)
} else {
results = &ResultSetResponse{
Metadata: Metadata{
ResultSet: ResultSetMetadata{
Count: reflect.Indirect(arrv).Len(),
//TODO: need to accomidate limit and offset here.
},
},
Results: arri,
}
}
response.WriteHeader(http.StatusOK)
_http.WriteEntity(response, results)
}
func valuesToMap(params map[string][]string, id string) map[string]interface{} {
m := make(map[string]interface{})
for key, val := range params {
if len(val) == 1 {
m[key] = val[0]
} else {
m[key] = val
}
}
if id != "" {
m[IDPathParameter] = id
}
return m
}

View File

@ -1,294 +0,0 @@
package rest_test
// import (
// "encoding/json"
// "fmt"
// "io/ioutil"
// "log"
// "net/http"
// "net/http/httptest"
// "path/filepath"
// "reflect"
// "runtime"
// "strings"
// "testing"
// // "github.com/jchenry/crud"
// keel_http "github.com/jchenry/http"
// keel_httptest "github.com/jchenry/http/httptest"
// // "github.com/jchenry/rest"
// )
// type TestObject struct {
// ID int64 `json:"id"`
// Name string `json:"name"`
// }
// var service *crud.InMemoryCrudService
// var instanceType reflect.Type = reflect.TypeOf(TestObject{})
// func Setup() {
// if service == nil {
// service = crud.NewInMemoryCrudService()
// }
// log.SetOutput(ioutil.Discard)
// }
// // func TestRESTCollectionTestSuite(t *testing.T) {
// // rsuite := new(RESTCollectionTestSuite)
// // rservice = db.NewInMemoryCrudService()
// // rinstanceType = reflect.TypeOf(TestObject{})
// // log.SetOutput(ioutil.Discard)
// // Run(t, rsuite)
// // }
// func TestCollectionCreate(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// container := createCollectionContainer(instanceType, service)
// requestJSON, err := json.Marshal(TestObject{Name: "Foo"})
// if err != nil {
// Fail(t, "unable to json body")
// }
// request, err := http.NewRequest("POST", "/testobjects", strings.NewReader(string(requestJSON)))
// request.Header.Add("Content-Type", keel_http.MimeJSON)
// if err != nil {
// Fail(t, "unable to create request")
// }
// response := httptest.NewRecorder()
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 201, "{\n \"id\": 1,\n \"name\": \"Foo\"\n }")
// }
// func TestCollectionCreateBadRequest(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// container := createCollectionContainer(instanceType, service)
// request, err := http.NewRequest("POST", "/testobjects", strings.NewReader(string("{malformedjson}")))
// request.Header.Add("Content-Type", keel_http.MimeJSON)
// if err != nil {
// Fail(t, "unable to create request")
// }
// response := httptest.NewRecorder()
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 400, "{\n \"Status\": 400,\n \"DeveloperMessage\": \"invalid character 'm' looking for beginning of object key string\"\n }")
// }
// func TestCollectionCreateInternalError(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// container := createCollectionContainer(instanceType, NewAllFailingCrudService())
// requestJSON, err := json.Marshal(TestObject{Name: "Foo"})
// if err != nil {
// Fail(t, "unable to json body")
// }
// request, err := http.NewRequest("POST", "/testobjects", strings.NewReader(string(requestJSON)))
// request.Header.Add("Content-Type", keel_http.MimeJSON)
// if err != nil {
// Fail(t, "unable to create request")
// }
// response := httptest.NewRecorder()
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 500, "{\n \"Status\": 500,\n \"DeveloperMessage\": \"unable to create\"\n }")
// }
// func TestCollectionUpdate(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// to.ID = 1
// container := createCollectionContainer(instanceType, service)
// requestJSON := keel_httptest.GetJSONReader(t, to)
// request, response := keel_httptest.GetRequestAndResponse(t, "PUT", "/testobjects/1", requestJSON)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 200, "{\n \"id\": 1,\n \"name\": \"foo\"\n }")
// }
// func TestCollectionUpdateBadRequest(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// to.ID = 1
// container := createCollectionContainer(instanceType, service)
// request, response := keel_httptest.GetRequestAndResponse(t, "PUT", "/testobjects/1", strings.NewReader(string("{malformedjson}")))
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 400, "{\n \"Status\": 400,\n \"DeveloperMessage\": \"invalid character 'm' looking for beginning of object key string\"\n }")
// }
// func TestCollectionUpdateBadEntityID(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// to.ID = 42
// container := createCollectionContainer(instanceType, service)
// requestJSON := keel_httptest.GetJSONReader(t, to)
// request, response := keel_httptest.GetRequestAndResponse(t, "PUT", "/testobjects/42", requestJSON)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 404, "{\n \"Status\": 404,\n \"DeveloperMessage\": \"testobject with id 42 not found\"\n }")
// }
// func TestCollectionUpdateInternalErrorOnFind(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// to.ID = 1
// container := createCollectionContainer(instanceType, NewAllFailingCrudService())
// requestJSON := keel_httptest.GetJSONReader(t, to)
// request, response := keel_httptest.GetRequestAndResponse(t, "PUT", "/testobjects/1", requestJSON)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 500, "{\n \"Status\": 500,\n \"DeveloperMessage\": \"unable to find\"\n }")
// }
// func TestCollectionUpdateInternalErrorOnServiceUpdate(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// to.ID = 1
// container := createCollectionContainer(instanceType, NewFailingCrudService(service, false, false, true, false))
// requestJSON := keel_httptest.GetJSONReader(t, to)
// request, response := keel_httptest.GetRequestAndResponse(t, "PUT", "/testobjects/1", requestJSON)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 500, "{\n \"Status\": 500,\n \"DeveloperMessage\": \"unable to update\"\n }")
// }
// func TestDelete(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "deleteObject"
// _, err := service.Create(to)
// if err != nil {
// Fail(t, "unable to create deleteObject")
// }
// container := createCollectionContainer(instanceType, service)
// uri := fmt.Sprintf("/testobjects/%d", int(to.ID))
// request, response := keel_httptest.GetRequestAndResponse(t, "DELETE", uri, nil)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 204, "")
// request, response = keel_httptest.GetRequestAndResponse(t, "DELETE", uri, nil)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 404, "{\n \"Status\": 404,\n \"DeveloperMessage\": \"testobject with id 2 not found\"\n }")
// }
// func TestCollectionDeleteInternalErrorOnFind(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// to.ID = 1
// container := createCollectionContainer(instanceType, NewAllFailingCrudService())
// requestJSON := keel_httptest.GetJSONReader(t, to)
// request, response := keel_httptest.GetRequestAndResponse(t, "DELETE", "/testobjects/1", requestJSON)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 500, "{\n \"Status\": 500,\n \"DeveloperMessage\": \"unable to find\"\n }")
// }
// func TestCollectionDeleteInternalErrorOnServiceDelete(t *testing.T) {
// Setup()
// to := new(TestObject)
// to.Name = "foo"
// to.ID = 1
// container := createCollectionContainer(instanceType, NewFailingCrudService(service, false, false, false, true))
// requestJSON := keel_httptest.GetJSONReader(t, to)
// request, response := keel_httptest.GetRequestAndResponse(t, "DELETE", "/testobjects/1", requestJSON)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 500, "{\n \"Status\": 500,\n \"DeveloperMessage\": \"unable to delete\"\n }")
// }
// func TestCollectionFindSingleItem(t *testing.T) {
// Setup()
// container := createCollectionContainer(instanceType, service)
// uri := fmt.Sprintf("/testobjects/%d", 1)
// request, response := keel_httptest.GetRequestAndResponse(t, "GET", uri, nil)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 200, "{\n \"id\": 1,\n \"name\": \"foo\"\n }")
// }
// //TODO we really should support thie in InMemoryCrudService for code coverage purposes
// // func TestFindOnQuery(t *testing.T) {
// // container := createCollectionContainer(TestObject{}, service)
// // uri := "/testobjects?name=foo"
// // request, response := keel_httptest.GetRequestAndResponse(t, "GET", uri, nil)
// // container.ServeHTTP(response, request)
// // keel_httptest.ValidateResponse(t, response, 200, "{\n \"id\": 1,\n \"name\": \"Foo\"\n }")
// //
// // }
// func TestCollectionFindInternalFailure(t *testing.T) {
// Setup()
// container := createCollectionContainer(instanceType, NewFailingCrudService(service, false, true, false, false))
// uri := fmt.Sprintf("/testobjects/%d", 1)
// request, response := keel_httptest.GetRequestAndResponse(t, "GET", uri, nil)
// container.ServeHTTP(response, request)
// keel_httptest.ValidateResponse(t, response, 500, "{\n \"Status\": 500,\n \"DeveloperMessage\": \"unable to find\"\n }")
// }
// func createCollectionContainer(entity reflect.Type, service crud.CrudService) *rest.Server {
// s := rest.NewServer().
// Service("", rest.NewCollection(entity, service))
// return s
// }
// func Fail(tb testing.TB, msg string) {
// Assert(tb, false, msg)
// }
// // Assert fails the test if the condition is false.
// func Assert(tb testing.TB, condition bool, msg string, v ...interface{}) {
// if !condition {
// _, file, line, _ := runtime.Caller(1)
// fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
// tb.FailNow()
// }
// }
// func NewAllFailingCrudService() crud.CrudService {
// return failingCrudService{}
// }
// type failingCrudService struct{}
// func (a *failingCrudService) Find(entityArrPtr interface{}, params map[string]interface{}) (err error) {
// return crud.ErrNotFound
// }
// func (a *failingCrudService) Create(entityPtr interface{}) (id interface{}, err error) {
// return nil, crud.ErrBadIDType
// }
// func (a *failingCrudService) Update(entityPtr interface{}) (id interface{}, err error) {
// return nil, crud.ErrNotFound
// }
// func (a *failingCrudService) Delete(entityPtr interface{}) error { return crud.ErrNotFound }

View File

@ -1,19 +0,0 @@
package rest
//ResultSetMetadata -
type ResultSetMetadata struct {
Count int `json:"count"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
//Metadata -
type Metadata struct {
ResultSet ResultSetMetadata `json:"resultset"`
}
//ResultSetResponse -
type ResultSetResponse struct {
Metadata Metadata `json:"metadata"`
Results interface{} `json:"results"`
}

118
internal/rss/model.go Normal file
View File

@ -0,0 +1,118 @@
package rss
import (
"encoding/json"
"time"
)
// Feed is an RSS Feed
type Feed struct {
Title string `json:"title,omitempty"`
Link string `json:"link,omitempty"`
Description string `json:"description,omitempty"`
Language string `json:"language,omitempty"`
Copyright string `json:"copyright,omitempty"`
ManagingEditor string `json:"managingEditor,omitempty"`
WebMaster string `json:"webMaster,omitempty"`
PubDate string `json:"pubDate,omitempty"`
PubDateParsed *time.Time `json:"pubDateParsed,omitempty"`
LastBuildDate string `json:"lastBuildDate,omitempty"`
LastBuildDateParsed *time.Time `json:"lastBuildDateParsed,omitempty"`
Categories []*Category `json:"categories,omitempty"`
Generator string `json:"generator,omitempty"`
Docs string `json:"docs,omitempty"`
TTL string `json:"ttl,omitempty"`
Image *Image `json:"image,omitempty"`
Rating string `json:"rating,omitempty"`
SkipHours []string `json:"skipHours,omitempty"`
SkipDays []string `json:"skipDays,omitempty"`
Cloud *Cloud `json:"cloud,omitempty"`
TextInput *TextInput `json:"textInput,omitempty"`
// DublinCoreExt *ext.DublinCoreExtension `json:"dcExt,omitempty"`
// ITunesExt *ext.ITunesFeedExtension `json:"itunesExt,omitempty"`
// Extensions ext.Extensions `json:"extensions,omitempty"`
Items []*Item `json:"items"`
Version string `json:"version"`
}
func (f Feed) String() string {
json, _ := json.MarshalIndent(f, "", " ")
return string(json)
}
// Item is an RSS Item
type Item struct {
Title string `json:"title,omitempty"`
Link string `json:"link,omitempty"`
Description string `json:"description,omitempty"`
Content string `json:"content,omitempty"`
Author string `json:"author,omitempty"`
Categories []*Category `json:"categories,omitempty"`
Comments string `json:"comments,omitempty"`
Enclosure *Enclosure `json:"enclosure,omitempty"`
GUID *GUID `json:"guid,omitempty"`
PubDate string `json:"pubDate,omitempty"`
PubDateParsed *time.Time `json:"pubDateParsed,omitempty"`
Source *Source `json:"source,omitempty"`
// DublinCoreExt *ext.DublinCoreExtension `json:"dcExt,omitempty"`
// ITunesExt *ext.ITunesItemExtension `json:"itunesExt,omitempty"`
// Extensions ext.Extensions `json:"extensions,omitempty"`
}
// Image is an image that represents the feed
type Image struct {
URL string `json:"url,omitempty"`
Link string `json:"link,omitempty"`
Title string `json:"title,omitempty"`
Width string `json:"width,omitempty"`
Height string `json:"height,omitempty"`
Description string `json:"description,omitempty"`
}
// Enclosure is a media object that is attached to
// the item
type Enclosure struct {
URL string `json:"url,omitempty"`
Length string `json:"length,omitempty"`
Type string `json:"type,omitempty"`
}
// GUID is a unique identifier for an item
type GUID struct {
Value string `json:"value,omitempty"`
IsPermalink string `json:"isPermalink,omitempty"`
}
// Source contains feed information for another
// feed if a given item came from that feed
type Source struct {
Title string `json:"title,omitempty"`
URL string `json:"url,omitempty"`
}
// Category is category metadata for Feeds and Entries
type Category struct {
Domain string `json:"domain,omitempty"`
Value string `json:"value,omitempty"`
}
// TextInput specifies a text input box that
// can be displayed with the channel
type TextInput struct {
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
Link string `json:"link,omitempty"`
}
// Cloud allows processes to register with a
// cloud to be notified of updates to the channel,
// implementing a lightweight publish-subscribe protocol
// for RSS feeds
type Cloud struct {
Domain string `json:"domain,omitempty"`
Port string `json:"port,omitempty"`
Path string `json:"path,omitempty"`
RegisterProcedure string `json:"registerProcedure,omitempty"`
Protocol string `json:"protocol,omitempty"`
}

21
internal/rss/parser.go Normal file
View File

@ -0,0 +1,21 @@
package rss
import (
"io"
"golang.org/x/net/html"
)
func Parse(r io.Reader) *Feed {
// z := html.NewTokenizer(r)
return nil
}
func parseFeed(z html.Tokenizer) *Feed {
z.Next()
return nil
}
func parseVersion(z html.Tokenizer) {}

View File

@ -32,6 +32,5 @@ func MutliHandler(h map[string]http.Handler) (http.HandlerFunc, error) {
} else {
NotFoundHandler.ServeHTTP(w, r)
}
}, nil
}

107
rest/collection.go Executable file
View File

@ -0,0 +1,107 @@
package rest
import (
"net/http"
"net/url"
"path/filepath"
"sync"
"github.com/jchenry/x/encoding"
)
type CollectionStore interface {
All(params url.Values) (interface{}, error)
Get(id string) (interface{}, error)
Delete(id string) error
Update(e interface{}) error
New(e interface{}) error
}
// Example: Collection(p, c, JSONEncoder, json.Decode(func()interface{}{return &foo{}}))
// type Decoder func(io.Reader) (interface{}, error)
func Collection(pool *sync.Pool, store CollectionStore, encode EntityEncoder, decode encoding.Decoder) http.HandlerFunc {
return EntityHandler(
collectionGet(store, encode),
collectionPost(store, encode, decode, pool),
collectionPut(store, encode, decode, pool),
collectionDelete(store, encode),
)
}
func collectionGet(store CollectionStore, encode EntityEncoder) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { // GET
if id := filepath.Base(r.URL.Path); id != "" {
if e, err := store.Get(id); err == nil { // handle individual entity
encode(w, e)
} else {
w.WriteHeader(http.StatusInternalServerError)
encode(w, err)
}
} else {
if params, err := url.ParseQuery(r.URL.RawQuery); err == nil {
if e, err := store.All(params); err == nil { // handle all entities
encode(w, e)
} else {
// TODO: we really should write a header here, but need to figure out what it should be
w.WriteHeader(http.StatusInternalServerError)
}
} else {
// encode(w, err)
w.WriteHeader(http.StatusBadRequest)
}
}
}
}
func collectionPost(store CollectionStore, encode EntityEncoder, decode encoding.Decoder, pool *sync.Pool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { // POST TODO
e := pool.New()
defer pool.Put(e)
if err := decode(r.Body, e); err == nil {
if err = store.New(e); err == nil {
w.WriteHeader(http.StatusCreated)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
} else {
w.WriteHeader(http.StatusBadRequest)
}
}
}
func collectionPut(store CollectionStore, encode EntityEncoder, decode encoding.Decoder, pool *sync.Pool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { // PUT TODO
e := pool.New()
defer pool.Put(e)
if err := decode(r.Body, e); err == nil {
if err = store.Update(e); err == nil {
w.WriteHeader(http.StatusAccepted)
encode(w, e)
} else {
w.WriteHeader(http.StatusInternalServerError)
encode(w, err)
}
} else {
w.WriteHeader(http.StatusBadRequest)
encode(w, err)
}
}
}
func collectionDelete(store CollectionStore, encode EntityEncoder) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { // DELETE TODO
if id := filepath.Base(r.URL.Path); id != "" {
if err := store.Delete(id); err == nil {
w.WriteHeader(http.StatusNoContent)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
} else {
w.WriteHeader(http.StatusBadRequest)
}
}
}

23
rest/entity_handler.go Normal file
View File

@ -0,0 +1,23 @@
package rest
import (
gohttp "net/http"
"github.com/jchenry/x/net/http"
)
// EntityHandler returns a handler that provides restful verbs, following a CRUD model
func EntityHandler(
get gohttp.Handler,
post gohttp.Handler,
put gohttp.Handler,
delete gohttp.Handler,
) gohttp.HandlerFunc {
h, _ := http.MutliHandler(map[string]gohttp.Handler{
gohttp.MethodGet: get,
gohttp.MethodPost: post,
gohttp.MethodPut: put,
gohttp.MethodDelete: delete,
})
return h
}

32
rest/response_encoder.go Normal file
View File

@ -0,0 +1,32 @@
package rest
import (
"net/http"
"github.com/jchenry/x/encoding"
"github.com/jchenry/x/encoding/json"
"github.com/jchenry/x/encoding/xml"
)
type EntityEncoder func(w http.ResponseWriter, e interface{})
func JSONEncoder(w http.ResponseWriter, e interface{}) error {
return EntityResponseEncoder(w, "application/json", json.Encoder, e)
}
func XMLEncoder(w http.ResponseWriter, e interface{}) error {
return EntityResponseEncoder(w, "application/xml", xml.Encoder, e)
}
func EntityResponseEncoder(w http.ResponseWriter, contentType string, encoder encoding.Encoder, e interface{}) error {
w.Header().Set("content-type", contentType)
return encoder(w, e)
}
func ErrorResponseEncoder(w http.ResponseWriter, contentType string, encoder encoding.Encoder, status int, err error) error {
w.WriteHeader(status)
return EntityResponseEncoder(w, contentType, encoder, map[string]interface{}{
"status": status,
"message": err.Error,
})
}

View File

@ -1,5 +0,0 @@
#!/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

View File

@ -1,15 +0,0 @@
#!/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

View File

@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/jchenry/libs/arvelie"
"github.com/jchenry/x/time/arvelie"
)
func TestFromDate(t *testing.T) {
@ -49,5 +49,4 @@ func TestToDate(t *testing.T) {
t.Fail()
}
}
}

View File

@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/jchenry/libs/neralie"
"github.com/jchenry/x/time/neralie"
)
func TestFromTime(t *testing.T) {