Copied over SWAssistant and CDR backend
Started converting them to new backend
This commit is contained in:
@@ -3,17 +3,18 @@ module github.com/CalebQ42/darkstorm-server
|
|||||||
go 1.22.5
|
go 1.22.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/CalebQ42/bbConvert v1.0.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
go.mongodb.org/mongo-driver v1.15.1
|
go.mongodb.org/mongo-driver v1.15.1
|
||||||
golang.org/x/crypto v0.24.0
|
golang.org/x/crypto v0.24.0
|
||||||
github.com/CalebQ42/bbConvert v1.0.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/snappy v0.0.1 // indirect
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/klauspost/compress v1.13.6 // indirect
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
|
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
|||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8=
|
||||||
|
github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
|||||||
@@ -213,8 +213,8 @@ func (b *BlogApp) reqLatestBlogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var ret struct {
|
var ret struct {
|
||||||
Blogs []Blog `json:"blogs"`
|
Blogs []*Blog `json:"blogs"`
|
||||||
Num int `json:"num"`
|
Num int `json:"num"`
|
||||||
}
|
}
|
||||||
ret.Num = len(blogs)
|
ret.Num = len(blogs)
|
||||||
ret.Blogs = blogs
|
ret.Blogs = blogs
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Caleb Gardner
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# cdr-backend
|
||||||
|
|
||||||
|
Stupid backend for [CDR]("https://github.com/CalebQ42/CustomDiceRoller").
|
||||||
|
|
||||||
|
## APIs
|
||||||
|
|
||||||
|
### Dice
|
||||||
|
|
||||||
|
Dice sharing
|
||||||
|
|
||||||
|
> POST: /upload?key={api_key}
|
||||||
|
|
||||||
|
Upload a die.
|
||||||
|
|
||||||
|
Request Body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// Die data
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Only allows up to 1MB of data. If over 1MB returns 413. Further limits might be imposed in the future.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "die ID",
|
||||||
|
"expiration": 0 // Unix time (Seconds) of expiration
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> GET: /die/{die id}?key={api_key}
|
||||||
|
|
||||||
|
Get an uploaded die.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// die data minus uid
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package cdr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/backend/db"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CDRBackend struct {
|
||||||
|
back *backend.Backend
|
||||||
|
db *mongo.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBackend(back *backend.Backend, db *mongo.Database) *CDRBackend {
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(time.Hour) {
|
||||||
|
log.Println("CDR: Deleting expired dice")
|
||||||
|
res, err := db.Collection("profiles").DeleteMany(context.TODO(), bson.M{"expiration": bson.M{"$lt": time.Now().Unix()}})
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("CDR: Deleted", res.DeletedCount, "dice")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return &CDRBackend{
|
||||||
|
back: back,
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b CDRBackend) AppID() string {
|
||||||
|
return "cdr"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b CDRBackend) CountTable() backend.CountTable {
|
||||||
|
return db.NewMongoTable[backend.CountLog](b.db.Collection("logs"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b CDRBackend) CrashTable() backend.CrashTable {
|
||||||
|
return db.NewMongoCrashTable(b.db.Collection("crashes"), b.db.Collection("crashArchive"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s CDRBackend) AddCrash(cr backend.IndividualCrash) bool {
|
||||||
|
res := s.db.Collection("versions").FindOne(context.TODO(), bson.M{"version": cr.Version})
|
||||||
|
return res.Err() != mongo.ErrNoDocuments
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b CDRBackend) Extension(mux *http.ServeMux) {
|
||||||
|
mux.HandleFunc("POST /cdr/die", b.UploadDie)
|
||||||
|
mux.HandleFunc("GET /cdr/die/{dieID}", b.GetDie)
|
||||||
|
|
||||||
|
//Legacy (TODO: remove this after a month or two after the applciation gets updated)
|
||||||
|
mux.HandleFunc("POST /upload", b.UploadDie)
|
||||||
|
mux.HandleFunc("GET /die/{dieID}", b.GetDie)
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package cdr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UploadedDie struct {
|
||||||
|
Die map[string]any `json:"die" bson:"die"`
|
||||||
|
ID string `json:"id" bson:"_id"`
|
||||||
|
Expiration int64 `json:"expiration" bson:"expiration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b CDRBackend) UploadDie(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hdr, err := b.back.VerifyHeader(w, r, "rooms", false)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
if r.Body == nil {
|
||||||
|
backend.ReturnError(w, http.StatusBadRequest, "bad request", "Application sent a bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bod, err := io.ReadAll(r.Body)
|
||||||
|
r.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
backend.ReturnError(w, http.StatusBadRequest, "bad request", "Application sent a bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(bod) > 1048576 { //1MB
|
||||||
|
backend.ReturnError(w, http.StatusRequestEntityTooLarge, "too large", "Die is too large to upload")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var toUpload = UploadedDie{
|
||||||
|
Die: make(map[string]any),
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
Expiration: time.Now().Add(12 * time.Hour).Round(time.Hour).Unix(),
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(bod, &toUpload.Die)
|
||||||
|
if err != nil {
|
||||||
|
backend.ReturnError(w, http.StatusBadRequest, "bad request", "Application sent a bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if toUpload.Die["uuid"] != nil {
|
||||||
|
delete(toUpload.Die, "uuid")
|
||||||
|
}
|
||||||
|
_, err = b.db.Collection("dice").InsertOne(context.TODO(), toUpload)
|
||||||
|
if err != nil {
|
||||||
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
|
log.Println("error inserting die:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
json.NewEncoder(w).Encode(map[string]any{"id": toUpload.ID, "expiration": toUpload.Expiration})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b CDRBackend) GetDie(w http.ResponseWriter, r *http.Request) {
|
||||||
|
res := b.db.Collection("dice").FindOne(context.TODO(), bson.M{"_id": r.PathValue("dieID")})
|
||||||
|
if res.Err() == mongo.ErrNoDocuments {
|
||||||
|
backend.ReturnError(w, 404, "not found", "Die with the given id is not found")
|
||||||
|
return
|
||||||
|
} else if res.Err() != nil {
|
||||||
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
|
log.Println("error getting CDR die:", res.Err())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var dieGet UploadedDie
|
||||||
|
err := res.Decode(&dieGet)
|
||||||
|
if err != nil {
|
||||||
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
|
log.Println("error decoding die:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
json.NewEncoder(w).Encode(dieGet.Die)
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Caleb Gardner
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# swassistant-backend
|
||||||
|
|
||||||
|
Custom backend for [SWAssistant](https://github.com/CalebQ42/SWAssistant). Extension of [darkstorm-backend](https://github.com/CalebQ42/darkstorm-server/tree/main/internal/backend)
|
||||||
|
|
||||||
|
## APIs
|
||||||
|
|
||||||
|
For `POST` requests, the `X-API-Key` http header must be set.
|
||||||
|
|
||||||
|
### Profiles
|
||||||
|
|
||||||
|
#### Upload profile to share
|
||||||
|
|
||||||
|
Character, vehicles, and minion profiles.
|
||||||
|
|
||||||
|
> POST: /profile?type={character|vehicle|minion}
|
||||||
|
|
||||||
|
Upload a profile. `type` query is required.
|
||||||
|
|
||||||
|
Request Body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// profile data
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Only allows up to 5MB of data. If over 5MB returns 413. Further limits might be imposed in the future.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "profile ID",
|
||||||
|
"expiration": 0 // Unix time (Seconds) of expiration
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get a shared profile
|
||||||
|
|
||||||
|
> GET: /profile/{profileID}
|
||||||
|
|
||||||
|
Get an uploaded profile.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "character|vehicle|minion",
|
||||||
|
// profile data minus uid
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rooms
|
||||||
|
|
||||||
|
All room requests must include both `X-API-Key` and `Authorization` headers.
|
||||||
|
|
||||||
|
#### Room list
|
||||||
|
|
||||||
|
> GET: /rooms
|
||||||
|
|
||||||
|
Get a list of rooms your currently a part of.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "room ID",
|
||||||
|
"name": "room name",
|
||||||
|
"owner": "username"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create new room
|
||||||
|
|
||||||
|
> POST: /rooms/new?name={roomName}
|
||||||
|
|
||||||
|
Create a new room. `name` query is required.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "room ID",
|
||||||
|
"name": "room name"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get room info
|
||||||
|
|
||||||
|
> GET: /rooms/{roomID}
|
||||||
|
|
||||||
|
Get info about a room.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "room ID",
|
||||||
|
"name": "room name",
|
||||||
|
"owner": "username",
|
||||||
|
"users": [
|
||||||
|
"username"
|
||||||
|
],
|
||||||
|
"profiles": [
|
||||||
|
"profile uuids"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package swassistant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/backend/db"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SWBackend struct {
|
||||||
|
back *backend.Backend
|
||||||
|
db *mongo.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSWBackend(back *backend.Backend, db *mongo.Database) *SWBackend {
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(time.Hour) {
|
||||||
|
log.Println("SWAssistant: Deleting expired profiles")
|
||||||
|
res, err := db.Collection("profiles").DeleteMany(context.TODO(), bson.M{"expiration": bson.M{"$lt": time.Now().Unix()}})
|
||||||
|
if err == mongo.ErrNoDocuments {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("SWAssistant: Deleted", res.DeletedCount, "profiles")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return &SWBackend{
|
||||||
|
back: back,
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) AppID() string {
|
||||||
|
return "swassistant"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) CountTable() backend.CountTable {
|
||||||
|
return db.NewMongoTable[backend.CountLog](s.db.Collection("logs"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) CrashTable() backend.CrashTable {
|
||||||
|
return db.NewMongoCrashTable(s.db.Collection("crashes"), s.db.Collection("crashArchive"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) AddCrash(cr backend.IndividualCrash) bool {
|
||||||
|
res := s.db.Collection("versions").FindOne(context.TODO(), bson.M{"version": cr.Version})
|
||||||
|
return res.Err() != mongo.ErrNoDocuments
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) Extension(mux *http.ServeMux) {
|
||||||
|
mux.HandleFunc("GET /swa/room", s.ListRooms)
|
||||||
|
mux.HandleFunc("POST /swa/room", s.NewRoom)
|
||||||
|
mux.HandleFunc("GET /swa/room/{roomID}", s.GetRoom)
|
||||||
|
|
||||||
|
mux.HandleFunc("POST /swa/profile", s.UploadProfile)
|
||||||
|
mux.HandleFunc("GET /swa/profile/{profileID}", s.GetProfile)
|
||||||
|
|
||||||
|
//Legacy (TODO: remove this after a month or two after the applciation gets updated)
|
||||||
|
mux.HandleFunc("GET /room/list", s.ListRooms)
|
||||||
|
mux.HandleFunc("POST /room/new", s.NewRoom)
|
||||||
|
mux.HandleFunc("GET /room/{roomID}", s.GetRoom)
|
||||||
|
|
||||||
|
mux.HandleFunc("POST /profile/upload", s.UploadProfile)
|
||||||
|
mux.HandleFunc("GET /profile/{profileID}", s.GetProfile)
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package swassistant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||||
|
"github.com/lithammer/shortuuid/v3"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UploadedProf struct {
|
||||||
|
Profile map[string]any `json:"profile" bson:"profile"`
|
||||||
|
ID string `json:"id" bson:"_id"`
|
||||||
|
Type string `json:"type" bson:"type"`
|
||||||
|
Expiration int64 `json:"expiration" bson:"expiration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) UploadProfile(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hdr, err := s.back.VerifyHeader(w, r, "profile", false)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hdr.Key.AppID != "swassistant" {
|
||||||
|
backend.ReturnError(w, http.StatusUnauthorized, "unauthorized", "Application not authorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
profType := r.URL.Query().Get("type")
|
||||||
|
if profType == "" || (profType != "character" && profType != "vehicle" && profType != "minion") {
|
||||||
|
backend.ReturnError(w, http.StatusBadRequest, "bad request", "Application sent a bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.Body == nil {
|
||||||
|
backend.ReturnError(w, http.StatusBadRequest, "bad request", "Application sent a bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(r.Body)
|
||||||
|
r.Body.Close()
|
||||||
|
if err != nil || len(data) == 0 {
|
||||||
|
backend.ReturnError(w, http.StatusBadRequest, "bad request", "Application sent a bad request")
|
||||||
|
return
|
||||||
|
} else if len(data) > 5242880 { // 5MB
|
||||||
|
backend.ReturnError(w, http.StatusRequestEntityTooLarge, "too large", "Profile is too large")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prof := make(map[string]any)
|
||||||
|
err = json.Unmarshal(data, &prof)
|
||||||
|
if err != nil {
|
||||||
|
backend.ReturnError(w, http.StatusBadRequest, "bad request", "Application sent a bad request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(prof, "uid")
|
||||||
|
toUpload := UploadedProf{
|
||||||
|
ID: shortuuid.New(),
|
||||||
|
Expiration: time.Now().Add(time.Hour * 12).Round(time.Hour).Unix(),
|
||||||
|
Type: profType,
|
||||||
|
Profile: prof,
|
||||||
|
}
|
||||||
|
_, err = s.db.Collection("profiles").InsertOne(context.TODO(), toUpload)
|
||||||
|
if err != nil {
|
||||||
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
|
log.Println("error inserting profile:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
json.NewEncoder(w).Encode(map[string]any{"id": toUpload.ID, "expiration": toUpload.Expiration})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) GetProfile(w http.ResponseWriter, r *http.Request) {
|
||||||
|
res := s.db.Collection("profiles").FindOne(context.TODO(), bson.M{"_id": r.PathValue("profileID")})
|
||||||
|
if res.Err() == mongo.ErrNoDocuments {
|
||||||
|
backend.ReturnError(w, 404, "not found", "Profile not found")
|
||||||
|
return
|
||||||
|
} else if res.Err() != nil {
|
||||||
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
|
log.Println("error getting profile:", res.Err())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var prof UploadedProf
|
||||||
|
err := res.Decode(&prof)
|
||||||
|
if err != nil {
|
||||||
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
|
log.Println("error decoding profile:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prof.Profile["type"] = prof.Type
|
||||||
|
json.NewEncoder(w).Encode(prof.Profile)
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package swassistant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Room struct {
|
||||||
|
ID string `json:"id" bson:"_id"`
|
||||||
|
Name string `json:"name" bson:"name"`
|
||||||
|
Owner string `json:"owner" bson:"owner"`
|
||||||
|
Users []string `json:"users" bson:"users"`
|
||||||
|
Profiles []string `json:"profiles" bson:"profiles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) ListRooms(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hdr, err := s.back.VerifyHeader(w, r, "rooms", false)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hdr.Key.AppID != "swassistant" || hdr.User == nil {
|
||||||
|
backend.ReturnError(w, http.StatusUnauthorized, "unauthorized", "Application not authorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := s.db.Collection("rooms").Find(context.TODO(), bson.M{"users": hdr.User.Username}, options.Find().SetProjection(bson.M{"_id": 1, "name": 1, "owner": 1}))
|
||||||
|
if err != nil && err != mongo.ErrNoDocuments {
|
||||||
|
log.Println("error getting room list:", err)
|
||||||
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out := make([]struct {
|
||||||
|
ID string `json:"id" bson:"_id"`
|
||||||
|
Name string `json:"name" bson:"name"`
|
||||||
|
Owner string `json:"owner" bson:"owner"`
|
||||||
|
}, 0)
|
||||||
|
if err == nil {
|
||||||
|
err = res.All(context.TODO(), &out)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error decoding room list:", err)
|
||||||
|
backend.ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json.NewEncoder(w).Encode(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) NewRoom(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hdr, err := s.back.VerifyHeader(w, r, "rooms", false)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hdr.Key.AppID != "swassistant" || hdr.User == nil {
|
||||||
|
backend.ReturnError(w, http.StatusUnauthorized, "unauthorized", "Application not authorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Method != http.MethodPost || req.Query["name"] == nil || len(req.Query["name"]) != 1 || req.Query["name"][0] == "" {
|
||||||
|
req.Resp.WriteHeader(http.StatusBadRequest)
|
||||||
|
return true
|
||||||
|
} else if req.User == nil {
|
||||||
|
req.Resp.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
//TODO: check room name for unsavory words
|
||||||
|
newRoom := Room{
|
||||||
|
ID: uuid.NewString(),
|
||||||
|
Name: req.Query["name"][0],
|
||||||
|
Owner: req.User.Username,
|
||||||
|
Users: []string{},
|
||||||
|
Profiles: []string{},
|
||||||
|
}
|
||||||
|
_, err := s.db.Collection("rooms").InsertOne(context.TODO(), newRoom)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("SWAssistant: Error creating room:", err)
|
||||||
|
req.Resp.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
out, err := json.Marshal(map[string]string{"id": newRoom.ID, "name": newRoom.Name})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("SWAssistant: Error encoding new room:", err)
|
||||||
|
req.Resp.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
req.Resp.WriteHeader(http.StatusCreated)
|
||||||
|
_, err = req.Resp.Write(out)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("SWAssistant: Error writing new room:", err)
|
||||||
|
req.Resp.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SWBackend) GetRoom(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hdr, err := s.back.VerifyHeader(w, r, "rooms", false)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hdr.Key.AppID != "swassistant" || hdr.User == nil {
|
||||||
|
backend.ReturnError(w, http.StatusUnauthorized, "unauthorized", "Application not authorized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Method != http.MethodGet {
|
||||||
|
req.Resp.WriteHeader(http.StatusBadRequest)
|
||||||
|
return true
|
||||||
|
} else if req.User == nil {
|
||||||
|
req.Resp.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
res := s.db.Collection("rooms").FindOne(context.TODO(), bson.M{"_id": req.Path[1]})
|
||||||
|
if res.Err() == mongo.ErrNoDocuments {
|
||||||
|
req.Resp.WriteHeader(http.StatusNotFound)
|
||||||
|
return true
|
||||||
|
} else if res.Err() != nil {
|
||||||
|
log.Println("SWAssistant: Error getting room:", res.Err())
|
||||||
|
req.Resp.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
r := Room{}
|
||||||
|
err := res.Decode(&r)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("SWAssistant: Error decoding room:", err)
|
||||||
|
req.Resp.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
out, err := json.Marshal(r)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("SWAssistant: Error encoding room:", err)
|
||||||
|
req.Resp.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, err = req.Resp.Write(out)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("SWAssistant: Error writing room:", err)
|
||||||
|
req.Resp.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@ import (
|
|||||||
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
"github.com/CalebQ42/darkstorm-server/internal/backend"
|
||||||
"github.com/CalebQ42/darkstorm-server/internal/backend/db"
|
"github.com/CalebQ42/darkstorm-server/internal/backend/db"
|
||||||
"github.com/CalebQ42/darkstorm-server/internal/blog"
|
"github.com/CalebQ42/darkstorm-server/internal/blog"
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/cdr"
|
||||||
|
"github.com/CalebQ42/darkstorm-server/internal/swassistant"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
)
|
)
|
||||||
@@ -65,19 +67,15 @@ func setupMongo(uri string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setupBackend(mux *http.ServeMux) {
|
func setupBackend(mux *http.ServeMux) {
|
||||||
testApp := backend.NewSimpleApp("testing",
|
|
||||||
db.NewMongoTable[backend.CountLog](mongoClient.Database("testing").Collection("count")),
|
|
||||||
db.NewMongoCrashTable(
|
|
||||||
mongoClient.Database("testing").Collection("crash"),
|
|
||||||
mongoClient.Database("testing").Collection("archive"),
|
|
||||||
))
|
|
||||||
blogApp = blog.NewBlogApp(back, mongoClient.Database("blog"))
|
blogApp = blog.NewBlogApp(back, mongoClient.Database("blog"))
|
||||||
//TODO: SWAssistant and CDR backends
|
swApp := swassistant.NewSWBackend(back, mongoClient.Database("swassistant"))
|
||||||
|
cdrApp := cdr.NewBackend(back, mongoClient.Database("cdr"))
|
||||||
var err error
|
var err error
|
||||||
back, err = backend.NewBackend(db.NewMongoTable[backend.ApiKey](
|
back, err = backend.NewBackend(db.NewMongoTable[backend.ApiKey](
|
||||||
mongoClient.Database("darkstorm").Collection("keys")),
|
mongoClient.Database("darkstorm").Collection("keys")),
|
||||||
testApp,
|
|
||||||
blogApp,
|
blogApp,
|
||||||
|
swApp,
|
||||||
|
cdrApp,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("error setting up backend:", err)
|
log.Fatal("error setting up backend:", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user