mirror of
https://github.com/pagefaultgames/rogueserver.git
synced 2025-04-02 02:57:15 +08:00
Merge branch 'master' into test-deployment
This commit is contained in:
commit
2da5ccaa33
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@ -0,0 +1,10 @@
|
||||
/.github/
|
||||
|
||||
Dockerfile*
|
||||
docker-compose*.yml
|
||||
|
||||
/.data/
|
||||
/secret.key
|
||||
|
||||
/rogueserver*
|
||||
!/rogueserver.go
|
52
.github/workflows/ci.yml
vendored
Normal file
52
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build (${{ matrix.os_name }})
|
||||
env:
|
||||
GO_VERSION: 1.22
|
||||
GOOS: ${{ matrix.os_name }}
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
os_name: linux
|
||||
arch: amd64
|
||||
- os: windows-latest
|
||||
os_name: windows
|
||||
arch: amd64
|
||||
# TODO macos needs universal binary!
|
||||
# - os: macos-latest
|
||||
# os_name: macos
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Install dependencies
|
||||
run: go mod download
|
||||
- name: Lint Codebase
|
||||
continue-on-error: true
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
||||
args: --config .golangci.yml
|
||||
- name: Test
|
||||
run: go test -v
|
||||
- name: Build
|
||||
run: go build -v
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rogueserver-${{ matrix.os_name }}-${{ matrix.arch }}-${{ github.sha }}
|
||||
path: |
|
||||
rogueserver*
|
||||
!rogueserver.go
|
38
.github/workflows/ghcr.yml
vendored
Normal file
38
.github/workflows/ghcr.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: Publish to GHCR
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and publish to GHCR
|
||||
if: github.repository == 'pagefaultgames/rogueserver'
|
||||
env:
|
||||
GO_VERSION: 1.22
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Docker BuildX
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Log into container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ github.token }}
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
GO_VERSION=${{ env.GO_VERSION }}
|
||||
VERSION=${{ github.ref_name }}-SNAPSHOT
|
||||
COMMIT_SHA=${{ env.GITHUB_SHA_SHORT }}
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,13 +5,12 @@ rogueserver*
|
||||
/userdata/*
|
||||
secret.key
|
||||
|
||||
|
||||
# local testing
|
||||
/.data/
|
||||
|
||||
|
||||
# Jetbrains IDEs
|
||||
/.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.vscode/launch.json
|
||||
|
8
.golangci.yml
Normal file
8
.golangci.yml
Normal file
@ -0,0 +1,8 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
severity:
|
||||
default-severity: error
|
||||
rules:
|
||||
- linters:
|
||||
- unused
|
||||
severity: info
|
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@ -0,0 +1,29 @@
|
||||
ARG GO_VERSION=1.22
|
||||
|
||||
FROM golang:${GO_VERSION} AS builder
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY ./go.mod /src/
|
||||
COPY ./go.sum /src/
|
||||
|
||||
RUN go mod download && go mod verify
|
||||
|
||||
COPY . /src/
|
||||
|
||||
RUN CGO_ENABLED=0 \
|
||||
go build -o rogueserver
|
||||
|
||||
RUN chmod +x /src/rogueserver
|
||||
|
||||
# ---------------------------------------------
|
||||
|
||||
FROM scratch
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /src/rogueserver .
|
||||
|
||||
EXPOSE 8001
|
||||
|
||||
ENTRYPOINT ["./rogueserver"]
|
84
README.md
84
README.md
@ -1 +1,83 @@
|
||||
# rogueserver
|
||||
# rogueserver
|
||||
|
||||
# Hosting in Docker
|
||||
It is advised that you host this in a docker container as it will be much easier to manage.
|
||||
There is a sample docker-compose file for setting up a docker container to setup this server.
|
||||
|
||||
# Self Hosting outside of Docker:
|
||||
## Required Tools:
|
||||
- Golang
|
||||
- Node: **18.3.0**
|
||||
- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
|
||||
|
||||
## Installation:
|
||||
The docker compose file should automatically implement a container with mariadb with an empty database and the default user and password combo of pokerogue:pokerogue
|
||||
|
||||
### src/utils.ts:224-225 (in pokerogue)
|
||||
Replace both URLs (one on each line) with the local API server address from rogueserver.go (0.0.0.0:8001) (or whatever port you picked)
|
||||
|
||||
# If you are on Windows
|
||||
|
||||
Now that all of the files are configured: start up powershell as administrator:
|
||||
```
|
||||
cd C:\api\server\location\
|
||||
go build .
|
||||
.\rogueserver.exe --debug --dbuser yourusername --dbpass yourpassword
|
||||
```
|
||||
The other available flags are located in rogueserver.go:34-43.
|
||||
|
||||
Then in another run this the first time then run `npm run start` from the rogueserver location from then on:
|
||||
```
|
||||
powershell -ep bypass
|
||||
cd C:\server\location\
|
||||
npm install
|
||||
npm run start
|
||||
```
|
||||
You will need to allow the port youre running the API (8001) on and port 8000 to accept inbound connections through the [Windows Advanced Firewall](https://www.youtube.com/watch?v=9llH5_CON-Y).
|
||||
|
||||
# If you are on Linux
|
||||
In whatever shell you prefer, run the following:
|
||||
```
|
||||
cd /api/server/location/
|
||||
go build .
|
||||
./rogueserver --debug --dbuser yourusername --dbpass yourpassword &
|
||||
|
||||
cd /server/location/
|
||||
npm run start
|
||||
```
|
||||
|
||||
If you have a firewall running such as ufw on your linux machine, make sure to allow inbound connections on the ports youre running the API and the pokerogue server (8000,8001).
|
||||
An example to allow incoming connections using UFW:
|
||||
```
|
||||
sudo ufw allow 8000,8001/tcp
|
||||
```
|
||||
|
||||
This should allow you to reach the game from other computers on the same network.
|
||||
|
||||
## Tying to a Domain
|
||||
|
||||
If you want to tie it to a domain like I did and make it publicly accessible, there is some extra work to be done.
|
||||
|
||||
I setup caddy and would recommend using it as a reverse proxy.
|
||||
[caddy installation](https://caddyserver.com/docs/install)
|
||||
once its installed setup a config file for caddy:
|
||||
|
||||
```
|
||||
pokerogue.exampledomain.com {
|
||||
reverse_proxy localhost:8000
|
||||
}
|
||||
pokeapi.exampledomain.com {
|
||||
reverse_proxy localhost:8001
|
||||
}
|
||||
```
|
||||
Preferably set up caddy as a service from [here.](https://caddyserver.com/docs/running)
|
||||
|
||||
Once this is good to go, take your API url (https://pokeapi.exampledomain.com) and paste it on
|
||||
### src/utils.ts:224-225
|
||||
in place of the previous 0.0.0.0:8001 address
|
||||
|
||||
Make sure that both 8000 and 8001 are portforwarded on your router.
|
||||
|
||||
Test that the server's game and game authentication works from other machines both in and outside of the network. Once this is complete, enjoy!
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ package account
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
@ -34,13 +35,13 @@ const (
|
||||
ArgonKeySize = 32
|
||||
ArgonSaltSize = 16
|
||||
|
||||
ArgonMaxInstances = 16
|
||||
|
||||
UUIDSize = 16
|
||||
TokenSize = 32
|
||||
)
|
||||
|
||||
var (
|
||||
ArgonMaxInstances = runtime.NumCPU()
|
||||
|
||||
isValidUsername = regexp.MustCompile(`^\w{1,16}$`).MatchString
|
||||
semaphore = make(chan bool, ArgonMaxInstances)
|
||||
)
|
||||
|
@ -19,6 +19,7 @@ package account
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pagefaultgames/rogueserver/db"
|
||||
@ -28,7 +29,7 @@ import (
|
||||
func Logout(token []byte) error {
|
||||
err := db.RemoveSessionFromToken(token)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return fmt.Errorf("token not found")
|
||||
}
|
||||
|
||||
|
@ -19,18 +19,22 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/pagefaultgames/rogueserver/api/account"
|
||||
"github.com/pagefaultgames/rogueserver/api/daily"
|
||||
"github.com/pagefaultgames/rogueserver/db"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Init(mux *http.ServeMux) {
|
||||
scheduleStatRefresh()
|
||||
daily.Init()
|
||||
func Init(mux *http.ServeMux) error {
|
||||
if err := scheduleStatRefresh(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := daily.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// account
|
||||
mux.HandleFunc("GET /account/info", handleAccountInfo)
|
||||
@ -40,20 +44,27 @@ func Init(mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /account/logout", handleAccountLogout)
|
||||
|
||||
// game
|
||||
mux.HandleFunc("GET /game/playercount", handleGamePlayerCount)
|
||||
mux.HandleFunc("GET /game/titlestats", handleGameTitleStats)
|
||||
mux.HandleFunc("GET /game/classicsessioncount", handleGameClassicSessionCount)
|
||||
|
||||
// savedata
|
||||
mux.HandleFunc("GET /savedata/get", handleSaveData)
|
||||
mux.HandleFunc("POST /savedata/update", handleSaveData)
|
||||
mux.HandleFunc("GET /savedata/delete", handleSaveData)
|
||||
mux.HandleFunc("POST /savedata/clear", handleSaveData)
|
||||
mux.HandleFunc("GET /savedata/get", legacyHandleGetSaveData)
|
||||
mux.HandleFunc("POST /savedata/update", legacyHandleSaveData)
|
||||
mux.HandleFunc("GET /savedata/delete", legacyHandleSaveData) // TODO use deleteSystemSave
|
||||
mux.HandleFunc("POST /savedata/clear", legacyHandleSaveData) // TODO use clearSessionData
|
||||
mux.HandleFunc("GET /savedata/newclear", legacyHandleNewClear)
|
||||
|
||||
// new session
|
||||
mux.HandleFunc("POST /savedata/updateall", handleUpdateAll)
|
||||
mux.HandleFunc("POST /savedata/verify", handleSessionVerify)
|
||||
mux.HandleFunc("GET /savedata/system", handleGetSystemData)
|
||||
mux.HandleFunc("GET /savedata/session", handleGetSessionData)
|
||||
|
||||
// daily
|
||||
mux.HandleFunc("GET /daily/seed", handleDailySeed)
|
||||
mux.HandleFunc("GET /daily/rankings", handleDailyRankings)
|
||||
mux.HandleFunc("GET /daily/rankingpagecount", handleDailyRankingPageCount)
|
||||
return nil
|
||||
}
|
||||
|
||||
func tokenFromRequest(r *http.Request) ([]byte, error) {
|
||||
@ -74,20 +85,34 @@ func tokenFromRequest(r *http.Request) ([]byte, error) {
|
||||
}
|
||||
|
||||
func uuidFromRequest(r *http.Request) ([]byte, error) {
|
||||
_, uuid, err := tokenAndUuidFromRequest(r)
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
func tokenAndUuidFromRequest(r *http.Request) ([]byte, []byte, error) {
|
||||
token, err := tokenFromRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
uuid, err := db.FetchUUIDFromToken(token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to validate token: %s", err)
|
||||
return nil, nil, fmt.Errorf("failed to validate token: %s", err)
|
||||
}
|
||||
|
||||
return uuid, nil
|
||||
return token, uuid, nil
|
||||
}
|
||||
|
||||
func httpError(w http.ResponseWriter, r *http.Request, err error, code int) {
|
||||
log.Printf("%s: %s\n", r.URL.Path, err)
|
||||
http.Error(w, err.Error(), code)
|
||||
}
|
||||
}
|
||||
|
||||
func jsonResponse(w http.ResponseWriter, r *http.Request, data any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(data)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to encode response json: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -72,11 +72,16 @@ func Init() error {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
seed, err = recordNewDaily()
|
||||
log.Printf("Daily Run Seed: %s", seed)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("error while recording new daily: %s", err)
|
||||
} else {
|
||||
log.Printf("Daily Run Seed: %s", seed)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
555
api/endpoints.go
555
api/endpoints.go
@ -18,8 +18,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"database/sql"
|
||||
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@ -58,13 +61,7 @@ func handleAccountInfo(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(response)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to encode response json: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
jsonResponse(w, r, response)
|
||||
}
|
||||
|
||||
func handleAccountRegister(w http.ResponseWriter, r *http.Request) {
|
||||
@ -96,13 +93,7 @@ func handleAccountLogin(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(response)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to encode response json: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
jsonResponse(w, r, response)
|
||||
}
|
||||
|
||||
func handleAccountChangePW(w http.ResponseWriter, r *http.Request) {
|
||||
@ -144,29 +135,66 @@ func handleAccountLogout(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// game
|
||||
|
||||
func handleGamePlayerCount(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(strconv.Itoa(playerCount)))
|
||||
}
|
||||
|
||||
func handleGameTitleStats(w http.ResponseWriter, r *http.Request) {
|
||||
err := json.NewEncoder(w).Encode(defs.TitleStats{
|
||||
stats := defs.TitleStats{
|
||||
PlayerCount: playerCount,
|
||||
BattleCount: battleCount,
|
||||
})
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to encode response json: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
jsonResponse(w, r, stats)
|
||||
}
|
||||
|
||||
func handleGameClassicSessionCount(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(strconv.Itoa(classicSessionCount)))
|
||||
_, _ = w.Write([]byte(strconv.Itoa(classicSessionCount)))
|
||||
}
|
||||
|
||||
func handleSaveData(w http.ResponseWriter, r *http.Request) {
|
||||
func handleGetSessionData(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var slot int
|
||||
if r.URL.Query().Has("slot") {
|
||||
slot, err = strconv.Atoi(r.URL.Query().Get("slot"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var clientSessionId string
|
||||
if r.URL.Query().Has("clientSessionId") {
|
||||
clientSessionId = r.URL.Query().Get("clientSessionId")
|
||||
} else {
|
||||
httpError(w, r, fmt.Errorf("missing clientSessionId"), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
err = db.UpdateActiveSession(uuid, clientSessionId)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var save any
|
||||
save, err = savedata.Get(uuid, 1, slot)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(w, r, save)
|
||||
}
|
||||
|
||||
const legacyClientSessionId = "LEGACY_CLIENT"
|
||||
|
||||
func legacyHandleGetSaveData(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
@ -191,6 +219,231 @@ func handleSaveData(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
var save any
|
||||
if datatype == 0 {
|
||||
err = db.UpdateActiveSession(uuid, legacyClientSessionId) // we dont have a client id
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
save, err = savedata.Get(uuid, datatype, slot)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(w, r, save)
|
||||
}
|
||||
|
||||
// FIXME UNFINISHED!!!
|
||||
func clearSessionData(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var slot int
|
||||
if r.URL.Query().Has("slot") {
|
||||
slot, err = strconv.Atoi(r.URL.Query().Get("slot"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var save any
|
||||
var session defs.SessionSaveData
|
||||
err = json.NewDecoder(r.Body).Decode(&session)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to decode request body: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
save = session
|
||||
|
||||
var active bool
|
||||
active, err = db.IsActiveSession(uuid, legacyClientSessionId) //TODO unfinished, read token from query
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var trainerId, secretId int
|
||||
if r.URL.Query().Has("trainerId") && r.URL.Query().Has("secretId") {
|
||||
trainerId, err = strconv.Atoi(r.URL.Query().Get("trainerId"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
secretId, err = strconv.Atoi(r.URL.Query().Get("secretId"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
storedTrainerId, storedSecretId, err := db.FetchTrainerIds(uuid)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if storedTrainerId > 0 || storedSecretId > 0 {
|
||||
if trainerId != storedTrainerId || secretId != storedSecretId {
|
||||
httpError(w, r, fmt.Errorf("session out of date"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = db.UpdateTrainerIds(trainerId, secretId, uuid)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("unable to update traienr ID: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !active {
|
||||
save = savedata.ClearResponse{Error: "session out of date"}
|
||||
}
|
||||
|
||||
var seed string
|
||||
seed, err = db.GetDailyRunSeed()
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := savedata.Clear(uuid, slot, seed, save.(defs.SessionSaveData))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(w, r, response)
|
||||
}
|
||||
|
||||
// FIXME UNFINISHED!!!
|
||||
func deleteSystemSave(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
datatype := 0
|
||||
if r.URL.Query().Has("datatype") {
|
||||
datatype, err = strconv.Atoi(r.URL.Query().Get("datatype"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var slot int
|
||||
if r.URL.Query().Has("slot") {
|
||||
slot, err = strconv.Atoi(r.URL.Query().Get("slot"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var active bool
|
||||
active, err = db.IsActiveSession(uuid, legacyClientSessionId) //TODO unfinished, read token from query
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if !active {
|
||||
httpError(w, r, fmt.Errorf("session out of date"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var trainerId, secretId int
|
||||
|
||||
if r.URL.Query().Has("trainerId") && r.URL.Query().Has("secretId") {
|
||||
trainerId, err = strconv.Atoi(r.URL.Query().Get("trainerId"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
secretId, err = strconv.Atoi(r.URL.Query().Get("secretId"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
storedTrainerId, storedSecretId, err := db.FetchTrainerIds(uuid)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if storedTrainerId > 0 || storedSecretId > 0 {
|
||||
if trainerId != storedTrainerId || secretId != storedSecretId {
|
||||
httpError(w, r, fmt.Errorf("session out of date"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := db.UpdateTrainerIds(trainerId, secretId, uuid); err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = savedata.Delete(uuid, datatype, slot)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func legacyHandleSaveData(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
datatype := -1
|
||||
if r.URL.Query().Has("datatype") {
|
||||
datatype, err = strconv.Atoi(r.URL.Query().Get("datatype"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var slot int
|
||||
if r.URL.Query().Has("slot") {
|
||||
slot, err = strconv.Atoi(r.URL.Query().Get("slot"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var clientSessionId string
|
||||
if r.URL.Query().Has("clientSessionId") {
|
||||
clientSessionId = r.URL.Query().Get("clientSessionId")
|
||||
}
|
||||
if clientSessionId == "" {
|
||||
clientSessionId = legacyClientSessionId
|
||||
}
|
||||
|
||||
var save any
|
||||
// /savedata/get and /savedata/delete specify datatype, but don't expect data in body
|
||||
if r.URL.Path != "/savedata/get" && r.URL.Path != "/savedata/delete" {
|
||||
@ -216,24 +469,17 @@ func handleSaveData(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
var token []byte
|
||||
token, err = tokenFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var active bool
|
||||
if r.URL.Path == "/savedata/get" {
|
||||
if datatype == 0 {
|
||||
err = db.UpdateActiveSession(uuid, token)
|
||||
err = db.UpdateActiveSession(uuid, clientSessionId)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
active, err = db.IsActiveSession(token)
|
||||
active, err = db.IsActiveSession(uuid, clientSessionId)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
@ -278,13 +524,20 @@ func handleSaveData(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
db.UpdateTrainerIds(trainerId, secretId, uuid)
|
||||
if err := db.UpdateTrainerIds(trainerId, secretId, uuid); err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/savedata/get":
|
||||
save, err = savedata.Get(uuid, datatype, slot)
|
||||
if err == sql.ErrNoRows {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
case "/savedata/update":
|
||||
err = savedata.Update(uuid, slot, save)
|
||||
case "/savedata/delete":
|
||||
@ -296,20 +549,16 @@ func handleSaveData(w http.ResponseWriter, r *http.Request) {
|
||||
break
|
||||
}
|
||||
|
||||
s, ok := save.(defs.SessionSaveData)
|
||||
if !ok {
|
||||
err = fmt.Errorf("save data is not type SessionSaveData")
|
||||
break
|
||||
}
|
||||
|
||||
// doesn't return a save, but it works
|
||||
var seed string
|
||||
seed, err = db.GetDailyRunSeed()
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
save, err = savedata.Clear(uuid, slot, seed, s)
|
||||
|
||||
// doesn't return a save, but it works
|
||||
save, err = savedata.Clear(uuid, slot, seed, save.(defs.SessionSaveData))
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
@ -321,32 +570,212 @@ func handleSaveData(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(save)
|
||||
jsonResponse(w, r, save)
|
||||
}
|
||||
|
||||
type CombinedSaveData struct {
|
||||
System defs.SystemSaveData `json:"system"`
|
||||
Session defs.SessionSaveData `json:"session"`
|
||||
SessionSlotId int `json:"sessionSlotId"`
|
||||
ClientSessionId string `json:"clientSessionId"`
|
||||
}
|
||||
|
||||
// TODO wrap this in a transaction
|
||||
func handleUpdateAll(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to encode response json: %s", err), http.StatusInternalServerError)
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
var clientSessionId string
|
||||
if r.URL.Query().Has("clientSessionId") {
|
||||
clientSessionId = r.URL.Query().Get("clientSessionId")
|
||||
}
|
||||
if clientSessionId == "" {
|
||||
clientSessionId = legacyClientSessionId
|
||||
}
|
||||
|
||||
var data CombinedSaveData
|
||||
err = json.NewDecoder(r.Body).Decode(&data)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to decode request body: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var active bool
|
||||
active, err = db.IsActiveSession(uuid, clientSessionId)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !active {
|
||||
httpError(w, r, fmt.Errorf("session out of date"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trainerId := data.System.TrainerId
|
||||
secretId := data.System.SecretId
|
||||
|
||||
storedTrainerId, storedSecretId, err := db.FetchTrainerIds(uuid)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if storedTrainerId > 0 || storedSecretId > 0 {
|
||||
if trainerId != storedTrainerId || secretId != storedSecretId {
|
||||
httpError(w, r, fmt.Errorf("session out of date"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := db.UpdateTrainerIds(trainerId, secretId, uuid); err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = savedata.Update(uuid, data.SessionSlotId, data.Session)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
err = savedata.Update(uuid, 0, data.System)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
type SessionVerifyResponse struct {
|
||||
Valid bool `json:"valid"`
|
||||
SessionData *defs.SessionSaveData `json:"sessionData"`
|
||||
}
|
||||
|
||||
type SessionVerifyRequest struct {
|
||||
ClientSessionId string `json:"clientSessionId"`
|
||||
Slot int `json:"slot"`
|
||||
}
|
||||
|
||||
func handleSessionVerify(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var input SessionVerifyRequest
|
||||
err = json.NewDecoder(r.Body).Decode(&input)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to decode request body: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var active bool
|
||||
active, err = db.IsActiveSession(uuid, input.ClientSessionId)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to check active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
response := SessionVerifyResponse{
|
||||
Valid: active,
|
||||
}
|
||||
|
||||
// not valid, send server state
|
||||
if !active {
|
||||
err = db.UpdateActiveSession(uuid, input.ClientSessionId)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var storedSaveData defs.SessionSaveData
|
||||
storedSaveData, err = db.ReadSessionSaveData(uuid, input.Slot)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to read session save data: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response.SessionData = &storedSaveData
|
||||
}
|
||||
|
||||
jsonResponse(w, r, response)
|
||||
}
|
||||
|
||||
func handleGetSystemData(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var clientSessionId string
|
||||
if r.URL.Query().Has("clientSessionId") {
|
||||
clientSessionId = r.URL.Query().Get("clientSessionId")
|
||||
} else {
|
||||
httpError(w, r, fmt.Errorf("missing clientSessionId"), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
err = db.UpdateActiveSession(uuid, clientSessionId)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to update active session: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var save any //TODO this is always system save data
|
||||
save, err = savedata.Get(uuid, 0, 0)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
} else {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(w, r, save)
|
||||
}
|
||||
|
||||
func legacyHandleNewClear(w http.ResponseWriter, r *http.Request) {
|
||||
uuid, err := uuidFromRequest(r)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var slot int
|
||||
if r.URL.Query().Has("slot") {
|
||||
slot, err = strconv.Atoi(r.URL.Query().Get("slot"))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
newClear, err := savedata.NewClear(uuid, slot)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to read new clear: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(w, r, newClear)
|
||||
}
|
||||
|
||||
// daily
|
||||
|
||||
func handleDailySeed(w http.ResponseWriter, r *http.Request) {
|
||||
seed, err := db.GetDailyRunSeed()
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
bytes, err := base64.StdEncoding.DecodeString(seed)
|
||||
|
||||
_, err = w.Write([]byte(seed))
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(bytes)
|
||||
if err != nil {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
return
|
||||
httpError(w, r, fmt.Errorf("failed to write seed: %s", err), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,13 +806,7 @@ func handleDailyRankings(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(rankings)
|
||||
if err != nil {
|
||||
httpError(w, r, fmt.Errorf("failed to encode response json: %s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
jsonResponse(w, r, rankings)
|
||||
}
|
||||
|
||||
func handleDailyRankingPageCount(w http.ResponseWriter, r *http.Request) {
|
||||
@ -402,5 +825,5 @@ func handleDailyRankingPageCount(w http.ResponseWriter, r *http.Request) {
|
||||
httpError(w, r, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
w.Write([]byte(strconv.Itoa(count)))
|
||||
_, _ = w.Write([]byte(strconv.Itoa(count)))
|
||||
}
|
||||
|
@ -19,9 +19,10 @@ package savedata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/pagefaultgames/rogueserver/db"
|
||||
"github.com/pagefaultgames/rogueserver/defs"
|
||||
"log"
|
||||
)
|
||||
|
||||
type ClearResponse struct {
|
||||
@ -56,7 +57,7 @@ func Clear(uuid []byte, slot int, seed string, save defs.SessionSaveData) (Clear
|
||||
}
|
||||
|
||||
if sessionCompleted {
|
||||
response.Success, err = db.TryAddDailyRunCompletion(uuid, save.Seed, int(save.GameMode))
|
||||
response.Success, err = db.TryAddSeedCompletion(uuid, save.Seed, int(save.GameMode))
|
||||
if err != nil {
|
||||
log.Printf("failed to mark seed as completed: %s", err)
|
||||
}
|
||||
|
@ -38,13 +38,30 @@ func Get(uuid []byte, datatype, slot int) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO this should be a transaction
|
||||
compensations, err := db.FetchAndClaimAccountCompensations(uuid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch compensations: %s", err)
|
||||
}
|
||||
|
||||
needsUpdate := false
|
||||
for compensationType, amount := range compensations {
|
||||
system.VoucherCounts[strconv.Itoa(compensationType)] += amount
|
||||
if amount > 0 {
|
||||
needsUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
err = db.StoreSystemSaveData(uuid, system)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update system save data: %s", err)
|
||||
}
|
||||
|
||||
err = db.UpdateAccountStats(uuid, system.GameStats, system.VoucherCounts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update account stats: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return system, nil
|
||||
|
44
api/savedata/newclear.go
Normal file
44
api/savedata/newclear.go
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright (C) 2024 Pagefault Games
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package savedata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pagefaultgames/rogueserver/db"
|
||||
"github.com/pagefaultgames/rogueserver/defs"
|
||||
)
|
||||
|
||||
// /savedata/newclear - return whether a session is a new clear for its seed
|
||||
func NewClear(uuid []byte, slot int) (bool, error) {
|
||||
if slot < 0 || slot >= defs.SessionSlotCount {
|
||||
return false, fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
session, err := db.ReadSessionSaveData(uuid, slot)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
completed, err := db.ReadSeedCompleted(uuid, session.Seed)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to read seed completed: %s", err)
|
||||
}
|
||||
|
||||
return !completed, nil
|
||||
}
|
@ -20,15 +20,11 @@ package savedata
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/pagefaultgames/rogueserver/db"
|
||||
"github.com/pagefaultgames/rogueserver/defs"
|
||||
)
|
||||
|
||||
var zstdEncoder, _ = zstd.NewWriter(nil)
|
||||
|
||||
// /savedata/update - update save data
|
||||
func Update(uuid []byte, slot int, save any) error {
|
||||
err := db.UpdateAccountLastActivity(uuid)
|
||||
@ -62,12 +58,6 @@ func Update(uuid []byte, slot int, save any) error {
|
||||
if slot < 0 || slot >= defs.SessionSlotCount {
|
||||
return fmt.Errorf("slot id %d out of range", slot)
|
||||
}
|
||||
|
||||
filename := "session"
|
||||
if slot != 0 {
|
||||
filename += strconv.Itoa(slot)
|
||||
}
|
||||
|
||||
return db.StoreSessionSaveData(uuid, save, slot)
|
||||
|
||||
default:
|
||||
|
@ -32,15 +32,19 @@ var (
|
||||
classicSessionCount int
|
||||
)
|
||||
|
||||
func scheduleStatRefresh() {
|
||||
scheduler.AddFunc("@every 30s", func() {
|
||||
func scheduleStatRefresh() error {
|
||||
_, err := scheduler.AddFunc("@every 30s", func() {
|
||||
err := updateStats()
|
||||
if err != nil {
|
||||
log.Printf("failed to update stats: %s", err)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scheduler.Start()
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateStats() error {
|
||||
|
@ -18,6 +18,8 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
@ -40,11 +42,6 @@ func AddAccountSession(username string, token []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = handle.Exec("UPDATE sessions s JOIN accounts a ON a.uuid = s.uuid SET s.active = 1 WHERE a.username = ? AND a.lastLoggedIn IS NULL", username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = handle.Exec("UPDATE accounts SET lastLoggedIn = UTC_TIMESTAMP() WHERE username = ?", username)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -213,18 +210,28 @@ func UpdateTrainerIds(trainerId, secretId int, uuid []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsActiveSession(token []byte) (bool, error) {
|
||||
var active int
|
||||
err := handle.QueryRow("SELECT `active` FROM sessions WHERE token = ?", token).Scan(&active)
|
||||
func IsActiveSession(uuid []byte, clientSessionId string) (bool, error) {
|
||||
var storedId string
|
||||
err := handle.QueryRow("SELECT clientSessionId FROM activeClientSessions WHERE sessions.uuid = ?", uuid).Scan(&storedId)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
if storedId == "" {
|
||||
err = UpdateActiveSession(uuid, clientSessionId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return active == 1, nil
|
||||
return storedId == clientSessionId, nil
|
||||
}
|
||||
|
||||
func UpdateActiveSession(uuid []byte, token []byte) error {
|
||||
_, err := handle.Exec("UPDATE sessions SET `active` = CASE WHEN token = ? THEN 1 ELSE 0 END WHERE uuid = ?", token, uuid)
|
||||
func UpdateActiveSession(uuid []byte, clientSessionId string) error {
|
||||
_, err := handle.Exec("REPLACE INTO activeClientSessions VALUES (?, ?)", uuid, clientSessionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func TryAddDailyRun(seed string) (string, error) {
|
||||
var actualSeed string
|
||||
err := handle.QueryRow("INSERT INTO dailyRuns (seed, date) VALUES (?, UTC_DATE()) ON DUPLICATE KEY UPDATE date = date RETURNING seed", seed).Scan(&actualSeed)
|
||||
if err != nil {
|
||||
return "INVALID", err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return actualSeed, nil
|
||||
@ -37,7 +37,7 @@ func GetDailyRunSeed() (string, error) {
|
||||
var seed string
|
||||
err := handle.QueryRow("SELECT seed FROM dailyRuns WHERE date = UTC_DATE()").Scan(&seed)
|
||||
if err != nil {
|
||||
return "INVALID", err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return seed, nil
|
||||
|
114
db/db.go
114
db/db.go
@ -21,9 +21,11 @@ import (
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
var handle *sql.DB
|
||||
@ -36,62 +38,47 @@ func Init(username, password, protocol, address, database string) error {
|
||||
return fmt.Errorf("failed to open database connection: %s", err)
|
||||
}
|
||||
|
||||
handle.SetMaxOpenConns(1000)
|
||||
conns := 1024
|
||||
if protocol != "unix" {
|
||||
conns = 256
|
||||
}
|
||||
|
||||
handle.SetMaxOpenConns(conns)
|
||||
handle.SetMaxIdleConns(conns / 4)
|
||||
|
||||
handle.SetConnMaxIdleTime(time.Second * 10)
|
||||
|
||||
tx, err := handle.Begin()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// accounts
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS accounts (uuid BINARY(16) NOT NULL PRIMARY KEY, username VARCHAR(16) UNIQUE NOT NULL, hash BINARY(32) NOT NULL, salt BINARY(16) NOT NULL, registered TIMESTAMP NOT NULL, lastLoggedIn TIMESTAMP DEFAULT NULL, lastActivity TIMESTAMP DEFAULT NULL, banned TINYINT(1) NOT NULL DEFAULT 0, trainerId SMALLINT(5) UNSIGNED DEFAULT 0, secretId SMALLINT(5) UNSIGNED DEFAULT 0)")
|
||||
tx.Exec("CREATE UNIQUE INDEX IF NOT EXISTS accountsByUsername ON accounts (username)")
|
||||
|
||||
// sessions
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS sessions (token BINARY(32) NOT NULL PRIMARY KEY, uuid BINARY(16) NOT NULL, active TINYINT(1) NOT NULL DEFAULT 0, expire TIMESTAMP DEFAULT NULL, CONSTRAINT sessions_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)")
|
||||
tx.Exec("CREATE INDEX IF NOT EXISTS sessionsByUuid ON sessions (uuid)")
|
||||
|
||||
// stats
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS accountStats (uuid BINARY(16) NOT NULL PRIMARY KEY, playTime INT(11) NOT NULL DEFAULT 0, battles INT(11) NOT NULL DEFAULT 0, classicSessionsPlayed INT(11) NOT NULL DEFAULT 0, sessionsWon INT(11) NOT NULL DEFAULT 0, highestEndlessWave INT(11) NOT NULL DEFAULT 0, highestLevel INT(11) NOT NULL DEFAULT 0, pokemonSeen INT(11) NOT NULL DEFAULT 0, pokemonDefeated INT(11) NOT NULL DEFAULT 0, pokemonCaught INT(11) NOT NULL DEFAULT 0, pokemonHatched INT(11) NOT NULL DEFAULT 0, eggsPulled INT(11) NOT NULL DEFAULT 0, regularVouchers INT(11) NOT NULL DEFAULT 0, plusVouchers INT(11) NOT NULL DEFAULT 0, premiumVouchers INT(11) NOT NULL DEFAULT 0, goldenVouchers INT(11) NOT NULL DEFAULT 0, CONSTRAINT accountStats_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)")
|
||||
|
||||
// compensations
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS accountCompensations (id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, uuid BINARY(16) NOT NULL, voucherType INT(11) NOT NULL, count INT(11) NOT NULL DEFAULT 1, claimed BIT(1) NOT NULL DEFAULT b'0', CONSTRAINT accountCompensations_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)")
|
||||
tx.Exec("CREATE INDEX IF NOT EXISTS accountCompensationsByUuid ON accountCompensations (uuid)")
|
||||
|
||||
// daily runs
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS dailyRuns (date DATE NOT NULL PRIMARY KEY, seed CHAR(24) CHARACTER SET ascii COLLATE ascii_bin NOT NULL)")
|
||||
tx.Exec("CREATE INDEX IF NOT EXISTS dailyRunsByDateAndSeed ON dailyRuns (date, seed)")
|
||||
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS dailyRunCompletions (uuid BINARY(16) NOT NULL, seed CHAR(24) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, mode INT(11) NOT NULL DEFAULT 0, score INT(11) NOT NULL DEFAULT 0, timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (uuid, seed), CONSTRAINT dailyRunCompletions_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)")
|
||||
tx.Exec("CREATE INDEX IF NOT EXISTS dailyRunCompletionsByUuidAndSeed ON dailyRunCompletions (uuid, seed)")
|
||||
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS accountDailyRuns (uuid BINARY(16) NOT NULL, date DATE NOT NULL, score INT(11) NOT NULL DEFAULT 0, WAVE INT(11) NOT NULL DEFAULT 0, timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (uuid, date), CONSTRAINT accountDailyRuns_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT accountDailyRuns_ibfk_2 FOREIGN KEY (date) REFERENCES dailyRuns (date) ON DELETE NO ACTION ON UPDATE NO ACTION)")
|
||||
tx.Exec("CREATE INDEX IF NOT EXISTS accountDailyRunsByDate ON accountDailyRuns (date)")
|
||||
|
||||
// save data
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS systemSaveData (uuid BINARY(16) PRIMARY KEY, data LONGBLOB, timestamp TIMESTAMP)")
|
||||
tx.Exec("CREATE TABLE IF NOT EXISTS sessionSaveData (uuid BINARY(16), slot TINYINT, data LONGBLOB, timestamp TIMESTAMP, PRIMARY KEY (uuid, slot))")
|
||||
err = setupDb(tx)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// TODO temp code
|
||||
_, err = os.Stat("userdata")
|
||||
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) { // not found, do not migrate
|
||||
return nil
|
||||
} else {
|
||||
if !os.IsNotExist(err) { // not found, do not migrate
|
||||
log.Fatalf("failed to stat userdata directory: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
entries, err := os.ReadDir("userdata")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
return nil
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
@ -106,6 +93,12 @@ func Init(username, password, protocol, address, database string) error {
|
||||
continue
|
||||
}
|
||||
|
||||
var count int
|
||||
err = handle.QueryRow("SELECT COUNT(*) FROM systemSaveData WHERE uuid = ?", uuid).Scan(&count)
|
||||
if err != nil || count != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// store new system data
|
||||
systemData, err := LegacyReadSystemSaveData(uuid)
|
||||
if err != nil {
|
||||
@ -116,7 +109,6 @@ func Init(username, password, protocol, address, database string) error {
|
||||
err = StoreSystemSaveData(uuid, systemData)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to store system save data for %v: %s\n", uuidString, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// delete old system data
|
||||
@ -152,3 +144,47 @@ func Init(username, password, protocol, address, database string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupDb(tx *sql.Tx) error {
|
||||
queries := []string{
|
||||
// MIGRATION 000
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS accounts (uuid BINARY(16) NOT NULL PRIMARY KEY, username VARCHAR(16) UNIQUE NOT NULL, hash BINARY(32) NOT NULL, salt BINARY(16) NOT NULL, registered TIMESTAMP NOT NULL, lastLoggedIn TIMESTAMP DEFAULT NULL, lastActivity TIMESTAMP DEFAULT NULL, banned TINYINT(1) NOT NULL DEFAULT 0, trainerId SMALLINT(5) UNSIGNED DEFAULT 0, secretId SMALLINT(5) UNSIGNED DEFAULT 0)`,
|
||||
`CREATE INDEX IF NOT EXISTS accountsByActivity ON accounts (lastActivity)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS sessions (token BINARY(32) NOT NULL PRIMARY KEY, uuid BINARY(16) NOT NULL, active TINYINT(1) NOT NULL DEFAULT 0, expire TIMESTAMP DEFAULT NULL, CONSTRAINT sessions_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)`,
|
||||
`CREATE INDEX IF NOT EXISTS sessionsByUuid ON sessions (uuid)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS accountStats (uuid BINARY(16) NOT NULL PRIMARY KEY, playTime INT(11) NOT NULL DEFAULT 0, battles INT(11) NOT NULL DEFAULT 0, classicSessionsPlayed INT(11) NOT NULL DEFAULT 0, sessionsWon INT(11) NOT NULL DEFAULT 0, highestEndlessWave INT(11) NOT NULL DEFAULT 0, highestLevel INT(11) NOT NULL DEFAULT 0, pokemonSeen INT(11) NOT NULL DEFAULT 0, pokemonDefeated INT(11) NOT NULL DEFAULT 0, pokemonCaught INT(11) NOT NULL DEFAULT 0, pokemonHatched INT(11) NOT NULL DEFAULT 0, eggsPulled INT(11) NOT NULL DEFAULT 0, regularVouchers INT(11) NOT NULL DEFAULT 0, plusVouchers INT(11) NOT NULL DEFAULT 0, premiumVouchers INT(11) NOT NULL DEFAULT 0, goldenVouchers INT(11) NOT NULL DEFAULT 0, CONSTRAINT accountStats_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS accountCompensations (id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, uuid BINARY(16) NOT NULL, voucherType INT(11) NOT NULL, count INT(11) NOT NULL DEFAULT 1, claimed BIT(1) NOT NULL DEFAULT b'0', CONSTRAINT accountCompensations_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)`,
|
||||
`CREATE INDEX IF NOT EXISTS accountCompensationsByUuid ON accountCompensations (uuid)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS dailyRuns (date DATE NOT NULL PRIMARY KEY, seed CHAR(24) CHARACTER SET ascii COLLATE ascii_bin NOT NULL)`,
|
||||
`CREATE INDEX IF NOT EXISTS dailyRunsByDateAndSeed ON dailyRuns (date, seed)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS dailyRunCompletions (uuid BINARY(16) NOT NULL, seed CHAR(24) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, mode INT(11) NOT NULL DEFAULT 0, score INT(11) NOT NULL DEFAULT 0, timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (uuid, seed), CONSTRAINT dailyRunCompletions_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)`,
|
||||
`CREATE INDEX IF NOT EXISTS dailyRunCompletionsByUuidAndSeed ON dailyRunCompletions (uuid, seed)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS accountDailyRuns (uuid BINARY(16) NOT NULL, date DATE NOT NULL, score INT(11) NOT NULL DEFAULT 0, wave INT(11) NOT NULL DEFAULT 0, timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (uuid, date), CONSTRAINT accountDailyRuns_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT accountDailyRuns_ibfk_2 FOREIGN KEY (date) REFERENCES dailyRuns (date) ON DELETE NO ACTION ON UPDATE NO ACTION)`,
|
||||
`CREATE INDEX IF NOT EXISTS accountDailyRunsByDate ON accountDailyRuns (date)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS systemSaveData (uuid BINARY(16) PRIMARY KEY, data LONGBLOB, timestamp TIMESTAMP)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS sessionSaveData (uuid BINARY(16), slot TINYINT, data LONGBLOB, timestamp TIMESTAMP, PRIMARY KEY (uuid, slot))`,
|
||||
|
||||
// ----------------------------------
|
||||
// MIGRATION 001
|
||||
|
||||
`ALTER TABLE sessions DROP COLUMN IF EXISTS active`,
|
||||
`CREATE TABLE IF NOT EXISTS activeClientSessions (uuid BINARY(16) NOT NULL PRIMARY KEY, clientSessionId VARCHAR(32) NOT NULL, FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)`,
|
||||
}
|
||||
|
||||
for _, q := range queries {
|
||||
_, err := tx.Exec(q)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute query: %w, query: %s", err, q)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
"github.com/pagefaultgames/rogueserver/defs"
|
||||
)
|
||||
|
||||
func TryAddDailyRunCompletion(uuid []byte, seed string, mode int) (bool, error) {
|
||||
func TryAddSeedCompletion(uuid []byte, seed string, mode int) (bool, error) {
|
||||
var count int
|
||||
err := handle.QueryRow("SELECT COUNT(*) FROM dailyRunCompletions WHERE uuid = ? AND seed = ?", uuid, seed).Scan(&count)
|
||||
if err != nil {
|
||||
@ -41,6 +41,16 @@ func TryAddDailyRunCompletion(uuid []byte, seed string, mode int) (bool, error)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func ReadSeedCompleted(uuid []byte, seed string) (bool, error) {
|
||||
var count int
|
||||
err := handle.QueryRow("SELECT COUNT(*) FROM dailyRunCompletions WHERE uuid = ? AND seed = ?", uuid, seed).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func ReadSystemSaveData(uuid []byte) (defs.SystemSaveData, error) {
|
||||
var system defs.SystemSaveData
|
||||
|
||||
@ -65,7 +75,7 @@ func StoreSystemSaveData(uuid []byte, data defs.SystemSaveData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = handle.Exec("REPLACE INTO systemSaveData (uuid, data, timestamp) VALUES (?, ?, UTC_TIMESTAMP())", uuid, buf.Bytes())
|
||||
_, err = handle.Exec("INSERT INTO systemSaveData (uuid, data, timestamp) VALUES (?, ?, UTC_TIMESTAMP()) ON DUPLICATE KEY UPDATE data = ?, timestamp = UTC_TIMESTAMP()", uuid, buf.Bytes(), buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -116,7 +126,7 @@ func StoreSessionSaveData(uuid []byte, data defs.SessionSaveData, slot int) erro
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = handle.Exec("REPLACE INTO sessionSaveData (uuid, slot, data, timestamp) VALUES (?, ?, ?, UTC_TIMESTAMP())", uuid, slot, buf.Bytes())
|
||||
_, err = handle.Exec("INSERT INTO sessionSaveData (uuid, slot, data, timestamp) VALUES (?, ?, ?, UTC_TIMESTAMP()) ON DUPLICATE KEY UPDATE data = ?, timestamp = UTC_TIMESTAMP()", uuid, slot, buf.Bytes(), buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
14
docker-compose.Development.yml
Normal file
14
docker-compose.Development.yml
Normal file
@ -0,0 +1,14 @@
|
||||
services:
|
||||
db:
|
||||
image: mariadb:11
|
||||
container_name: pokerogue-db-local
|
||||
restart: on-failure
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: admin
|
||||
MYSQL_DATABASE: pokeroguedb
|
||||
MYSQL_USER: pokerogue
|
||||
MYSQL_PASSWORD: pokerogue
|
||||
ports:
|
||||
- "3306:3306"
|
||||
volumes:
|
||||
- ./.data/db:/var/lib/mysql
|
54
docker-compose.Example.yml
Normal file
54
docker-compose.Example.yml
Normal file
@ -0,0 +1,54 @@
|
||||
services:
|
||||
server:
|
||||
command: --debug --dbaddr db --dbuser pokerogue --dbpass pokerogue --dbname pokeroguedb
|
||||
image: ghcr.io/pagefaultgames/rogueserver:master
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- internal
|
||||
ports:
|
||||
- "8001:8001"
|
||||
|
||||
db:
|
||||
image: mariadb:11
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: [ "CMD", "healthcheck.sh", "--su-mysql", "--connect", "--innodb_initialized" ]
|
||||
start_period: 10s
|
||||
start_interval: 10s
|
||||
interval: 1m
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: admin
|
||||
MYSQL_DATABASE: pokeroguedb
|
||||
MYSQL_USER: pokerogue
|
||||
MYSQL_PASSWORD: pokerogue
|
||||
volumes:
|
||||
- database:/var/lib/mysql
|
||||
networks:
|
||||
- internal
|
||||
|
||||
# Watchtower is a service that will automatically update your running containers
|
||||
# when a new image is available. This is useful for keeping your server up-to-date.
|
||||
# see https://containrrr.dev/watchtower/ for more information.
|
||||
watchtower:
|
||||
image: containrrr/watchtower
|
||||
container_name: watchtower
|
||||
restart: always
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
environment:
|
||||
WATCHTOWER_CLEANUP: true
|
||||
WATCHTOWER_SCHEDULE: "@midnight"
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
volumes:
|
||||
database:
|
||||
|
||||
networks:
|
||||
internal:
|
@ -39,13 +39,13 @@ func main() {
|
||||
proto := "tcp"
|
||||
addr := "0.0.0.0:8001"
|
||||
|
||||
|
||||
dbuser := os.Getenv("dbuser")
|
||||
dbpass := os.Getenv("dbpass")
|
||||
dbproto := "tcp"
|
||||
dbaddr := os.Getenv("dbaddr")
|
||||
dbname := os.Getenv("dbname")
|
||||
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// register gob types
|
||||
@ -67,13 +67,20 @@ func main() {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// init api
|
||||
api.Init(mux)
|
||||
if err := api.Init(mux); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// start web server
|
||||
if debug == true {
|
||||
err = http.Serve(listener, debugHandler(mux))
|
||||
|
||||
handler := prodHandler(mux)
|
||||
if debug {
|
||||
handler = debugHandler(mux)
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
err = http.Serve(listener, mux)
|
||||
err = http.ServeTLS(listener, handler, *tlscert, *tlskey)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create http server or server errored: %s", err)
|
||||
@ -91,12 +98,30 @@ func createListener(proto, addr string) (net.Listener, error) {
|
||||
}
|
||||
|
||||
if proto == "unix" {
|
||||
os.Chmod(addr, 0777)
|
||||
if err := os.Chmod(addr, 0777); err != nil {
|
||||
listener.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
func prodHandler(router *http.ServeMux) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "https://pokerogue.net")
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
router.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func debugHandler(router *http.ServeMux) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Headers", "*")
|
||||
|
Loading…
x
Reference in New Issue
Block a user