mirror of https://github.com/dirtbags/moth.git
Adding SQLite engine
This commit is contained in:
parent
d8064e2b1a
commit
307e5bcbec
|
@ -1,8 +1,12 @@
|
||||||
FROM golang:1.12.0-alpine AS builder
|
FROM golang:1.12.0-alpine AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache git gcc musl-dev
|
||||||
|
|
||||||
COPY src /go/src/github.com/dirtbags/moth/src
|
COPY src /go/src/github.com/dirtbags/moth/src
|
||||||
WORKDIR /go/src/github.com/dirtbags/moth/src
|
WORKDIR /go/src/github.com/dirtbags/moth/src
|
||||||
|
|
||||||
RUN go get .
|
RUN go get .
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o /mothd *.go
|
RUN CGO_ENABLED=1 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o /mothd *.go
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=builder /mothd /mothd
|
COPY --from=builder /mothd /mothd
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"errors"
|
||||||
|
"database/sql"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SQLite3ErrAwardingPointsUnique = errors.New("Your team has already solved this puzzle")
|
||||||
|
|
||||||
|
type SQLiteMOTHState struct {
|
||||||
|
database *sql.DB
|
||||||
|
StateDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) Initialize() (bool, error) {
|
||||||
|
database, err := sql.Open("sqlite3", state.StatePath("moth.sqlite3"))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
state.database = database
|
||||||
|
|
||||||
|
if _, err := database.Exec("CREATE TABLE IF NOT EXISTS config (id INTEGER PRIMARY KEY, key TEXT UNIQUE, value TEXT)"); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := database.Exec("CREATE TABLE IF NOT EXISTS teams (id INTEGER PRIMARY KEY, team_name TEXT, team_hash TEXT, UNIQUE(team_name, team_hash))"); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := database.Exec("CREATE TABLE IF NOT EXISTS valid_team_ids (id INTEGER PRIMARY KEY, team_hash TEXT UNIQUE)"); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if _, err := database.Exec("CREATE TABLE IF NOT EXISTS points (id INTEGER PRIMARY KEY, time INTEGER, team_id TEXT, category TEXT, points INTEGER, UNIQUE(team_id, category, points))"); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only do these things if we haven't been initialized
|
||||||
|
if _, err := state.getConfig("initialized"); err != nil {
|
||||||
|
log.Printf("Initialized config missing, re-initializing")
|
||||||
|
|
||||||
|
if len(state.getValidTeamIds()) == 0 {
|
||||||
|
for i := 0; i <= 100; i += 1 {
|
||||||
|
state.database.Exec("REPLACE INTO valid_team_ids (team_hash) VALUES (?)", mktoken())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.setConfig("initialized", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) StatePath(parts ...string) string {
|
||||||
|
tail := pathCleanse(parts)
|
||||||
|
return path.Join(state.StateDir, tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) PointsLog(teamId string) []*Award {
|
||||||
|
var ret []*Award
|
||||||
|
|
||||||
|
var When int64
|
||||||
|
var TeamId string
|
||||||
|
var Category string
|
||||||
|
var Points int
|
||||||
|
|
||||||
|
var rows *sql.Rows
|
||||||
|
|
||||||
|
if len(teamId) > 0 {
|
||||||
|
rows, _ = state.database.Query("SELECT time, team_id, category, points FROM points WHERE team_id = ?", teamId)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rows, _ = state.database.Query("SELECT time, team_id, category, points FROM points")
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
rows.Scan(&When, &TeamId, &Category, &Points)
|
||||||
|
new_award := Award{}
|
||||||
|
new_award.When = time.Unix(When, 0)
|
||||||
|
new_award.TeamId = TeamId
|
||||||
|
new_award.Category = Category
|
||||||
|
new_award.Points = Points
|
||||||
|
ret = append(ret, &new_award)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) AwardPoints(teamID string, category string, points int) error {
|
||||||
|
_, err := state.database.Exec("INSERT INTO points (time, team_id, category, points) VALUES (?, ?, ?, ?)", time.Now().Unix(), teamID, category, points)
|
||||||
|
if sqliteErr, ok := err.(sqlite3.Error); ok {
|
||||||
|
if sqliteErr.Code == sqlite3.ErrConstraint {
|
||||||
|
return SQLite3ErrAwardingPointsUnique
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) TeamName(teamId string) (string, error) {
|
||||||
|
res := state.database.QueryRow("SELECT team_name FROM teams WHERE team_hash = ?", teamId)
|
||||||
|
|
||||||
|
var teamName string
|
||||||
|
|
||||||
|
err := res.Scan(&teamName)
|
||||||
|
|
||||||
|
return teamName, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) isEnabled() bool {
|
||||||
|
if res, _ := state.getConfig("disabled"); res == "true" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if res, err := state.getConfig("until"); err == nil {
|
||||||
|
untilspecs := strings.TrimSpace(res)
|
||||||
|
until, err := time.Parse(time.RFC3339, untilspecs)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Suspended: Unparseable until date: %s", untilspecs)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if until.Before(time.Now()) {
|
||||||
|
log.Print("Suspended: until time reached, suspending maintenance")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) getConfig(configName string) (string, error) {
|
||||||
|
res := state.database.QueryRow("SELECT value FROM config WHERE key = ?", configName)
|
||||||
|
|
||||||
|
var value string
|
||||||
|
|
||||||
|
err := res.Scan(&value)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) setConfig(configName string, value string) error {
|
||||||
|
_, err := state.database.Exec("REPLACE INTO config (key, value) VALUES (?, ?)", configName, value)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) getValidTeamIds() map[string]struct{} {
|
||||||
|
teams := make(map[string]struct{})
|
||||||
|
var team_id string
|
||||||
|
|
||||||
|
rows, _ := state.database.Query("SELECT team_hash FROM valid_team_ids")
|
||||||
|
for rows.Next() {
|
||||||
|
rows.Scan(&team_id)
|
||||||
|
teams[team_id] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return teams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *SQLiteMOTHState) login(teamName string, token string) (bool, error) {
|
||||||
|
if _, err := state.TeamName(token); err == nil{
|
||||||
|
return true, ErrAlreadyRegistered
|
||||||
|
}
|
||||||
|
|
||||||
|
row := state.database.QueryRow("SELECT count(*) FROM valid_team_ids WHERE team_hash = ?", token)
|
||||||
|
|
||||||
|
var count int
|
||||||
|
|
||||||
|
err := row.Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
return false, ErrRegistrationError
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return false, ErrInvalidTeamID
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := state.database.Exec("INSERT INTO teams (team_name, team_hash) VALUES (?, ?)", teamName, token); err != nil {
|
||||||
|
return false, ErrRegistrationError
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
|
@ -75,6 +75,10 @@ func main() {
|
||||||
lm_engine.StateDir = state_path
|
lm_engine.StateDir = state_path
|
||||||
lm_engine.maintenanceInterval = *maintenanceInterval
|
lm_engine.maintenanceInterval = *maintenanceInterval
|
||||||
state_engine = lm_engine
|
state_engine = lm_engine
|
||||||
|
} else if (state_engine_choice == "sqlite") {
|
||||||
|
lm_engine := &SQLiteMOTHState{}
|
||||||
|
lm_engine.StateDir = state_path
|
||||||
|
state_engine = lm_engine
|
||||||
} else {
|
} else {
|
||||||
log.Fatal("Unrecognized state engine '", state_engine_choice, "'")
|
log.Fatal("Unrecognized state engine '", state_engine_choice, "'")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue