feat(backend): avatar migration stuff
This commit is contained in:
parent
bcdb2f9540
commit
77e74dd331
15 changed files with 2094 additions and 0 deletions
103
migration-tools/avatar-proxy/main.go
Normal file
103
migration-tools/avatar-proxy/main.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Proxy struct {
|
||||
OldAvatarBase string `json:"old_avatar_base"`
|
||||
NewAvatarBase string `json:"new_avatar_base"`
|
||||
FlagBase string `json:"flag_base"`
|
||||
|
||||
Port int `json:"port"`
|
||||
Database string `json:"database"`
|
||||
|
||||
db *pgxpool.Pool
|
||||
}
|
||||
|
||||
type EntityWithAvatar struct {
|
||||
ID uint64
|
||||
LegacyID string
|
||||
Avatar string
|
||||
AvatarMigrated bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
b, err := os.ReadFile("config.json")
|
||||
if err != nil {
|
||||
log.Fatalln("error reading config:", err)
|
||||
}
|
||||
|
||||
p := &Proxy{}
|
||||
err = json.Unmarshal(b, p)
|
||||
if err != nil {
|
||||
log.Fatalln("error parsing config:", err)
|
||||
}
|
||||
|
||||
p.db, err = pgxpool.New(context.Background(), p.Database)
|
||||
if err != nil {
|
||||
log.Fatalln("error connecting to database:", err)
|
||||
}
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.HandleFunc(`/flags/{hash:[\da-f]+}.webp`, func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, fmt.Sprintf("%s%s", p.FlagBase, r.URL.Path), http.StatusTemporaryRedirect)
|
||||
})
|
||||
r.Get(`/members/{id:[\d]+}/avatars/{hash:[\da-f]+}.webp`, p.proxyHandler("member"))
|
||||
r.Get(`/users/{id:[\d]+}/avatars/{hash:[\da-f]+}.webp`, p.proxyHandler("user"))
|
||||
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("file not found"))
|
||||
})
|
||||
|
||||
log.Printf("serving on port %v", p.Port)
|
||||
|
||||
err = http.ListenAndServe(":"+strconv.Itoa(p.Port), r)
|
||||
if err != nil {
|
||||
log.Fatalf("listening on port %v: %v", p.Port, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) proxyHandler(avatarType string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
hash := chi.URLParam(r, "hash")
|
||||
|
||||
var e EntityWithAvatar
|
||||
// don't do this normally, kids. avatarType can only be "user" or "member" so it's fine here but this is a BAD idea otherwise.
|
||||
err := p.db.QueryRow(
|
||||
r.Context(), "SELECT id, legacy_id, avatar, avatar_migrated FROM "+avatarType+"s WHERE id = $1 AND avatar = $2",
|
||||
id, hash,
|
||||
).Scan(&e.ID, &e.LegacyID, &e.Avatar, &e.AvatarMigrated)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("avatar not found"))
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("error getting avatar for %s %s: %v", avatarType, id, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("internal server error"))
|
||||
return
|
||||
}
|
||||
|
||||
if e.AvatarMigrated {
|
||||
http.Redirect(w, r, fmt.Sprintf("%s/%ss/%d/avatars/%s.webp", p.NewAvatarBase, avatarType, e.ID, e.Avatar), http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
http.Redirect(w, r, fmt.Sprintf("%s/%ss/%s/%s.webp", p.OldAvatarBase, avatarType, e.LegacyID, e.Avatar), http.StatusTemporaryRedirect)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue