mirror of
https://github.com/jesperh1/csgo-hub-backend.git
synced 2025-05-16 09:18:11 +01:00
Initialize project
This commit is contained in:
parent
d53796137d
commit
c5aebc03f4
7
config.yaml
Normal file
7
config.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
config:
|
||||
MYSQL_DB: "cshub"
|
||||
MYSQL_USER: "root"
|
||||
MYSQL_PASS: ""
|
||||
MYSQL_HOST: "127.0.0.1:3306"
|
||||
DOMAIN: "steam.csgohub.xyz"
|
||||
PORT: ":8383"
|
13
go.mod
Normal file
13
go.mod
Normal file
@ -0,0 +1,13 @@
|
||||
module github.com/jesperbakhandskemager/csgo-hub-backend
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/emily33901/go-csfriendcode v0.0.0-20200914202423-31b418e4b897
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/yohcop/openid-go v1.0.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect
|
16
go.sum
Normal file
16
go.sum
Normal file
@ -0,0 +1,16 @@
|
||||
github.com/emily33901/go-csfriendcode v0.0.0-20200914202423-31b418e4b897 h1:/2VV8RykOeIbKbFRaA37wvz3LylTjlwmqRPN1uL90Z4=
|
||||
github.com/emily33901/go-csfriendcode v0.0.0-20200914202423-31b418e4b897/go.mod h1:Fc5k4zUsP3edBqsb8AZdUE/S1ON4D0sVHI4Rcu2pA7Y=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/yohcop/openid-go v1.0.0 h1:EciJ7ZLETHR3wOtxBvKXx9RV6eyHZpCaSZ1inbBaUXE=
|
||||
github.com/yohcop/openid-go v1.0.0/go.mod h1:/408xiwkeItSPJZSTPF7+VtZxPkPrRRpRNK2vjGh6yI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
19
index.html
Normal file
19
index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<html>
|
||||
<head><title>Steam OpenID</title></head>
|
||||
<style>body{font-family: Inter, Avenir, Helvetica, Arial, sans-serif;font-size: 16px;line-height: 24px;font-weight: 400;color-scheme: light dark;color: rgba(255, 255, 255, 0.87);background-color: #242424;font-synthesis: none;text-rendering: optimizeLegibility;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;-webkit-text-size-adjust: 100%;}a {font-weight: 500;color: #646cff;text-decoration: inherit;}a:hover {color: #535bf2;}.login {margin-top: 5%;margin: 0;text-align: center;min-width: 320px;min-height: 100vh;}h1 {font-size: 3.2em;line-height: 1.1;}img {scale: 1.2;}</style>
|
||||
<body>
|
||||
<div class="login">
|
||||
{{if .user}}
|
||||
<p>{{.user}} Linked to your Discord account</p>
|
||||
{{else}}
|
||||
<h1>Authenticate Steam</h1>
|
||||
<a href="/discover">
|
||||
<img alt="Sign in through Steam" src="http://steamcommunity-a.akamaihd.net/public/images/signinthroughsteam/sits_large_noborder.png" />
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .error}}
|
||||
<p>An error occured</p>
|
||||
{{end}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
212
main.go
Normal file
212
main.go
Normal file
@ -0,0 +1,212 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/gorilla/mux"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var domain string
|
||||
var port string
|
||||
|
||||
type YAMLFile struct {
|
||||
Config Config `yaml:"config"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
MYSQL_DB string `yaml:"MYSQL_DB"`
|
||||
MYSQL_USER string `yaml:"MYSQL_USER"`
|
||||
MYSQL_PASS string `yaml:"MYSQL_PASS"`
|
||||
MYSQL_HOST string `yaml:"MYSQL_HOST"`
|
||||
DOMAIN string `yaml:"DOMAIN"`
|
||||
PORT string `yaml:"PORT"`
|
||||
}
|
||||
|
||||
func ReadConfig() (*Config, error) {
|
||||
config := &YAMLFile{}
|
||||
cfgFile, err := os.ReadFile("./config.yaml")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = yaml.Unmarshal(cfgFile, config)
|
||||
return &config.Config, err
|
||||
}
|
||||
|
||||
type user struct {
|
||||
Id int `json:"id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
DiscordId string `json:"discord_id"`
|
||||
FriendCode string `json:"friend_code"`
|
||||
}
|
||||
|
||||
func ReturnSingleUser(w http.ResponseWriter, r *http.Request) {
|
||||
var u user
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
query := `SELECT id, created_at, discord_id, friend_code FROM users where discord_id = ?`
|
||||
err := db.QueryRow(query, id).Scan(&u.Id, &u.CreatedAt, &u.DiscordId, &u.FriendCode)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bad Request")
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(u)
|
||||
}
|
||||
|
||||
func GetMultipleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
// Read body
|
||||
b, err := io.ReadAll(r.Body)
|
||||
defer r.Body.Close()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal
|
||||
var getUsers []user
|
||||
err = json.Unmarshal(b, &getUsers)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
var returnUsers []user
|
||||
|
||||
for _, us := range getUsers {
|
||||
var u user
|
||||
query := `SELECT id, created_at, discord_id, friend_code FROM users where discord_id = ?`
|
||||
err := db.QueryRow(query, us.DiscordId).Scan(&u.Id, &u.CreatedAt, &u.DiscordId, &u.FriendCode)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
returnUsers = append(returnUsers, u)
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(returnUsers)
|
||||
}
|
||||
|
||||
func CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Host != "localhost:8383" {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
fmt.Fprintf(w, "Unauthorized")
|
||||
return
|
||||
}
|
||||
// Read body
|
||||
b, err := io.ReadAll(r.Body)
|
||||
defer r.Body.Close()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal
|
||||
var msg user
|
||||
err = json.Unmarshal(b, &msg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.Exec(`INSERT INTO users(discord_id, friend_code) VALUES (?, ?)`, msg.DiscordId, msg.FriendCode)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bad request")
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprintf(w, "OK")
|
||||
}
|
||||
|
||||
func CreateToken(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Host != "localhost:8383" {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
fmt.Fprintf(w, "Unauthorized")
|
||||
return
|
||||
}
|
||||
token, err := GenerateRandomString(8)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
discord := vars["discord"]
|
||||
if len(discord) != 18 {
|
||||
fmt.Fprintf(w, "Bad request")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.Exec(`INSERT INTO tokens(discord_id, token) VALUES (?, ?)`, discord, token)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bad request")
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(token)
|
||||
}
|
||||
|
||||
func GenerateRandomString(n int) (string, error) {
|
||||
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
|
||||
ret := make([]byte, n)
|
||||
for i := 0; i < n; i++ {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ret[i] = letters[num.Int64()]
|
||||
}
|
||||
|
||||
return string(ret), nil
|
||||
}
|
||||
|
||||
var db *sql.DB
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
config, err := ReadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
domain = config.DOMAIN
|
||||
port = config.PORT
|
||||
|
||||
db, err = sql.Open("mysql", config.MYSQL_USER+":"+config.MYSQL_PASS+"@("+config.MYSQL_HOST+")/"+config.MYSQL_DB+"?parseTime=true")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
log.Println("Database connection established")
|
||||
|
||||
router := mux.NewRouter()
|
||||
|
||||
router.HandleFunc("/", indexHandler)
|
||||
router.HandleFunc("/discover", discoverHandler)
|
||||
router.HandleFunc("/openidcallback", callbackHandler)
|
||||
router.HandleFunc("/{token}", indexHandler)
|
||||
// router.HandleFunc("/api/v1/user/{id}", ReturnSingleUser).Methods("GET")
|
||||
router.HandleFunc("/api/v1/users", GetMultipleUsers)
|
||||
//router.HandleFunc("/api/v1/user", CreateUser).Methods("POST")
|
||||
router.HandleFunc("/api/v1/token/{discord}", CreateToken)
|
||||
http.ListenAndServe(port, router)
|
||||
}
|
136
steam.go
Normal file
136
steam.go
Normal file
@ -0,0 +1,136 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emily33901/go-csfriendcode"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/yohcop/openid-go"
|
||||
)
|
||||
|
||||
// Load the templates once
|
||||
var templateDir = "./"
|
||||
var indexTemplate = template.Must(template.ParseFiles(templateDir + "index.html"))
|
||||
|
||||
// NoOpDiscoveryCache implements the DiscoveryCache interface and doesn't cache anything.
|
||||
// For a simple website, I'm not sure you need a cache.
|
||||
type NoOpDiscoveryCache struct{}
|
||||
|
||||
// Put is a no op.
|
||||
func (n *NoOpDiscoveryCache) Put(id string, info openid.DiscoveredInfo) {}
|
||||
|
||||
// Get always returns nil.
|
||||
func (n *NoOpDiscoveryCache) Get(id string) openid.DiscoveredInfo {
|
||||
return nil
|
||||
}
|
||||
|
||||
var nonceStore = openid.NewSimpleNonceStore()
|
||||
var discoveryCache = &NoOpDiscoveryCache{}
|
||||
|
||||
// indexHandler serves up the index template with the "Sign in through STEAM" button.
|
||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
token := vars["token"]
|
||||
query := `SELECT token FROM tokens where token = BINARY ?`
|
||||
err := db.QueryRow(query, token).Scan(&token)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bad request")
|
||||
return
|
||||
}
|
||||
log.Println(token)
|
||||
expiration := time.Now().Add(time.Hour)
|
||||
cookie := http.Cookie{Name: "token", Value: token, Expires: expiration}
|
||||
http.SetCookie(w, &cookie)
|
||||
indexTemplate.Execute(w, nil)
|
||||
}
|
||||
|
||||
// discoverHandler calls the Steam openid API and redirects to steam for login.
|
||||
func discoverHandler(w http.ResponseWriter, r *http.Request) {
|
||||
url, err := openid.RedirectURL(
|
||||
"http://steamcommunity.com/openid",
|
||||
"http://"+domain+"/openidcallback",
|
||||
"http://"+domain+"/")
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error creating redirect URL: %q\n", err)
|
||||
} else {
|
||||
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
func ClearToken(token string) error {
|
||||
_, err := db.Exec(`DELETE FROM tokens where token = ?`, token)
|
||||
return err
|
||||
}
|
||||
|
||||
// callbackHandler handles the response back from Steam. It verifies the callback and then renders
|
||||
// the index template with the logged in user's id.
|
||||
func callbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fullURL := "http://" + domain + r.URL.String()
|
||||
|
||||
id, err := openid.Verify(fullURL, discoveryCache, nonceStore)
|
||||
if err != nil {
|
||||
log.Printf("Error verifying: %q\n", err)
|
||||
} else {
|
||||
log.Printf("NonceStore: %+v\n", nonceStore)
|
||||
data := make(map[string]string)
|
||||
println(id)
|
||||
steamId, err := strconv.Atoi(strings.Split(id, "/id/")[1])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bad request")
|
||||
return
|
||||
}
|
||||
|
||||
friendCode := csfriendcode.Encode(uint64(steamId))
|
||||
cookie, _ := r.Cookie("token")
|
||||
token := cookie.Value
|
||||
var discordId string
|
||||
|
||||
query := `SELECT discord_id FROM tokens where token = ?`
|
||||
err = db.QueryRow(query, token).Scan(&discordId)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bad request")
|
||||
return
|
||||
}
|
||||
ClearToken(token)
|
||||
|
||||
var checkId, checkFriend string
|
||||
query = `SELECT discord_id, friend_code FROM users where discord_id = ?`
|
||||
err = db.QueryRow(query, discordId).Scan(&checkId, &checkFriend)
|
||||
if err == nil {
|
||||
_, err = db.Exec(`UPDATE users SET friend_code = ? WHERE discord_id = ?`, friendCode, discordId)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bad request")
|
||||
}
|
||||
log.Print(err)
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprintf(w, "Account link updated")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.Exec(`INSERT INTO users(discord_id, friend_code) VALUES (?, ?)`, discordId, friendCode)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bad request")
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
data["user"] = id
|
||||
indexTemplate.Execute(w, data)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user