plumbed through child add/delete subcommands
This commit is contained in:
parent
8e5f559567
commit
5957437ded
43
children/db.go
Normal file
43
children/db.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package children
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"mcquay.me/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mut sync.Mutex
|
||||||
|
|
||||||
|
type DB map[string]int
|
||||||
|
|
||||||
|
func Load(filename string) (children DB, err error) {
|
||||||
|
db := DB{}
|
||||||
|
if !fs.Exists(filename) {
|
||||||
|
return db, errors.New("db file doesn't exist")
|
||||||
|
}
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(f).Decode(&db); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db DB) Save(filename string) error {
|
||||||
|
mut.Lock()
|
||||||
|
defer mut.Unlock()
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if err := json.NewEncoder(f).Encode(db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -10,12 +9,14 @@ import (
|
|||||||
"github.com/bgentry/speakeasy"
|
"github.com/bgentry/speakeasy"
|
||||||
|
|
||||||
"mcquay.me/allowances"
|
"mcquay.me/allowances"
|
||||||
|
"mcquay.me/allowances/children"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `allowances app
|
const usage = `allowances app
|
||||||
|
|
||||||
subcommands:
|
subcommands:
|
||||||
pw -- manage password file
|
pw -- manage password file
|
||||||
|
child -- manage children
|
||||||
serve -- serve webapp
|
serve -- serve webapp
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -26,6 +27,13 @@ subcommands:
|
|||||||
test <passes.json>
|
test <passes.json>
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const childUsage = `allowances child
|
||||||
|
|
||||||
|
subcommands:
|
||||||
|
add <children.json> <child name> <child name> ...
|
||||||
|
delete <children.json> <child name> <child name> ...
|
||||||
|
`
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
fmt.Fprintf(os.Stderr, usage)
|
fmt.Fprintf(os.Stderr, usage)
|
||||||
@ -74,6 +82,41 @@ func main() {
|
|||||||
fmt.Fprintf(os.Stderr, "%s\n", pwUsage)
|
fmt.Fprintf(os.Stderr, "%s\n", pwUsage)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
case "child":
|
||||||
|
childCmd := os.Args[2:]
|
||||||
|
if len(childCmd) < 3 {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", childUsage)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
switch childCmd[0] {
|
||||||
|
case "add":
|
||||||
|
dbfile := os.Args[3]
|
||||||
|
names := os.Args[4:]
|
||||||
|
db, err := children.Load(dbfile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "problem loading children db: %s\n", pwUsage)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
db[name] = 0
|
||||||
|
}
|
||||||
|
db.Save(dbfile)
|
||||||
|
case "delete", "del":
|
||||||
|
dbfile := os.Args[3]
|
||||||
|
names := os.Args[4:]
|
||||||
|
db, err := children.Load(dbfile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "problem loading children db: %s\n", pwUsage)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
delete(db, name)
|
||||||
|
}
|
||||||
|
db.Save(dbfile)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", pwUsage)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
case "serve":
|
case "serve":
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
dbfile := os.Getenv("DB")
|
dbfile := os.Getenv("DB")
|
||||||
@ -93,7 +136,6 @@ func main() {
|
|||||||
port = p
|
port = p
|
||||||
}
|
}
|
||||||
addr := fmt.Sprintf(":%d", port)
|
addr := fmt.Sprintf(":%d", port)
|
||||||
log.Printf("%+v", addr)
|
|
||||||
err = http.ListenAndServe(addr, sm)
|
err = http.ListenAndServe(addr, sm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
35
db.go
35
db.go
@ -2,18 +2,20 @@ package allowances
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
|
"mcquay.me/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var dbMutex sync.RWMutex
|
||||||
|
|
||||||
func GetHashes(filename string) (Passes, bool, error) {
|
func GetHashes(filename string) (Passes, bool, error) {
|
||||||
r := []string{}
|
r := []string{}
|
||||||
exists := false
|
exists := false
|
||||||
if !Exists(filename) {
|
if !fs.Exists(filename) {
|
||||||
return r, exists, nil
|
return r, exists, nil
|
||||||
}
|
}
|
||||||
exists = true
|
exists = true
|
||||||
@ -59,30 +61,3 @@ func (p Passes) Check(attempt string) (bool, error) {
|
|||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbMutex = sync.RWMutex{}
|
|
||||||
|
|
||||||
func loadChildren(filename string) (children map[string]int) {
|
|
||||||
dbMutex.RLock()
|
|
||||||
defer dbMutex.RUnlock()
|
|
||||||
b, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(b, &children)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func dumpChildren(filename string, children map[string]int) {
|
|
||||||
dbMutex.Lock()
|
|
||||||
defer dbMutex.Unlock()
|
|
||||||
b, err := json.Marshal(children)
|
|
||||||
err = ioutil.WriteFile(filename, b, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("serious issue writing children db file", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
12
fs.go
12
fs.go
@ -1,12 +0,0 @@
|
|||||||
package allowances
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
func Exists(path string) bool {
|
|
||||||
if _, err := os.Stat(path); err != nil {
|
|
||||||
return false
|
|
||||||
} else if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
24
handlers.go
24
handlers.go
@ -9,6 +9,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
"mcquay.me/allowances/children"
|
||||||
|
"mcquay.me/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -28,7 +30,8 @@ func NewFailure(msg string) *failure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Allowances struct {
|
type Allowances struct {
|
||||||
db string
|
db children.DB
|
||||||
|
dbfile string
|
||||||
hashes Passes
|
hashes Passes
|
||||||
store *sessions.CookieStore
|
store *sessions.CookieStore
|
||||||
}
|
}
|
||||||
@ -47,11 +50,16 @@ func NewAllowances(sm *http.ServeMux, dbfile, passfile, staticFiles string) (*Al
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !Exists(dbfile) {
|
if !fs.Exists(dbfile) {
|
||||||
return nil, fmt.Errorf("child db file doesn't exist: %q", dbfile)
|
return nil, fmt.Errorf("child db file doesn't exist: %q", dbfile)
|
||||||
}
|
}
|
||||||
|
db, err := children.Load(dbfile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
r := &Allowances{
|
r := &Allowances{
|
||||||
db: dbfile,
|
db: db,
|
||||||
|
dbfile: dbfile,
|
||||||
hashes: hashes,
|
hashes: hashes,
|
||||||
store: sessions.NewCookieStore([]byte("hello world")),
|
store: sessions.NewCookieStore([]byte("hello world")),
|
||||||
}
|
}
|
||||||
@ -60,8 +68,7 @@ func NewAllowances(sm *http.ServeMux, dbfile, passfile, staticFiles string) (*Al
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Allowances) home(w http.ResponseWriter, req *http.Request, uid string) error {
|
func (a *Allowances) home(w http.ResponseWriter, req *http.Request, uid string) error {
|
||||||
children := loadChildren(a.db)
|
tmpls["home"].Execute(w, map[string]interface{}{"children": a.db})
|
||||||
tmpls["home"].Execute(w, map[string]interface{}{"children": children})
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,12 +109,11 @@ func (a *Allowances) add(w http.ResponseWriter, req *http.Request, uid string) e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't parse a dollar amount: %v", err)
|
return fmt.Errorf("couldn't parse a dollar amount: %v", err)
|
||||||
}
|
}
|
||||||
children := loadChildren(a.db)
|
a.db[child] += amount
|
||||||
children[child] += amount
|
a.db.Save(a.dbfile)
|
||||||
defer dumpChildren(a.db, children)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
b, err := json.Marshal(map[string]interface{}{
|
b, err := json.Marshal(map[string]interface{}{
|
||||||
"amount": dollarize(children[child]),
|
"amount": dollarize(a.db[child]),
|
||||||
"name": child,
|
"name": child,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user