Skip to content

Commit

Permalink
Reimagine as web API
Browse files Browse the repository at this point in the history
  • Loading branch information
kdkaergaard committed Aug 20, 2018
1 parent da485e7 commit 7c42807
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 386 deletions.
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
# Steam Api Utility
# Steam Gameserver REST API

A commandline utility for managing Steam Gameserver Tokens through Steam Web API.
A REST API for pulling Steam Gameserver Tokens through Steamworks Web API.

Its primary target is the IGameServersService Interface, and the code has been built on knowledge from two sources.
Its wraps the IGameServersService Interface, and the code has been built on knowledge from two sources.
A [community made API reference](http://steamwebapi.azurewebsites.net/).
And the [Steamworks Documentation Website](https://partner.steamgames.com/doc/webapi/IGameServersService).

It currently outputs all returned API JSON data as unquoted Semicolon Separated Values with Headers.
It returns tokens as text/plain on the following URL:

The Utility currently supports three features.
> [GET] /token/{appID}/{memo}
* Create new Gameserver Account with accompanying Token
* List Gameserver Accounts
* Delete Gameserver Account by ID (SteamID)
* **appID** is the Steam Application ID (e.g. 740 for CSGO dedicated server)
* **memo** is a note that uniquely identifies a gameserver

If the utility receives an X-error_message Response Header, it will log the message to console, and exit.
The library it uses to communicate with Steamworks Web API is [nested in this project](steam/README.md).

## Errors

Errors from the Steamworks Web API will be forwarded as JSON objects.

> { "error": "some error happened" }
## Build

```sh
# Windows
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o steam-api.exe main.go
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o steam-api.exe main.go app.go

# Linux
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o steam-api main.go
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o steam-api main.go app.go

# OSX
GOOS=darwin go build -ldflags="-s -w" -o steam-api main.go
GOOS=darwin go build -ldflags="-s -w" -o steam-api main.go app.go
```

All binary releases of steam-api are compressed with `upx --brute`.
Optionally, you can cut down binary size with `upx --brute`.
122 changes: 122 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package main

import (
"encoding/json"
"log"
"net/http"
"os"
"strconv"

"github.com/gorilla/mux"
"github.com/npflan/steam-api/steam"
)

// App contains references to global necessities
type App struct {
Router *mux.Router
Log Log
}

// Log is a modifiable endpoint
type Log struct {
Error *log.Logger
Info *log.Logger
}

const defaultLogFormat = log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile | log.LUTC

// Run server on specific interface
func (a *App) Run(addr string) {
a.registerRoutes()
// set default log format if no custom format present
if a.Log.Info == nil {
log.New(os.Stdout, "INFO: ", defaultLogFormat)
}
if a.Log.Error == nil {
log.New(os.Stderr, "ERROR: ", defaultLogFormat)
}
log.Fatal(http.ListenAndServe(addr, a.Router))
}

func (a *App) registerRoutes() {
a.Router = mux.NewRouter().StrictSlash(true)
a.Router.HandleFunc("/", a.getHome).Methods("GET")
a.Router.HandleFunc("/token/{appID}/{memo}", a.pullToken).Methods("GET")
}

// RespondWithJSON uses a struct, for a JSON response.
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}

// RespondWithText returns text/plain.
func respondWithText(w http.ResponseWriter, code int, payload string) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(code)
w.Write([]byte(payload))
}

// RespondWithError standardizes error messages, through the use of RespondWithJSON.
func respondWithError(w http.ResponseWriter, code int, message string) {
respondWithJSON(w, code, map[string]string{"error": message})
}

func (a *App) getHome(w http.ResponseWriter, r *http.Request) {

}

func (a *App) pullToken(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
if _, ok := vars["appID"]; !ok {
respondWithError(w, http.StatusBadRequest, "Missing appID")
return
}
if _, ok := vars["memo"]; !ok {
respondWithError(w, http.StatusBadRequest, "Missing memo")
return
}

appID, err := strconv.Atoi(vars["appID"])
if err != nil {
respondWithError(w, http.StatusBadRequest, "bad appID")
return
}

accounts, err := steam.GetAccountList()
if err != nil {
respondWithError(w, http.StatusInternalServerError, "Unable to list existing tokens")
return
}

// Check for existing account
var account steam.Account
for _, acct := range accounts {
if acct.Memo == vars["memo"] && int(acct.AppID) == appID {
account = acct
break
}
}

// Create new if not found
if account.SteamID == "" {
account, err = steam.CreateAccount(appID, vars["memo"])
}
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}

// Refresh token if found and expired
if account.IsExpired == true {
account, err = steam.ResetLoginToken(account.SteamID)
}
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
}

respondWithText(w, http.StatusOK, account.LoginToken)
}
Loading

0 comments on commit 7c42807

Please sign in to comment.