add proxy server for dynamic api endpoint handling and auth

This commit is contained in:
ju09279
2024-09-01 14:51:19 +02:00
parent 2bf269cf74
commit aea7e147e5
21 changed files with 4643 additions and 4328 deletions

BIN
frontend/public/404.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "My Vue 3 App",
"short_name": "My App",
"name": "osu! music player",
"short_name": "music player",
"icons": [
{
"src": "img/icons/android-chrome-192x192.png",
@@ -13,8 +13,8 @@
"type": "image/png"
}
],
"start_url": "/index.html",
"start_url": "/menu",
"background_color": "#ffffff",
"display": "standalone",
"theme_color": "#4DBA87"
"theme_color": "#1c1719"
}

View File

@@ -69,6 +69,14 @@ function reset() {
<h1> Meeeeee </h1>
<input @change="update" type="text" id="url-input" :value="userStore.baseUrl" />
<br>
<div class="flex p-5 justify-between">
<img :src="'https://a.ppy.sh/14100399'" class="w-1/3">
<div>
<p>User: {{ 'JuLi0n_' }}</p>
<p>Api: Not Connected</p>
<p>Sharing: <button class="border bordercolor rounded-lg p-0.5"> Off </button></p>
</div>
</div>
<div class="flex flex-col w-full justify-around p-10">
<div class="flex flex-1 justify-between">

4
proxy/.env Normal file
View File

@@ -0,0 +1,4 @@
CLIENT_ID=
CLIENT_SECRET=
REDIRECT_URI=
HOST=

0
proxy/.gitignore vendored Normal file
View File

66
proxy/auth.go Normal file
View File

@@ -0,0 +1,66 @@
package main
import (
"errors"
"fmt"
"net/http"
"os"
"strings"
"time"
)
const (
OsuApiUrl = "https://osu.ppy.sh/api/v2"
)
var clientid = os.Getenv("CLIENT_ID")
var clientsecret = os.Getenv("CLIENT_SECRET")
var redirect_uri = os.Getenv("REDIRECT_URI")
var scopes = []string{
"public",
"identify",
}
type OsuApiClient struct {
User User
BaseURL string
HTTPclient *http.Client
}
func NewOsuApiClient(user User) (*OsuApiClient, error) {
if user.Token == (Token{}) {
return nil, errors.New("No Valid Credentials")
}
if time.Now().After(user.ExpireDate) {
}
return &OsuApiClient{
User: user,
BaseURL: OsuApiUrl,
HTTPclient: &http.Client{
Timeout: time.Minute * 2},
}, nil
}
func LoginRedirect(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Context())
cookie, ok := r.Context().Value("cookie").(string)
if !ok || cookie == "" {
fmt.Println(cookie, ok)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
http.Redirect(w, r,
fmt.Sprintf("https://osu.ppy.sh/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s",
clientid,
redirect_uri,
strings.Join(scopes, " "),
cookie),
http.StatusPermanentRedirect)
}

BIN
proxy/database.db Normal file

Binary file not shown.

100
proxy/db.go Normal file
View File

@@ -0,0 +1,100 @@
package main
import (
"database/sql"
"log"
"time"
_ "github.com/mattn/go-sqlite3"
)
type User struct {
UserID int
Name string
AvatarUrl string
EndPoint string
Token
}
type Token struct {
AuthToken string
RefreshToken string
ExpireDate time.Time
}
var db *sql.DB
func InitDB() {
var err error
db, err = sql.Open("sqlite3", "database.db")
if err != nil {
log.Fatal(err)
}
//users
u := `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
endpoint TEXT,
avatar_url TEXT,
auth_token TEXT,
refresh_token TEXT,
expire_date TEXT
);`
_, err = db.Exec(u)
if err != nil {
log.Fatal(err)
}
//cookiejar
a := `
CREATE TABLE IF NOT EXISTS cookiejar (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
cookie TEXT NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) On DELETE CASCADE
);`
_, err = db.Exec(a)
if err != nil {
log.Fatal(err)
}
}
func GetUserByCookie(cookie string) (*User, error) {
query := `
SELECT users.id, users.name, users.endpoint, users.avatar_url, users.auth_token, users.refresh_token, users.expire_date
FROM users users
JOIN cookiejar cookie ON users.id = cookie.user_id
WHERE cookie.cookie = ?`
row := db.QueryRow(query, cookie)
var user User
err := row.Scan(&user.UserID, &user.Name, &user.EndPoint, &user.AvatarUrl, &user.AuthToken, &user.RefreshToken, &user.ExpireDate)
if err != nil {
return &User{}, err
}
return &user, nil
}
func SaveCookie(userID int, cookie string) error {
query := "INSERT INTO cookiejar (user_id, cookie) VALUES (?, ?)"
_, err := db.Exec(query, userID, cookie)
return err
}
func UpdateUserTokens(userID int, auth Token) error {
query := "UPDATE users SET auth_token = ?, refresh_token = ?, expire_date = ? WHERE id = ?"
_, err := db.Exec(query, auth.AuthToken, auth.RefreshToken, auth.ExpireDate, userID)
return err
}
func UpdateUserEndPoint(userID int, endPoint string) error {
query := "UPDATE users SET endpoint = ? WHERE id = ?"
_, err := db.Exec(query, endPoint, userID)
return err
}

8
proxy/go.mod Normal file
View File

@@ -0,0 +1,8 @@
module proxy
go 1.22.1
require (
github.com/joho/godotenv v1.5.1
github.com/mattn/go-sqlite3 v1.14.22
)

4
proxy/go.sum Normal file
View File

@@ -0,0 +1,4 @@
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=

20
proxy/main.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
"github.com/joho/godotenv"
)
func main() {
if ok := godotenv.Load(); ok != nil {
panic(".env not found")
}
InitDB()
err := run()
if err != nil {
fmt.Println(err)
}
}

65
proxy/middleware.go Normal file
View File

@@ -0,0 +1,65 @@
package main
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"net/http"
"time"
)
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_cookie")
if err != nil || cookie.Value == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
user, err := GetUserByCookie(cookie.Value)
if err != nil || cookie.Value == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), "user", user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func CookieMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_cookie")
if err != nil || cookie.Value == "" {
newCookie := &http.Cookie{
Name: "session_cookie",
Value: "session_cookie_" + generateRandomString(64),
Expires: time.Now().Add(time.Hour * 10000),
HttpOnly: true,
Secure: true,
Path: "/",
}
http.SetCookie(w, newCookie)
}
ctx := context.WithValue(r.Context(), "cookie", cookie.Value)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func generateRandomString(length int) string {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
if err != nil {
fmt.Print(err)
return "IF_U_SEE_THIS_UR_THE_GOAT"
}
return base64.URLEncoding.EncodeToString(bytes)
}

40
proxy/routes.go Normal file
View File

@@ -0,0 +1,40 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
)
func run() error {
mux := http.NewServeMux()
mux.Handle("/me", AuthMiddleware(http.HandlerFunc(MeHandler)))
mux.Handle("/login", http.HandlerFunc(LoginRedirect))
fmt.Println("Starting Server on :42000")
//global middleware
handler := CookieMiddleware(mux)
return http.ListenAndServe(":42000", handler)
}
func MeHandler(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value("user").(*User)
w.Header().Set("Content-Type", "application/Json")
JSONResponse(w, http.StatusOK, user)
}
func JSONResponse(w http.ResponseWriter, statusCode int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
if err := json.NewEncoder(w).Encode(data); err != nil {
http.Error(w, "Failed to encode response as JSON", http.StatusInternalServerError)
}
}