Merge branch 'master' into admin-panel-activity-change

This commit is contained in:
Opaque02 2024-10-28 11:21:51 +10:00
commit 4540482ac2
10 changed files with 177 additions and 116 deletions

View File

@ -41,6 +41,7 @@ func HandleDiscordCallback(w http.ResponseWriter, r *http.Request) (string, erro
http.Redirect(w, r, GameURL, http.StatusSeeOther) http.Redirect(w, r, GameURL, http.StatusSeeOther)
return "", errors.New("code is empty") return "", errors.New("code is empty")
} }
discordId, err := RetrieveDiscordId(code) discordId, err := RetrieveDiscordId(code)
if err != nil { if err != nil {
http.Redirect(w, r, GameURL, http.StatusSeeOther) http.Redirect(w, r, GameURL, http.StatusSeeOther)

View File

@ -75,5 +75,6 @@ func GenerateTokenForUsername(username string) (string, error) {
if err != nil { if err != nil {
return "", fmt.Errorf("failed to add account session") return "", fmt.Errorf("failed to add account session")
} }
return base64.StdEncoding.EncodeToString(token), nil return base64.StdEncoding.EncodeToString(token), nil
} }

View File

@ -40,9 +40,8 @@ import (
const secondsPerDay = 60 * 60 * 24 const secondsPerDay = 60 * 60 * 24
var ( var (
scheduler = cron.New(cron.WithLocation(time.UTC)) scheduler = cron.New(cron.WithLocation(time.UTC))
s3scheduler = cron.New(cron.WithLocation(time.UTC)) secret []byte
secret []byte
) )
func Init() error { func Init() error {
@ -91,22 +90,17 @@ func Init() error {
scheduler.Start() scheduler.Start()
if os.Getenv("AWS_ENDPOINT_URL_S3") == "" { if os.Getenv("AWS_ENDPOINT_URL_S3") != "" {
log.Printf("AWS_ENDPOINT_URL_S3 not set, skipping s3 migration") go func() {
return nil for {
err = S3SaveMigration()
if err != nil {
return
}
}
}()
} }
_, err = s3scheduler.AddFunc("@hourly", func() {
time.Sleep(time.Second)
S3SaveMigration()
})
if err != nil {
return err
}
s3scheduler.Start()
return nil return nil
} }
@ -123,38 +117,60 @@ func deriveSeed(seedTime time.Time) []byte {
return hashedSeed[:] return hashedSeed[:]
} }
func S3SaveMigration() { func S3SaveMigration() error {
cfg, _ := config.LoadDefaultConfig(context.TODO()) cfg, _ := config.LoadDefaultConfig(context.TODO())
svc := s3.NewFromConfig(cfg, func(o *s3.Options) { svc := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.BaseEndpoint = aws.String(os.Getenv("AWS_ENDPOINT_URL_S3")) o.BaseEndpoint = aws.String(os.Getenv("AWS_ENDPOINT_URL_S3"))
}) })
// retrieve accounts from db
_, err := svc.CreateBucket(context.Background(), &s3.CreateBucketInput{ _, err := svc.CreateBucket(context.Background(), &s3.CreateBucketInput{
Bucket: aws.String("pokerogue-system"), Bucket: aws.String("pokerogue-system"),
}) })
if err != nil { if err != nil {
log.Printf("error while creating bucket: %s", err) log.Printf("error while creating bucket (already exists?): %s", err)
}
// retrieve accounts from db
accounts, err := db.RetrieveOldAccounts()
if err != nil {
return fmt.Errorf("failed to retrieve old accounts: %s", err)
} }
accounts := db.RetrieveOldAccounts()
for _, user := range accounts { for _, user := range accounts {
data, _ := db.ReadSystemSaveData(user) data, err := db.ReadSystemSaveData(user)
username, _ := db.FetchUsernameFromUUID(user) if err != nil {
json, _ := json.Marshal(data) continue
_, err := svc.PutObject(context.Background(), &s3.PutObjectInput{ }
username, err := db.FetchUsernameFromUUID(user)
if err != nil {
continue
}
json, err := json.Marshal(data)
if err != nil {
continue
}
_, err = svc.PutObject(context.Background(), &s3.PutObjectInput{
Bucket: aws.String("pokerogue-system"), Bucket: aws.String("pokerogue-system"),
Key: aws.String(username), Key: aws.String(username),
Body: bytes.NewReader(json), Body: bytes.NewReader(json),
}) })
if err != nil { if err != nil {
log.Printf("error while saving data in s3 for user %s: %s", username, err) log.Printf("error while saving data in S3 for user %s: %s", username, err)
continue continue
} }
fmt.Printf("Saved data in s3 for user %s\n", username) err = db.UpdateLocation(user, username)
db.UpdateLocation(user, username) if err != nil {
log.Printf("failed to update location for user %s: %s", username, err)
continue
}
log.Printf("saved data in S3 for user %s", username)
} }
return nil
} }

View File

@ -420,8 +420,7 @@ func handleSystem(w http.ResponseWriter, r *http.Request) {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
http.Error(w, err.Error(), http.StatusNotFound) http.Error(w, err.Error(), http.StatusNotFound)
} else { } else {
fmt.Printf("failed to get system save data: %s\n", err) httpError(w, r, fmt.Errorf("failed to get system save data: %s", err), http.StatusInternalServerError)
httpError(w, r, err, http.StatusInternalServerError)
} }
return return
@ -751,36 +750,36 @@ func handleAdminDiscordUnlink(w http.ResponseWriter, r *http.Request) {
discordId := r.Form.Get("discordId") discordId := r.Form.Get("discordId")
switch { switch {
case username != "": case username != "":
log.Printf("Username given, removing discordId") log.Printf("Username given, removing discordId")
// this does a quick call to make sure the username exists on the server before allowing the rest of the code to run // this does a quick call to make sure the username exists on the server before allowing the rest of the code to run
// this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server // this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server
_, err = db.CheckUsernameExists(username) _, err = db.CheckUsernameExists(username)
if err != nil { if err != nil {
httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound) httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound)
return return
} }
userUuid, err := db.FetchUUIDFromUsername(username) userUuid, err := db.FetchUUIDFromUsername(username)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusInternalServerError) httpError(w, r, err, http.StatusInternalServerError)
return return
} }
err = db.RemoveDiscordIdByUUID(userUuid) err = db.RemoveDiscordIdByUUID(userUuid)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusInternalServerError) httpError(w, r, err, http.StatusInternalServerError)
return return
} }
case discordId != "": case discordId != "":
log.Printf("DiscordID given, removing discordId") log.Printf("DiscordID given, removing discordId")
err = db.RemoveDiscordIdByDiscordId(discordId) err = db.RemoveDiscordIdByDiscordId(discordId)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusInternalServerError) httpError(w, r, err, http.StatusInternalServerError)
return return
} }
} }
log.Printf("%s: %s removed discord id %s from username %s", userDiscordId, r.URL.Path, r.Form.Get("discordId"), r.Form.Get("username")) log.Printf("%s: %s removed discord id %s from username %s", userDiscordId, r.URL.Path, r.Form.Get("discordId"), r.Form.Get("username"))
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
@ -821,7 +820,7 @@ func handleAdminGoogleLink(w http.ResponseWriter, r *http.Request) {
httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound) httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound)
return return
} }
userUuid, err := db.FetchUUIDFromUsername(username) userUuid, err := db.FetchUUIDFromUsername(username)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusInternalServerError) httpError(w, r, err, http.StatusInternalServerError)
@ -868,34 +867,34 @@ func handleAdminGoogleUnlink(w http.ResponseWriter, r *http.Request) {
googleId := r.Form.Get("googleId") googleId := r.Form.Get("googleId")
switch { switch {
case username != "": case username != "":
log.Printf("Username given, removing googleId") log.Printf("Username given, removing googleId")
// this does a quick call to make sure the username exists on the server before allowing the rest of the code to run // this does a quick call to make sure the username exists on the server before allowing the rest of the code to run
// this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server // this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server
_, err = db.CheckUsernameExists(username) _, err = db.CheckUsernameExists(username)
if err != nil { if err != nil {
httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound) httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound)
return return
} }
userUuid, err := db.FetchUUIDFromUsername(username) userUuid, err := db.FetchUUIDFromUsername(username)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusInternalServerError) httpError(w, r, err, http.StatusInternalServerError)
return return
} }
err = db.RemoveGoogleIdByUUID(userUuid) err = db.RemoveGoogleIdByUUID(userUuid)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusInternalServerError) httpError(w, r, err, http.StatusInternalServerError)
return return
} }
case googleId != "": case googleId != "":
log.Printf("DiscordID given, removing googleId") log.Printf("DiscordID given, removing googleId")
err = db.RemoveGoogleIdByDiscordId(googleId) err = db.RemoveGoogleIdByDiscordId(googleId)
if err != nil { if err != nil {
httpError(w, r, err, http.StatusInternalServerError) httpError(w, r, err, http.StatusInternalServerError)
return return
} }
} }
log.Printf("%s: %s removed google id %s from username %s", userDiscordId, r.URL.Path, r.Form.Get("googleId"), r.Form.Get("username")) log.Printf("%s: %s removed google id %s from username %s", userDiscordId, r.URL.Path, r.Form.Get("googleId"), r.Form.Get("username"))

View File

@ -1,3 +1,20 @@
/*
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 package savedata
import ( import (

View File

@ -1,3 +1,20 @@
/*
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 package savedata
import ( import (

View File

@ -86,7 +86,6 @@ func AddDiscordIdByUUID(discordId string, uuid []byte) error {
return nil return nil
} }
func FetchUsernameByDiscordId(discordId string) (string, error) { func FetchUsernameByDiscordId(discordId string) (string, error) {
var username string var username string
err := handle.QueryRow("SELECT username FROM accounts WHERE discordId = ?", discordId).Scan(&username) err := handle.QueryRow("SELECT username FROM accounts WHERE discordId = ?", discordId).Scan(&username)
@ -478,4 +477,4 @@ func RemoveGoogleIdByDiscordId(discordId string) error {
} }
return nil return nil
} }

View File

@ -116,5 +116,6 @@ func setupDb(tx *sql.Tx) error {
return fmt.Errorf("failed to execute query: %w, query: %s", err, q) return fmt.Errorf("failed to execute query: %w, query: %s", err, q)
} }
} }
return nil return nil
} }

View File

@ -68,8 +68,13 @@ func ReadSystemSaveData(uuid []byte) (defs.SystemSaveData, error) {
} }
if !isLocal { if !isLocal {
RetrieveSystemSaveFromS3(uuid) // writes the data back into the database
err = RetrieveSystemSaveFromS3(uuid)
if err != nil {
return system, err
}
} }
var data []byte var data []byte
err = handle.QueryRow("SELECT data FROM systemSaveData WHERE uuid = ?", uuid).Scan(&data) err = handle.QueryRow("SELECT data FROM systemSaveData WHERE uuid = ?", uuid).Scan(&data)
if err != nil { if err != nil {
@ -220,6 +225,11 @@ func isSaveInLocalDb(uuid []byte) (bool, error) {
} }
func RetrieveSystemSaveFromS3(uuid []byte) error { func RetrieveSystemSaveFromS3(uuid []byte) error {
username, err := FetchUsernameFromUUID(uuid)
if err != nil {
return err
}
cfg, err := config.LoadDefaultConfig(context.TODO()) cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil { if err != nil {
return err return err
@ -227,31 +237,26 @@ func RetrieveSystemSaveFromS3(uuid []byte) error {
client := s3.NewFromConfig(cfg) client := s3.NewFromConfig(cfg)
username, err := FetchUsernameFromUUID(uuid) s3Object := s3.GetObjectInput{
if err != nil {
return err
}
s3Object := &s3.GetObjectInput{
Bucket: aws.String("pokerogue-system"), Bucket: aws.String("pokerogue-system"),
Key: aws.String(username), Key: aws.String(username),
} }
resp, err := client.GetObject(context.TODO(), s3Object) resp, err := client.GetObject(context.TODO(), &s3Object)
if err != nil { if err != nil {
return err return err
} }
var session defs.SystemSaveData var session defs.SystemSaveData
json.NewDecoder(resp.Body).Decode(&session) err = json.NewDecoder(resp.Body).Decode(&session)
err = StoreSystemSaveData(uuid, session)
if err != nil { if err != nil {
fmt.Printf("Failed to store system save data from s3 for user %s\n", username)
return err return err
} }
fmt.Printf("Retrieved system save data from s3 for user %s\n", username) err = StoreSystemSaveData(uuid, session)
if err != nil {
return fmt.Errorf("failed to store system save data from S3 for user %s: %s", username, err)
}
_, err = handle.Exec("UPDATE accounts SET isInLocalDb = 1 WHERE uuid = ?", uuid) _, err = handle.Exec("UPDATE accounts SET isInLocalDb = 1 WHERE uuid = ?", uuid)
if err != nil { if err != nil {
@ -262,41 +267,45 @@ func RetrieveSystemSaveFromS3(uuid []byte) error {
Bucket: aws.String("pokerogue-system"), Bucket: aws.String("pokerogue-system"),
Key: aws.String(username), Key: aws.String(username),
}) })
if err != nil { if err != nil {
fmt.Printf("Failed to delete object %s from s3: %s\n", username, err) return fmt.Errorf("failed to delete object %s from S3: %s", username, err)
} }
return nil return nil
} }
func RetrieveOldAccounts() [][]byte { func RetrieveOldAccounts() ([][]byte, error) {
var users [][]byte var users [][]byte
rows, err := handle.Query("SELECT uuid FROM accounts WHERE isInLocalDb = 1 && lastActivity < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 3000") rows, err := handle.Query("SELECT uuid FROM accounts WHERE isInLocalDb = 1 AND lastActivity < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 3000")
if err != nil { if err != nil {
return nil return nil, err
} }
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var uuid []byte var uuid []byte
if err := rows.Scan(&uuid); err != nil { err := rows.Scan(&uuid)
return nil if err != nil {
return nil, err
} }
users = append(users, uuid) users = append(users, uuid)
} }
if err := rows.Err(); err != nil {
return nil
}
return users return users, nil
} }
func UpdateLocation(uuid []byte, username string) { func UpdateLocation(uuid []byte, username string) error {
_, err := handle.Exec("UPDATE accounts SET isInLocalDb = 0 WHERE uuid = ?", uuid) _, err := handle.Exec("UPDATE accounts SET isInLocalDb = 0 WHERE uuid = ?", uuid)
if err != nil { if err != nil {
fmt.Printf("Failed to update location for user %s\n", username) return err
return
} }
DeleteSystemSaveData(uuid) err = DeleteSystemSaveData(uuid)
if err != nil {
return err
}
return nil
} }

View File

@ -70,6 +70,7 @@ func main() {
account.GoogleCallbackURL = callbackurl + "/auth/google/callback" account.GoogleCallbackURL = callbackurl + "/auth/google/callback"
account.DiscordSession, _ = discordgo.New("Bot " + discordbottoken) account.DiscordSession, _ = discordgo.New("Bot " + discordbottoken)
account.DiscordGuildID = discordguildid account.DiscordGuildID = discordguildid
// register gob types // register gob types
gob.Register([]interface{}{}) gob.Register([]interface{}{})
gob.Register(map[string]interface{}{}) gob.Register(map[string]interface{}{})