Initial commit
the build was successful
Details
the build was successful
Details
This commit is contained in:
commit
55d521db48
|
@ -0,0 +1,46 @@
|
|||
workspace:
|
||||
base: /go
|
||||
path: src/gitea.meow.tf/ow-api/ow-api
|
||||
|
||||
pipeline:
|
||||
dependencies:
|
||||
image: golang:latest
|
||||
commands:
|
||||
- mkdir /go/bin
|
||||
- curl https://glide.sh/get | sh
|
||||
- glide install
|
||||
build-i386:
|
||||
image: golang:latest
|
||||
group: build
|
||||
commands:
|
||||
- mkdir -p build/i386
|
||||
- GOOS=linux GOARCH=386 go build -o build/i386/owapi
|
||||
build-amd64:
|
||||
image: golang:latest
|
||||
group: build
|
||||
commands:
|
||||
- mkdir -p build/amd64
|
||||
- go build -o build/amd64/owapi
|
||||
build-armv7:
|
||||
image: golang:latest
|
||||
group: build
|
||||
commands:
|
||||
- mkdir -p build/armv7
|
||||
- GOOS=linux GOARCH=arm GOARM=7 go build -o build/armv7/owapi
|
||||
build-arm64:
|
||||
image: golang:latest
|
||||
group: build
|
||||
commands:
|
||||
- mkdir -p build/arm64
|
||||
- GOOS=linux GOARCH=arm64 go build -o build/arm64/owapi
|
||||
package:
|
||||
image: tystuyfzand/fpm
|
||||
commands:
|
||||
- export VERSION=`grep "Version" main.go | head -n 1 | awk '{print $3}' | sed -e 's/^"//' -e 's/"$//' | tr -d '\n'`
|
||||
- chmod +x packaging/build-package.sh packaging/package-upload.sh
|
||||
- ARCH=i386 packaging/build-package.sh
|
||||
- ARCH=amd64 packaging/build-package.sh
|
||||
- ARCH=armv7 packaging/build-package.sh
|
||||
- ARCH=arm64 packaging/build-package.sh
|
||||
- packaging/package-upload.sh
|
||||
secrets: [ upload_url ]
|
|
@ -0,0 +1 @@
|
|||
vendor/
|
|
@ -0,0 +1,37 @@
|
|||
hash: bb44c8a1544b360d30dd09101d029e73c355e26b621a885bb6029e35b0eb834b
|
||||
updated: 2018-10-23T08:14:21.9542093-04:00
|
||||
imports:
|
||||
- name: github.com/andybalholm/cascadia
|
||||
version: 901648c87902174f774fac311d7f176f8647bdaa
|
||||
- name: github.com/evanphx/json-patch
|
||||
version: 72bf35d0ff611848c1dc9df0f976c81192392fa5
|
||||
- name: github.com/go-redis/redis
|
||||
version: f3bba01df2026fc865f7782948845db9cf44cf23
|
||||
subpackages:
|
||||
- internal
|
||||
- internal/consistenthash
|
||||
- internal/hashtag
|
||||
- internal/pool
|
||||
- internal/proto
|
||||
- internal/singleflight
|
||||
- internal/util
|
||||
- name: github.com/jinzhu/inflection
|
||||
version: 04140366298a54a039076d798123ffa108fff46c
|
||||
- name: github.com/julienschmidt/httprouter
|
||||
version: 348b672cd90d8190f8240323e372ecd1e66b59dc
|
||||
- name: github.com/PuerkitoBio/goquery
|
||||
version: ea1bc64a63088ac2bbf1a2edcae38413794a7710
|
||||
- name: github.com/rs/cors
|
||||
version: 9a47f48565a795472d43519dd49aac781f3034fb
|
||||
- name: github.com/starboy/httpclient
|
||||
version: d6d6e42b608037f8e4c37fd3a55d6819c44a99f1
|
||||
- name: github.com/starboy/ovrstat
|
||||
version: dd5d178c511fc652deeaddbfbe2e7ddfb32ae188
|
||||
subpackages:
|
||||
- ovrstat
|
||||
- name: golang.org/x/net
|
||||
version: 92b859f39abd2d91a854c9f9c4621b2f5054a92d
|
||||
subpackages:
|
||||
- html
|
||||
- html/atom
|
||||
testImports: []
|
|
@ -0,0 +1,13 @@
|
|||
package: gitea.meow.tf/ow-api/ow-api
|
||||
import:
|
||||
- package: github.com/evanphx/json-patch
|
||||
version: ^4.1.0
|
||||
- package: github.com/go-redis/redis
|
||||
version: ^6.14.1
|
||||
- package: github.com/julienschmidt/httprouter
|
||||
version: ^1.2.0
|
||||
- package: github.com/rs/cors
|
||||
version: ^1.6.0
|
||||
- package: github.com/starboy/ovrstat
|
||||
subpackages:
|
||||
- ovrstat
|
|
@ -0,0 +1,406 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"github.com/evanphx/json-patch"
|
||||
"github.com/go-redis/redis"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/rs/cors"
|
||||
"github.com/starboy/ovrstat/ovrstat"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
Version = "2.0.0"
|
||||
|
||||
OpAdd = "add"
|
||||
OpRemove = "remove"
|
||||
)
|
||||
|
||||
type patchOperation struct {
|
||||
Op string `json:"op"`
|
||||
Path string `json:"path"`
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
type gamesStats struct {
|
||||
Played int64 `json:"played"`
|
||||
Won int64 `json:"won"`
|
||||
}
|
||||
|
||||
type awardsStats struct {
|
||||
Cards int64 `json:"cards"`
|
||||
Medals int64 `json:"medals"`
|
||||
Bronze int64 `json:"medalsBronze"`
|
||||
Silver int64 `json:"medalsSilver"`
|
||||
Gold int64 `json:"medalsGold"`
|
||||
}
|
||||
|
||||
var (
|
||||
flagBind = flag.String("bind-address", ":8080", "Address to bind to for http requests")
|
||||
|
||||
client *redis.Client
|
||||
|
||||
profilePatch *jsonpatch.Patch
|
||||
|
||||
heroNames []string
|
||||
)
|
||||
|
||||
func main() {
|
||||
loadHeroNames()
|
||||
|
||||
client = redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default DB
|
||||
})
|
||||
|
||||
var err error
|
||||
|
||||
ops := []patchOperation{
|
||||
{Op: OpRemove, Path: "/quickPlayStats/topHeroes"},
|
||||
{Op: OpRemove, Path: "/competitiveStats/topHeroes"},
|
||||
{Op: OpRemove, Path: "/quickPlayStats/careerStats"},
|
||||
{Op: OpRemove, Path: "/competitiveStats/careerStats"},
|
||||
}
|
||||
|
||||
profilePatch, err = patchFromOperations(ops)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
router := httprouter.New()
|
||||
|
||||
// PC
|
||||
router.GET("/v1/stats/pc/:region/:tag/heroes/:heroes", heroes)
|
||||
router.GET("/v1/stats/pc/:region/:tag/profile", profile)
|
||||
router.GET("/v1/stats/pc/:region/:tag/complete", stats)
|
||||
|
||||
// Console
|
||||
router.GET("/v1/stats/:platform/:tag/heroes/:heroes", heroes)
|
||||
router.GET("/v1/stats/:platform/:tag/profile", profile)
|
||||
router.GET("/v1/stats/:platform/:tag/complete", stats)
|
||||
|
||||
// Version
|
||||
router.GET("/v1/version", VersionHandler)
|
||||
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
})
|
||||
|
||||
router.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "OPTIONS" {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
})
|
||||
|
||||
log.Fatal(http.ListenAndServe(*flagBind, c.Handler(router)))
|
||||
}
|
||||
|
||||
func loadHeroNames() {
|
||||
stats, err := ovrstat.PCStats("na", "cats-11481")
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m := make(map[string]bool)
|
||||
|
||||
for k := range stats.QuickPlayStats.TopHeroes {
|
||||
m[k] = true
|
||||
}
|
||||
|
||||
for k := range stats.QuickPlayStats.CareerStats {
|
||||
m[k] = true
|
||||
}
|
||||
|
||||
heroNames = make([]string, 0)
|
||||
|
||||
for k := range m {
|
||||
heroNames = append(heroNames, k)
|
||||
}
|
||||
}
|
||||
|
||||
func statsResponse(w http.ResponseWriter, ps httprouter.Params, patch *jsonpatch.Patch) ([]byte, error) {
|
||||
var stats *ovrstat.PlayerStats
|
||||
var err error
|
||||
|
||||
tag := ps.ByName("tag")
|
||||
|
||||
tag = strings.Replace(tag, "#", "-", -1)
|
||||
|
||||
cacheKey := generateCacheKey(ps)
|
||||
|
||||
if region := ps.ByName("region"); region != "" {
|
||||
stats, err = ovrstat.PCStats(region, tag)
|
||||
} else if platform := ps.ByName("platform"); platform != "" {
|
||||
stats, err = ovrstat.ConsoleStats(platform, tag)
|
||||
} else {
|
||||
return nil, errors.New("unknown region/platform")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Caching of full response for modification
|
||||
|
||||
res, err := client.Get(cacheKey).Bytes()
|
||||
|
||||
if res != nil && err == nil {
|
||||
if patch != nil {
|
||||
res, err = patch.Apply(res)
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
extra := make([]patchOperation, 0)
|
||||
|
||||
if hs, ok := stats.QuickPlayStats.CareerStats["allHeroes"]; ok {
|
||||
games := &gamesStats{}
|
||||
|
||||
games.Played = valueOrDefault(hs.Game, "gamesPlayed", 0)
|
||||
games.Won = valueOrDefault(hs.Game, "gamesWon", 0)
|
||||
|
||||
awards := &awardsStats{}
|
||||
|
||||
awards.Cards = valueOrDefault(hs.MatchAwards, "cards", 0)
|
||||
awards.Medals = valueOrDefault(hs.MatchAwards, "medals", 0)
|
||||
awards.Bronze = valueOrDefault(hs.MatchAwards, "medalsBronze", 0)
|
||||
awards.Silver = valueOrDefault(hs.MatchAwards, "medalsSilver", 0)
|
||||
awards.Gold = valueOrDefault(hs.MatchAwards, "medalsGold", 0)
|
||||
|
||||
extra = append(extra, patchOperation{
|
||||
Op: OpAdd,
|
||||
Path: "/quickPlayStats/games",
|
||||
Value: games,
|
||||
}, patchOperation{
|
||||
Op: OpAdd,
|
||||
Path: "/quickPlayStats/awards",
|
||||
Value: awards,
|
||||
})
|
||||
}
|
||||
|
||||
if hs, ok := stats.CompetitiveStats.CareerStats["allHeroes"]; ok {
|
||||
games := &gamesStats{}
|
||||
|
||||
games.Played = valueOrDefault(hs.Game, "gamesPlayed", 0)
|
||||
games.Won = valueOrDefault(hs.Game, "gamesWon", 0)
|
||||
|
||||
awards := &awardsStats{}
|
||||
|
||||
awards.Cards = valueOrDefault(hs.MatchAwards, "cards", 0)
|
||||
awards.Medals = valueOrDefault(hs.MatchAwards, "medals", 0)
|
||||
awards.Bronze = valueOrDefault(hs.MatchAwards, "medalsBronze", 0)
|
||||
awards.Silver = valueOrDefault(hs.MatchAwards, "medalsSilver", 0)
|
||||
awards.Gold = valueOrDefault(hs.MatchAwards, "medalsGold", 0)
|
||||
|
||||
extra = append(extra, patchOperation{
|
||||
Op: OpAdd,
|
||||
Path: "/competitiveStats/games",
|
||||
Value: games,
|
||||
}, patchOperation{
|
||||
Op: OpAdd,
|
||||
Path: "/competitiveStats/awards",
|
||||
Value: awards,
|
||||
})
|
||||
}
|
||||
|
||||
b, err := json.Marshal(stats)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(extra) > 0 {
|
||||
extraPatch, err := patchFromOperations(extra)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err = extraPatch.Apply(b)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Cache response
|
||||
client.Set(cacheKey, b, 10 * time.Minute)
|
||||
|
||||
if patch != nil {
|
||||
// Apply filter patch
|
||||
b, err = patch.Apply(b)
|
||||
}
|
||||
|
||||
return b, err
|
||||
}
|
||||
|
||||
func stats(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
data, err := statsResponse(w, ps, nil)
|
||||
|
||||
if err != nil {
|
||||
writeError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func profile(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
cacheKey := generateCacheKey(ps) + "-profile"
|
||||
|
||||
// Check cache for -profile to prevent jsonpatch calls
|
||||
res, err := client.Get(cacheKey).Bytes()
|
||||
|
||||
if res != nil && err == nil {
|
||||
w.Write(res)
|
||||
return
|
||||
}
|
||||
|
||||
// Cache result for profile specifically
|
||||
data, err := statsResponse(w, ps, profilePatch)
|
||||
|
||||
if err != nil {
|
||||
writeError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
client.Set(cacheKey, data, 10 * time.Minute)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func heroes(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
names := strings.Split(ps.ByName("heroes"), ",")
|
||||
|
||||
if len(names) == 0 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
writeError(w, "Name list must contain at least one hero")
|
||||
return
|
||||
}
|
||||
|
||||
nameMap := make(map[string]bool)
|
||||
|
||||
for _, name := range names {
|
||||
nameMap[name] = true
|
||||
}
|
||||
|
||||
ops := make([]patchOperation, 0)
|
||||
|
||||
for _, heroName := range heroNames {
|
||||
if _, exists := nameMap[heroName]; !exists {
|
||||
ops = append(ops, patchOperation{
|
||||
Op: OpRemove,
|
||||
Path: "/quickPlayStats/topHeroes/" + heroName,
|
||||
}, patchOperation{
|
||||
Op: OpRemove,
|
||||
Path: "/quickPlayStats/careerStats/" + heroName,
|
||||
}, patchOperation{
|
||||
Op: OpRemove,
|
||||
Path: "/competitiveStats/topHeroes/" + heroName,
|
||||
}, patchOperation{
|
||||
Op: OpRemove,
|
||||
Path: "/competitiveStats/careerStats/" + heroName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
patch, err := patchFromOperations(ops)
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
writeError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Create a patch to remove all but specified heroes
|
||||
data, err := statsResponse(w, ps, patch)
|
||||
|
||||
if err != nil {
|
||||
writeError(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func valueOrDefault(m map[string]interface{}, key string, d int64) int64 {
|
||||
if v, ok := m[key]; ok {
|
||||
switch v.(type) {
|
||||
case int64:
|
||||
return v.(int64)
|
||||
case int:
|
||||
return int64(v.(int))
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func patchFromOperations(ops []patchOperation) (*jsonpatch.Patch, error) {
|
||||
patchBytes, err := json.Marshal(ops)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patch, err := jsonpatch.DecodePatch(patchBytes)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &patch, nil
|
||||
}
|
||||
|
||||
type versionObject struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
func VersionHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if err := json.NewEncoder(w).Encode(&versionObject{Version: Version}); err != nil {
|
||||
writeError(w, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type errorObject struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func writeError(w http.ResponseWriter, err string) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if err := json.NewEncoder(w).Encode(&errorObject{Error: err}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func generateCacheKey(ps httprouter.Params) string {
|
||||
var cacheKey string
|
||||
|
||||
tag := ps.ByName("tag")
|
||||
|
||||
if region := ps.ByName("region"); region != "" {
|
||||
cacheKey = "pc-" + region + "-" + tag
|
||||
} else if platform := ps.ByName("platform"); platform != "" {
|
||||
cacheKey = platform + "-" + tag
|
||||
}
|
||||
|
||||
return cacheKey
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
fpm -s dir -t deb -p build/$ARCH/owapi_$VERSION.deb \
|
||||
-n ow-api -v $VERSION \
|
||||
--deb-priority optional --force \
|
||||
--deb-compression bzip2 \
|
||||
--description "Overwatch API Server" \
|
||||
-m "Tyler Stuyfzand <admin@meow.tf>" --vendor "Meow.tf" \
|
||||
--before-install packaging/scripts/preinst.deb \
|
||||
--after-install packaging/scripts/postinst.deb \
|
||||
-a $ARCH build/$ARCH/owapi=/usr/bin/owapi \
|
||||
packaging/owapi.service=/lib/systemd/system/owapi.service
|
|
@ -0,0 +1,11 @@
|
|||
[Unit]
|
||||
Description=Overwatch API Server
|
||||
|
||||
[Service]
|
||||
User=owapi
|
||||
Group=owapi
|
||||
Restart=on-failure
|
||||
ExecStart=/usr/bin/owapi
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1 @@
|
|||
curl -X POST "$UPLOAD_URL" -F "file_i386=@build/i386/owapi_$VERSION.deb" -F "file_amd64=@build/amd64/owapi_$VERSION.deb" -F "file_armv7=@build/armv7/owapi_$VERSION.deb"
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$1" != "configure" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Touch and set permisions on default log files on installation
|
||||
|
||||
if [ -x "/etc/init.d/owapi" ]; then
|
||||
update-rc.d owapi defaults >/dev/null
|
||||
if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
|
||||
invoke-rc.d owapi start || true
|
||||
else
|
||||
/etc/init.d/owapi start || true
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,37 @@
|
|||
#! /bin/sh
|
||||
# preinst script for astra
|
||||
set -e
|
||||
addowapiuser() {
|
||||
if ! getent passwd owapi > /dev/null; then
|
||||
adduser --quiet --system --group \
|
||||
--disabled-login \
|
||||
--home /var/lib/owapi \
|
||||
--no-create-home \
|
||||
--shell /bin/false \
|
||||
owapi
|
||||
fi
|
||||
}
|
||||
|
||||
createdirectories() {
|
||||
mkdir -p /var/lib/owapi || true
|
||||
chown -R owapi:owapi /var/lib/owapi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
install)
|
||||
addowapiuser
|
||||
createdirectories
|
||||
;;
|
||||
upgrade)
|
||||
addowapiuser
|
||||
createdirectories
|
||||
;;
|
||||
abort-upgrade)
|
||||
;;
|
||||
*)
|
||||
echo "preinst called with unknown argument \`$1'" >&2
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue