commit bfe6fa8a5a54a594ea0dd3b00e4f4796c415042a Author: Jan-Lukas Else Date: Tue Apr 2 16:28:06 2019 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dddcc6c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +data/ diff --git a/config.go b/config.go new file mode 100644 index 0000000..3bd2c71 --- /dev/null +++ b/config.go @@ -0,0 +1,64 @@ +package main + +import ( + "os" + "strconv" +) + +type config struct { + port string + dnt bool + dbPath string + statsAuth bool + statsUsername string + statsPassword string +} + +var ( + appConfig = &config{} +) + +func init() { + appConfig.port = port() + appConfig.dnt = dnt() + appConfig.dbPath = dbPath() + appConfig.statsUsername = statsUsername() + appConfig.statsPassword = statsPassword() + appConfig.statsAuth = len(appConfig.statsUsername) > 0 && len(appConfig.statsPassword) > 0 +} + +func port() string { + port := os.Getenv("PORT") + if len(port) != 0 { + return port + } else { + return "8080" + } +} + +func dnt() bool { + dnt := os.Getenv("DNT") + dntBool, e := strconv.ParseBool(dnt) + if e != nil { + dntBool = true + } + return dntBool +} + +func dbPath() (dbPath string) { + dbPath = os.Getenv("DB_PATH") + if len(dbPath) == 0 { + dbPath = "data/kis3.db" + } + return +} + +func statsUsername() (username string) { + username = os.Getenv("STATS_USERNAME") + return +} + +func statsPassword() (password string) { + password = os.Getenv("STATS_PASSWORD") + return +} diff --git a/database.go b/database.go new file mode 100644 index 0000000..3f15206 --- /dev/null +++ b/database.go @@ -0,0 +1,210 @@ +package main + +import ( + "database/sql" + "fmt" + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/database/sqlite3" + _ "github.com/mattn/go-sqlite3" + "kis3.dev/kis3/helpers" + "os" + "path/filepath" + "strings" +) + +type Database struct { + sqlDB *sql.DB +} + +func initDatabase() (database *Database, e error) { + database = &Database{} + if _, err := os.Stat(appConfig.dbPath); os.IsNotExist(err) { + _ = os.MkdirAll(filepath.Dir(appConfig.dbPath), os.ModePerm) + } + database.sqlDB, e = sql.Open("sqlite3", appConfig.dbPath) + if e != nil { + return + } + e = migrateDatabase(database.sqlDB) + return +} + +func migrateDatabase(database *sql.DB) (e error) { + sourceDriver, e := (&helpers.PackrSource{}).Open("") + if e != nil { + return + } + databaseDriver, e := sqlite3.WithInstance(database, &sqlite3.Config{}) + if e != nil { + return + } + m, e := migrate.NewWithInstance("packr", sourceDriver, "kis3", databaseDriver) + if e != nil { + return + } + e = m.Up() + if e == migrate.ErrNoChange { + e = nil + } + return +} + +// Tracking + +func (db *Database) trackView(url string, ref string) { + if len(url) == 0 { + return + } + _, e := db.sqlDB.Exec("insert into views(url, ref) values(:url, :ref)", sql.Named("url", url), sql.Named("ref", ref)) + if e != nil { + fmt.Println("Inserting into DB failed:", e) + } +} + +// Requesting + +type View int + +const ( + PAGES View = iota + 1 + REFERRERS + HOURS + DAYS + WEEKS + MONTHS +) + +type ViewsRequest struct { + view View + from string + to string + url string + domain string + ref string +} + +type RequestResultRow struct { + First string `json:"first"` + Second int `json:"second"` +} + +func (db *Database) request(request *ViewsRequest) (resultRows []*RequestResultRow, e error) { + filterString, parameters := request.buildFilter() + // Fix to use array as varargs + namedArgs := make([]interface{}, len(parameters)) + for i, v := range parameters { + namedArgs[i] = v + } + // Query + statement := request.buildStatement(filterString) + rows, e := db.sqlDB.Query(statement, namedArgs...) + if e != nil { + return + } else { + resultRows = []*RequestResultRow{} + for rows.Next() { + var first string + var second int + e = rows.Scan(&first, &second) + if e != nil { + _ = rows.Close() + return + } + resultRows = append(resultRows, &RequestResultRow{ + First: first, + Second: second, + }) + } + return + } +} + +func (request *ViewsRequest) buildStatement(filters string) (statement string) { + if len(filters) > 0 { + filters = " where " + filters + " " + } else { + filters = " " + } + switch request.view { + case PAGES: + statement = "SELECT url as first, count(*) as second from views" + filters + "group by url;" + return + case REFERRERS: + statement = "SELECT ref as first, count(*) as second from views" + filters + "group by ref;" + return + case HOURS, DAYS, WEEKS, MONTHS: + format := "" + switch request.view { + case HOURS: + format = "%Y-%m-%d %H" + case DAYS: + format = "%Y-%m-%d" + case WEEKS: + format = "%Y-%W" + case MONTHS: + format = "%Y-%m" + } + statement = "SELECT strftime('" + format + "', time, 'localtime') as first, count(*) as second from views" + filters + "group by first;" + } + return +} + +// Request filters + +func (request *ViewsRequest) buildFilter() (filters string, parameters []sql.NamedArg) { + parameters = []sql.NamedArg{} + var allFilters []string + for _, filter := range []string{ + request.buildDateTimeFilter(¶meters), + request.buildUrlFilter(¶meters), + request.buildDomainFilter(¶meters), + request.buildRefFilter(¶meters), + } { + if len(filter) > 0 { + allFilters = append(allFilters, filter) + } + } + filters = strings.Join(allFilters, " and ") + return +} + +func (request *ViewsRequest) buildDateTimeFilter(namedArg *[]sql.NamedArg) (dateTimeFilter string) { + if len(request.from) > 0 && len(request.to) > 0 { + *namedArg = append(*namedArg, sql.Named("from", request.from)) + *namedArg = append(*namedArg, sql.Named("to", request.to)) + dateTimeFilter = "datetime(time, 'localtime') between :from and :to" + return + } else if len(request.from) > 0 { + *namedArg = append(*namedArg, sql.Named("from", request.from)) + dateTimeFilter = "datetime(time, 'localtime') >= :from" + return + } else if len(request.to) > 0 { + *namedArg = append(*namedArg, sql.Named("to", request.to)) + dateTimeFilter = "datetime(time, 'localtime') <= :to" + } + return +} + +func (request *ViewsRequest) buildUrlFilter(namedArg *[]sql.NamedArg) (urlFilter string) { + if len(request.url) > 0 { + *namedArg = append(*namedArg, sql.Named("url", request.url)) + urlFilter = "url = :url" + } + return +} + +func (request *ViewsRequest) buildDomainFilter(namedArg *[]sql.NamedArg) (domainFilter string) { + if len(request.domain) > 0 { + *namedArg = append(*namedArg, sql.Named("domain", request.domain+"%")) + domainFilter = "url like :domain" + } + return +} + +func (request *ViewsRequest) buildRefFilter(namedArg *[]sql.NamedArg) (refFilter string) { + if len(request.url) > 0 { + *namedArg = append(*namedArg, sql.Named("ref", request.ref)) + refFilter = "ref = :ref" + } + return +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c9c1d0d --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module kis3.dev/kis3 + +go 1.12 + +require ( + github.com/gobuffalo/packr/v2 v2.0.9 + github.com/golang-migrate/migrate/v4 v4.2.5 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/gorilla/mux v1.7.0 + github.com/mattn/go-sqlite3 v1.10.0 + github.com/wcharczuk/go-chart v2.0.1+incompatible + golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..11080ca --- /dev/null +++ b/go.sum @@ -0,0 +1,211 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.28.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +contrib.go.opencensus.io/exporter/stackdriver v0.6.0/go.mod h1:QeFzMJDAw8TXt5+aRaSuE8l5BwaMIOIlaVkBOPRuMuw= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +git.apache.org/thrift.git v0.0.0-20180924222215-a9235805469b/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/aws/aws-sdk-go v1.15.54/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/blend/go-sdk v2.0.0+incompatible h1:FL9X/of4ZYO5D2JJNI4vHrbXPfuSDbUa7h8JP9+E92w= +github.com/blend/go-sdk v2.0.0+incompatible/go.mod h1:3GUb0YsHFNTJ6hsJTpzdmCUl05o8HisKjx5OAlzYKdw= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= +github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= +github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= +github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4= +github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A= +github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE= +github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= +github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= +github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8= +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/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= +github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v0.7.3-0.20190108045446-77df18c24acf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/fsouza/fake-gcs-server v1.3.0/go.mod h1:Lq+43m2znsXfDKHnQMfdA0HpYYAEJsfizsbpk5k3TLo= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-ini/ini v1.39.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/envy v1.6.15 h1:OsV5vOpHYUpP7ZLS6sem1y40/lNX1BZj+ynMiRi21lQ= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9 h1:lGw35M+JVpedVCf08hSiBHKpp3FPTKqt6qRdXMSVAq0= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5 h1:f3Fpd5AqsFuTHUEhUeEMIFJkX8FpVnzdW+GpYxIyXkA= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1 h1:JRuTiZzDEZhBHkFiHTxJkYRT6CbYuL0K/rn+1byJoEA= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0 h1:P6naWPiHm/7R3eYx/ub3VhaW9G+1xAMJ6vzACePaGPI= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9 h1:g8P4tjthK0vAu2aopXlNNxCNsnGsWd+dbZLdc3fHSYk= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gocql/gocql v0.0.0-20181124151448-70385f88b28b/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang-migrate/migrate/v4 v4.2.5 h1:L41P/diej/v1ip3MPpbNPrUu2MHQmvho4DKchKAPkEQ= +github.com/golang-migrate/migrate/v4 v4.2.5/go.mod h1:zt+104di20bTWX9M3cCcERBkS/6mncciH5sAjcn6kBU= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= +github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/karrick/godirwalk v1.8.0 h1:ycpSqVon/QJJoaT1t8sae0tp1Stg21j+dyuS7OoagcA= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +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/kshvakov/clickhouse v1.3.4/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mongodb/mongo-go-driver v0.1.0/go.mod h1:NK/HWDIIZkaYsnYa0hmtP443T5ELr0KDecmIioVuuyU= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.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/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2 h1:J7U/N7eRtzjhs26d6GqMh2HBuXP8/Z64Densiiieafo= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/wcharczuk/go-chart v2.0.1+incompatible h1:0pz39ZAycJFF7ju/1mepnk26RLVLBCWz1STcD3doU0A= +github.com/wcharczuk/go-chart v2.0.1+incompatible/go.mod h1:PF5tmL4EIx/7Wf+hEkpCqYi5He4u90sw+0+6FhrryuE= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +go.opencensus.io v0.17.0/go.mod h1:mp1VrMQxhlqqDpKvH4UcQUa4YwlzNmymAjPrDdfxNpI= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f h1:FO4MZ3N56GnxbqxGKqh+YTzUWQ2sDwtFQEZgLOxh9Jc= +golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190108104531-7fbe1cd0fcc2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180924175601-e93be7f42f9f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190108222858-421f03a57a64/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe h1:tilCFoChsm7TcjFUH5YtBjno7wDleRO+6jICJg0WDS0= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20180921000521-920bb1beccf7/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181015145326-625cd1887957/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190108161440-ae2f86662275/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.39.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/helpers/basicAuth.go b/helpers/basicAuth.go new file mode 100644 index 0000000..2101ca0 --- /dev/null +++ b/helpers/basicAuth.go @@ -0,0 +1,14 @@ +package helpers + +import "net/http" + +func CheckAuth(w http.ResponseWriter, r *http.Request, username string, password string) (ok bool) { + w.Header().Set("WWW-Authenticate", `Basic realm="Authentication required"`) + rUsername, rPassword, rOk := r.BasicAuth() + if rOk && rUsername == username && rPassword == password { + return true + } else { + http.Error(w, "Not authorized", 401) + return false + } +} diff --git a/helpers/packrSource.go b/helpers/packrSource.go new file mode 100644 index 0000000..841314e --- /dev/null +++ b/helpers/packrSource.go @@ -0,0 +1,97 @@ +package helpers + +// This is a quick hack to get Packr working with golang-migrate + +import ( + "bytes" + "github.com/gobuffalo/packr/v2" + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/source" + "io" + "io/ioutil" + "os" + "sync" +) + +type PackrSource struct { + lock sync.Mutex + box *packr.Box + migrations *source.Migrations +} + +func (s *PackrSource) loadMigrations() (*source.Migrations, error) { + migrations := source.NewMigrations() + for _, filename := range s.box.List() { + migration, err := source.Parse(filename) + if err != nil { + return nil, err + } + migrations.Append(migration) + } + return migrations, nil +} + +func (s *PackrSource) Open(url string) (source.Driver, error) { + s.lock.Lock() + defer s.lock.Unlock() + s.box = packr.New("migrations", "../migrations") + if migrations, err := s.loadMigrations(); err != nil { + return nil, err + } else { + s.migrations = migrations + return s, nil + } +} + +func (s *PackrSource) Close() error { + s.lock.Lock() + defer s.lock.Unlock() + s.migrations = nil + return nil +} + +func (s *PackrSource) First() (version uint, err error) { + if v, ok := s.migrations.First(); !ok { + return 0, os.ErrNotExist + } else { + return v, nil + } +} + +func (s *PackrSource) Prev(version uint) (prevVersion uint, err error) { + if v, ok := s.migrations.Prev(version); !ok { + return 0, os.ErrNotExist + } else { + return v, nil + } +} + +func (s *PackrSource) Next(version uint) (nextVersion uint, err error) { + if v, ok := s.migrations.Next(version); !ok { + return 0, os.ErrNotExist + } else { + return v, nil + } +} + +func (s *PackrSource) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) { + if migration, ok := s.migrations.Up(version); !ok { + return nil, "", os.ErrNotExist + } else { + b, _ := s.box.Find(migration.Raw) + return ioutil.NopCloser(bytes.NewBuffer(b)), + migration.Identifier, + nil + } +} + +func (s *PackrSource) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) { + if migration, ok := s.migrations.Down(version); !ok { + return nil, "", migrate.ErrNilVersion + } else { + b := s.box.Bytes(migration.Raw) + return ioutil.NopCloser(bytes.NewBuffer(b)), + migration.Identifier, + nil + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..0883613 --- /dev/null +++ b/main.go @@ -0,0 +1,186 @@ +package main + +import ( + "encoding/json" + "fmt" + "github.com/gobuffalo/packr/v2" + "github.com/gorilla/mux" + "github.com/wcharczuk/go-chart" + "github.com/wcharczuk/go-chart/drawing" + "kis3.dev/kis3/helpers" + "log" + "math" + "net/http" + "strconv" +) + +type kis3 struct { + db *Database + router *mux.Router + fs http.Handler +} + +var ( + app = &kis3{} +) + +func init() { + e := app.setupDB() + if e != nil { + log.Fatal("Database setup failed:", e) + } + app.setupRouter() +} + +func main() { + app.startListening() +} + +func (kis3 *kis3) setupDB() (e error) { + kis3.db, e = initDatabase() + return +} + +func (kis3 *kis3) setupRouter() { + kis3.router = mux.NewRouter() + kis3.router.HandleFunc("/view", kis3.trackView) + kis3.router.HandleFunc("/stats", kis3.requestStats) + kis3.router.PathPrefix("/").Handler(http.HandlerFunc(kis3.serveStaticFile)) +} + +func (kis3 kis3) startListening() { + port := appConfig.port + addr := ":" + port + fmt.Printf("Listening to %s\n", addr) + log.Fatal(http.ListenAndServe(addr, kis3.router)) +} + +func (kis3 kis3) trackView(w http.ResponseWriter, r *http.Request) { + url := r.URL.Query().Get("url") + ref := r.URL.Query().Get("ref") + if r.Header.Get("DNT") == "1" && appConfig.dnt { + fmt.Println("Not tracking because of DNT") + } else { + fmt.Printf("Tracking %s with referrer %s\n", url, ref) + go kis3.db.trackView(url, ref) // run with goroutine for awesome speed! + _, _ = fmt.Fprint(w, "true") + } +} + +func (kis3 kis3) serveStaticFile(w http.ResponseWriter, r *http.Request) { + if kis3.fs == nil { + kis3.fs = http.FileServer(packr.New("staticFiles", "./static")) + } + // Fix, because file server isn't serving index.html otherwise + if r.URL.Path == "/" || r.URL.Path == "/index.html" { + r.URL.Path = "/default.html" + } + kis3.fs.ServeHTTP(w, r) +} + +func (kis3 kis3) requestStats(w http.ResponseWriter, r *http.Request) { + // Require authentication + if appConfig.statsAuth { + if !helpers.CheckAuth(w, r, appConfig.statsUsername, appConfig.statsPassword) { + return + } + } + // Do request + queries := r.URL.Query() + view := PAGES + switch queries.Get("view") { + case "pages": + view = PAGES + case "referrers": + view = REFERRERS + case "hours": + view = HOURS + case "days": + view = DAYS + case "weeks": + view = WEEKS + case "months": + view = MONTHS + } + result, e := kis3.db.request(&ViewsRequest{ + view: view, + from: queries.Get("from"), + to: queries.Get("to"), + url: queries.Get("url"), + domain: queries.Get("domain"), + ref: queries.Get("ref"), + }) + if e != nil { + fmt.Println("Database request failed:", e) + w.WriteHeader(500) + } else if result != nil { + switch queries.Get("format") { + case "json": + sendJsonResponse(result, w) + return + case "chart": + sendChartResponse(result, w) + return + default: // "plain" + sendPlainResponse(result, w) + return + } + } +} + +func sendPlainResponse(result []*RequestResultRow, w http.ResponseWriter) { + w.Header().Add("Content-Type", "text/plain") + for _, row := range result { + _, _ = fmt.Fprintln(w, (*row).First+": "+strconv.Itoa((*row).Second)) + } +} + +func sendJsonResponse(result []*RequestResultRow, w http.ResponseWriter) { + w.Header().Add("Content-Type", "application/json") + jsonBytes, _ := json.Marshal(result) + _, _ = fmt.Fprintln(w, string(jsonBytes)) +} + +func sendChartResponse(result []*RequestResultRow, w http.ResponseWriter) { + var values []chart.Value + max := float64(1) + for _, row := range result { + values = append(values, chart.Value{Label: row.First, Value: float64(row.Second), Style: chart.Style{ + FillColor: drawing.ColorBlue, + StrokeColor: drawing.ColorBlue, + }}) + max = math.Max(max, float64(row.Second)) + } + chartRange := &chart.ContinuousRange{ + Min: float64(0), + Max: max, + } + chartWidth := len(values)*30 + 100 + barChart := chart.BarChart{ + Title: "Stats", + Height: 500, + Width: chartWidth, + TitleStyle: chart.StyleShow(), + Background: chart.Style{ + Padding: chart.Box{ + Top: 40, + }, + }, + BarWidth: 20, + BarSpacing: 10, + XAxis: chart.Style{ + Show: true, + TextRotationDegrees: 90.0, + }, + YAxis: chart.YAxis{ + Style: chart.StyleShow(), + Range: chartRange, + }, + Bars: values, + } + w.Header().Set("Content-Type", chart.ContentTypeSVG) + e := barChart.Render(chart.SVG, w) + if e != nil { + sendPlainResponse(result, w) // Fallback to plain + } +} diff --git a/migrations/1_Init_Database.down.sql b/migrations/1_Init_Database.down.sql new file mode 100644 index 0000000..7ea0e39 --- /dev/null +++ b/migrations/1_Init_Database.down.sql @@ -0,0 +1 @@ +DROP TABLE `views`; \ No newline at end of file diff --git a/migrations/1_Init_Database.up.sql b/migrations/1_Init_Database.up.sql new file mode 100644 index 0000000..7c539e9 --- /dev/null +++ b/migrations/1_Init_Database.up.sql @@ -0,0 +1,6 @@ +CREATE TABLE `views` +( + `url` TEXT NOT NULL, + `time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + `ref` TEXT DEFAULT '' NOT NULL +); \ No newline at end of file diff --git a/static/default.html b/static/default.html new file mode 100644 index 0000000..93cc5c4 --- /dev/null +++ b/static/default.html @@ -0,0 +1,11 @@ + + + + + KISSS + + +Welcome to KISSS + + + diff --git a/static/generator.html b/static/generator.html new file mode 100644 index 0000000..b9325bc --- /dev/null +++ b/static/generator.html @@ -0,0 +1,59 @@ + + + + + KISSS + + +

Stats-URL Generator

+
+
+
+ +
+ +
+ +
+ + + diff --git a/static/kis3.js b/static/kis3.js new file mode 100644 index 0000000..6f6841d --- /dev/null +++ b/static/kis3.js @@ -0,0 +1,17 @@ +(function () { + var request = new XMLHttpRequest(); + var url = document.currentScript.baseURI + + "view?url=" + window.decodeURI(document.documentURI); + if (document.referrer && document.referrer.length > 0) { + url += window.decodeURI(document.referrer); + } + request.onload = function () { + if (request.status >= 200 && request.status < 300) { + console.log('Success with tracking page view!'); + } else { + console.log('The tracking request failed!'); + } + }; + request.open("POST", url); + request.send(); +})();