auth finished

This commit is contained in:
ju09279
2024-09-06 22:56:08 +02:00
parent b1be8502c9
commit f26529b1a3
11 changed files with 397 additions and 35 deletions

85
backend/HttpClient.cs Normal file
View File

@@ -0,0 +1,85 @@
using OsuParsers.Enums.Replays;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
namespace shitweb
{
public class ApiClient
{
private const string CookiesFilePath = "cookies.json";
private HttpClient _client;
private HttpClientHandler _handler;
public ApiClient()
{
_handler = new HttpClientHandler();
_client = new HttpClient(_handler);
}
public async Task InitializeAsync()
{
LoadCookies();
}
public void SaveCookies(String cookie)
{
var cookies = _handler.CookieContainer.GetCookies(new Uri("https://proxy.illegalesachen.download"));
var Cookie = new CookieInfo();
Cookie.Name = "session_cookie";
Cookie.Value = cookie;
var json = JsonSerializer.Serialize(Cookie, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(CookiesFilePath, json);
LoadCookies();
}
public Boolean LoadCookies()
{
if (File.Exists(CookiesFilePath))
{
var json = File.ReadAllText(CookiesFilePath);
var cookieInfo = JsonSerializer.Deserialize<CookieInfo>(json);
var cookie = new Cookie(cookieInfo.Name, cookieInfo.Value);
_handler.CookieContainer.Add(new Uri("https://proxy.illegalesachen.download"), cookie);
return true;
}
return false;
}
public async Task UpdateSettingsAsync(string endpoint)
{
var requestContent = new JsonContent(new { endpoint = endpoint });
var response = await _client.PostAsync("https://proxy.illegalesachen.download/settings", requestContent);
try
{
response.EnsureSuccessStatusCode();
}
catch (Exception ex) {
System.Console.WriteLine(ex.Message);
}
}
}
public class CookieInfo
{
public string Name { get; set; }
public string Value { get; set; }
}
public class JsonContent : StringContent
{
public JsonContent(object obj)
: base(JsonSerializer.Serialize(obj), System.Text.Encoding.UTF8, "application/json")
{
}
}
}

View File

@@ -1,11 +1,17 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using shitweb; using shitweb;
using System;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Net.Http;
using System.Text.RegularExpressions;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -25,18 +31,24 @@ builder.Services.AddCors(options =>
var app = builder.Build(); var app = builder.Build();
app.UseCors("AllowAll"); app.UseCors("AllowAll");
var apiClient = new ApiClient();
app.MapGet("/ping", () => "pong"); app.MapGet("/ping", () => "pong");
// Define the API routes app.MapGet("/login", () => {
return Results.Redirect("https://proxy.illegalesachen.download/login");
});
app.MapGet("/api/v1/songs/{hash}", (string hash) => app.MapGet("/api/v1/songs/{hash}", (string hash) =>
{ {
return Results.Ok(new { hash }); return Results.Ok(new { hash });
}); });
app.MapGet("/api/v1/songs/recent", (int? limit, int? offset) => app.MapGet("/api/v1/songs/recent", (HttpContext httpContext, int? limit, int? offset) =>
{ {
var limitValue = limit ?? 100; // default to 10 if not provided var limitValue = limit ?? 100; // default to 10 if not provided
var offsetValue = offset ?? 0; // default to 0 if not provided var offsetValue = offset ?? 0; // default to 0 if not provided
@@ -225,4 +237,63 @@ static ImageFormat GetImageFormat(string extension)
} }
Osudb.Instance.ToString(); Osudb.Instance.ToString();
startCloudflared();
Task.Run(() =>
{
Thread.Sleep(500);
if (!apiClient.LoadCookies())
{
Console.WriteLine("Please visit this link and paste the Value back into here: ");
var cookie = Console.ReadLine();
apiClient.SaveCookies(cookie);
}
Console.WriteLine("Ur Osu songs should now be available, please delete the cookies.json if it doesnt show up and try again.");
});
await apiClient.InitializeAsync();
app.Run(); app.Run();
async Task startCloudflared() {
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cloudflared",
Arguments = "tunnel --url http://localhost:5153",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.ErrorDataReceived += (sender, e) => {
if (!string.IsNullOrEmpty(e.Data))
{
ParseForUrls(e.Data);
}
};
process.Start();
process.BeginErrorReadLine();
await Task.Run(() => process.WaitForExit());
}
void ParseForUrls(string data)
{
var urlRegex = new Regex(@"https?://[^\s]*\.trycloudflare\.com");
var matches = urlRegex.Matches(data);
foreach (Match match in matches)
{
Console.WriteLine($"Login here if not done already: {match.Value}/login");
apiClient.UpdateSettingsAsync(match.Value);
}
}

4
backend/cookies.json Normal file
View File

@@ -0,0 +1,4 @@
{
"Name": "session_cookie",
"Value": "session_cookie_c_PhxGlrrYcWY1h9cQ6mu_LxsDIVu0u_Nv0qAK1WmvcZiZXQ_FcwXZZXDk5Tm0qrqZTHHs800Pv5bTMGIrfa8Q=="
}

View File

@@ -6,7 +6,7 @@ export type Song = {
url: string; url: string;
previewimage: string; previewimage: string;
mapper: string; mapper: string;
}; };
export type CollectionPreview = { export type CollectionPreview = {
index: number; index: number;
@@ -15,3 +15,10 @@ export type CollectionPreview = {
previewimage: string; previewimage: string;
}; };
export type Me = {
id: number;
name: string;
avatar_url: string;
endpoint: string;
share: boolean;
};

View File

@@ -1,10 +1,27 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { ref } from 'vue'; import { ref } from 'vue';
import type { Song, CollectionPreview } from '@/script/types'; import type { Song, CollectionPreview, Me } from '@/script/types';
export const useUserStore = defineStore('userStore', () => { export const useUserStore = defineStore('userStore', () => {
const userId = ref(null) const userId = ref(null)
const baseUrl = ref('https://service.illegalesachen.download/') const baseUrl = ref('https://service.illegalesachen.download/')
const proxyUrl = ref('https://proxy.illegalesachen.download/')
const User = ref<Me>(null)
function saveUser(user: Me) {
localStorage.setItem('activeUser', JSON.stringify(user));
}
function loadUser(): Me | null {
const user = localStorage.getItem('activeUser');
return user ? JSON.parse(user) : null;
}
function setUser(user: Me) {
User.value = user;
saveUser(user)
}
async function fetchSong(hash: string): Promise<Song> { async function fetchSong(hash: string): Promise<Song> {
try { try {
@@ -30,7 +47,7 @@ export const useUserStore = defineStore('userStore', () => {
} }
} }
async function fetchWithCache<T>(cacheKey: string, url: string, cacheDuration: number = 24 * 60 * 60 * 1000): Promise<T> { async function fetchWithCache<T>(cacheKey: string, url: string, cacheDuration: number = 24 * 60 * 60 * 1): Promise<T> {
const cacheTimestampKey = `${cacheKey}_timestamp`; const cacheTimestampKey = `${cacheKey}_timestamp`;
const cachedData = localStorage.getItem(cacheKey); const cachedData = localStorage.getItem(cacheKey);
@@ -96,5 +113,35 @@ export const useUserStore = defineStore('userStore', () => {
return fetchWithCache<Song[]>(cacheKey, url); return fetchWithCache<Song[]>(cacheKey, url);
} }
return { fetchSong, fetchActiveSearch, fetchSearchArtist, fetchCollections, fetchCollection, fetchRecent, fetchFavorites, userId, baseUrl } async function fetchMe(): Promise<Me | {}> {
const url = `${proxyUrl.value}me`;
try {
const response = await fetch(url, {
method: 'GET',
credentials: 'include'
});
console.log(response);
if (response.redirected) {
window.open(response.url, '_blank');
return { "redirected": true };
}
if (!response.ok) {
console.error(`Fetch failed with status: ${response.status} ${response.statusText}`);
return { id: -1 } as Me;
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch error:', error);
return {} as Me;
}
}
setUser(loadUser());
return { fetchSong, fetchActiveSearch, fetchSearchArtist, fetchCollections, fetchCollection, fetchRecent, fetchFavorites, fetchMe, userId, baseUrl, proxyUrl, User, setUser }
}) })

View File

@@ -12,6 +12,8 @@ const actionColor = ref('');
const infoColor = ref(''); const infoColor = ref('');
const borderColor = ref(''); const borderColor = ref('');
const loginStatus = ref('Login');
function update() { function update() {
var input = document.getElementById("url-input") as HTMLAudioElement; var input = document.getElementById("url-input") as HTMLAudioElement;
console.log(input.value) console.log(input.value)
@@ -34,6 +36,24 @@ function save(bg: string | null, main: string | null, info: string | null, borde
console.log("bg", bgColor.value, "action:", actionColor.value, "info", infoColor.value, "border", borderColor.value) console.log("bg", bgColor.value, "action:", actionColor.value, "info", infoColor.value, "border", borderColor.value)
} }
async function getMe() {
const data = await userStore.fetchMe() as Me;
if (data.redirected == true) {
loginStatus.value = "waiting for login, click to refresh!"
console.log("redirect detected");
}
console.log(data)
if (data.id === null || data.id === undefined || Object.keys(data).length === 0) {
return
}
console.log("active user: ", data.name)
userStore.setUser(data);
}
onMounted(() => { onMounted(() => {
reset(); reset();
}) })
@@ -69,12 +89,17 @@ function reset() {
<h1> Meeeeee </h1> <h1> Meeeeee </h1>
<input @change="update" type="text" id="url-input" :value="userStore.baseUrl" /> <input @change="update" type="text" id="url-input" :value="userStore.baseUrl" />
<br> <br>
<div class="flex p-5 justify-between"> <button v-if="!userStore.User" @click="getMe" class="border bordercolor rounded-lg p-0.5">{{ loginStatus }}</button>
<img :src="'https://a.ppy.sh/14100399'" class="w-1/3"> <div v-if="userStore.User" class="flex p-5 justify-between">
<img :src="userStore.User.avatar_url" class="w-1/3">
<div> <div>
<p>User: {{ 'JuLi0n_' }}</p> <p>{{ userStore.User.name }}</p>
<p>Api: Not Connected</p> <p>{{ userStore.User.endpoint == "" ? 'Not Connected' : 'Connected' }}</p>
<p>Sharing: <button class="border bordercolor rounded-lg p-0.5"> Off </button></p> <p>Sharing: <button @click="share" class="border bordercolor rounded-lg p-0.5">{{ userStore.User.share
}}</button></p>
<button @click="getMe" class="border bordercolor rounded-lg p-0.5"> Refresh
</button>
</div> </div>
</div> </div>

View File

@@ -86,8 +86,7 @@ func (c *OsuApiClient) sendRequest(req *http.Request, v interface{}) error {
return nil return nil
} }
func LoginRedirect(w http.ResponseWriter, r *http.Request) { func LoginMiddlePage(w http.ResponseWriter, r *http.Request) {
cookie, ok := r.Context().Value("cookie").(string) cookie, ok := r.Context().Value("cookie").(string)
if !ok || cookie == "" { if !ok || cookie == "" {
@@ -98,13 +97,32 @@ func LoginRedirect(w http.ResponseWriter, r *http.Request) {
var clientid = os.Getenv("CLIENT_ID") var clientid = os.Getenv("CLIENT_ID")
var redirect_uri = os.Getenv("REDIRECT_URI") + "/oauth/code" var redirect_uri = os.Getenv("REDIRECT_URI") + "/oauth/code"
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", html := `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Required</title>
</head>
<body>
<p>Redirecting...</p>
<a href="%s">Click here if ur not being Redirected!</a>
<script>
window.location.href = "%s";
</script>
</body>
</html>
`
loginURL := fmt.Sprintf("https://osu.ppy.sh/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s",
clientid, clientid,
redirect_uri, redirect_uri,
strings.Join(scopes, " "), strings.Join(scopes, " "),
cookie), cookie)
http.StatusTemporaryRedirect) fmt.Fprintf(w, html, loginURL, loginURL)
return
} }
func Oauth(w http.ResponseWriter, r *http.Request) { func Oauth(w http.ResponseWriter, r *http.Request) {
@@ -204,13 +222,41 @@ func Oauth(w http.ResponseWriter, r *http.Request) {
user.UserID = apiuser.ID user.UserID = apiuser.ID
user.Name = apiuser.Username user.Name = apiuser.Username
user.AvatarUrl = apiuser.AvatarURL user.AvatarUrl = apiuser.AvatarURL
user.Share = false
SaveCookie(user.UserID, cookie) SaveCookie(user.UserID, cookie)
if err = SaveUser(user); err != nil { if err = SaveUser(user); err != nil {
fmt.Println(err) fmt.Println(err)
} }
JSONResponse(w, http.StatusCreated, user) var html = fmt.Sprintf(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login Success</title>
<body>
<input type="password" value="%s" id="myInput" disabled>
<button onclick="copyToClipboard()">Copy Text</button>
<script>
function copyToClipboard() {
var copyText = document.getElementById("myInput");
copyText.select();
copyText.setSelectionRange(0, 99999); // For mobile devices
navigator.clipboard.writeText(copyText.value);
}
window.close(); // Close the window after copy
</script>
</head>
</html>
`, cookie)
fmt.Fprint(w, html)
return
} }
type AuthToken struct { type AuthToken struct {

Binary file not shown.

View File

@@ -14,6 +14,7 @@ type User struct {
Name string `json:"name"` Name string `json:"name"`
AvatarUrl string `json:"avatar_url"` AvatarUrl string `json:"avatar_url"`
EndPoint string `json:"endpoint"` EndPoint string `json:"endpoint"`
Share bool `json:"share"`
Token `json:"-"` Token `json:"-"`
} }
@@ -127,3 +128,9 @@ func UpdateUserEndPoint(userID int, endPoint string) error {
_, err := db.Exec(query, endPoint, userID) _, err := db.Exec(query, endPoint, userID)
return err return err
} }
func UpdateUserShare(userID int, sharing bool) error {
query := "UPDATE users SET sharing = ? WHERE id = ?"
_, err := db.Exec(query, sharing, userID)
return err
}

View File

@@ -20,7 +20,7 @@ func AuthMiddleware(next http.Handler) http.Handler {
user, err := GetUserByCookie(cookie.Value) user, err := GetUserByCookie(cookie.Value)
if err != nil || cookie.Value == "" { if err != nil || cookie.Value == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized) http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
return return
} }
@@ -43,10 +43,13 @@ func CookieMiddleware(next http.Handler) http.Handler {
HttpOnly: true, HttpOnly: true,
Secure: true, Secure: true,
Path: "/", Path: "/",
Domain: ".illegalesachen.download",
SameSite: http.SameSiteLaxMode,
} }
http.SetCookie(w, newCookie) http.SetCookie(w, newCookie)
cookie = newCookie cookie = newCookie
r.AddCookie(cookie)
} }
ctx := context.WithValue(r.Context(), "cookie", cookie.Value) ctx := context.WithValue(r.Context(), "cookie", cookie.Value)
@@ -54,6 +57,30 @@ func CookieMiddleware(next http.Handler) http.Handler {
}) })
} }
func CORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "https://music.illegalesachen.download")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Credentials", "true")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
func Logger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL)
next.ServeHTTP(w, r)
})
}
func generateRandomString(length int) string { func generateRandomString(length int) string {
bytes := make([]byte, length) bytes := make([]byte, length)
_, err := rand.Read(bytes) _, err := rand.Read(bytes)

View File

@@ -12,17 +12,29 @@ func run() error {
port := os.Getenv("PORT") port := os.Getenv("PORT")
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/me", AuthMiddleware(http.HandlerFunc(MeHandler))) mux.Handle("GET /me", AuthMiddleware(http.HandlerFunc(MeHandler)))
mux.Handle("/login", http.HandlerFunc(LoginRedirect)) mux.Handle("GET /login", http.HandlerFunc(LoginMiddlePage))
mux.Handle("GET /oauth/code", http.HandlerFunc(Oauth))
mux.Handle("POST /settings", AuthMiddleware(http.HandlerFunc(Settings)))
mux.Handle("/oauth/code", http.HandlerFunc(Oauth)) // mux.Handle("POST /setting", );
fmt.Println("Starting Server on", port) fmt.Println("Starting Server on", port)
//global middleware //global middleware
handler := CookieMiddleware(mux) handler := CORS(CookieMiddleware(Logger(mux)))
return http.ListenAndServe(port, handler) finalMux := http.NewServeMux()
finalMux.Handle("/", handler)
finalMux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
w.Write([]byte("pong"))
})
return http.ListenAndServe(port, finalMux)
} }
func MeHandler(w http.ResponseWriter, r *http.Request) { func MeHandler(w http.ResponseWriter, r *http.Request) {
@@ -37,6 +49,37 @@ func MeHandler(w http.ResponseWriter, r *http.Request) {
JSONResponse(w, http.StatusOK, user) JSONResponse(w, http.StatusOK, user)
} }
func Settings(w http.ResponseWriter, r *http.Request) {
type settings struct {
Sharing *bool `json:"sharing"`
Endpoint string `json:"endpoint"`
}
user, ok := r.Context().Value("user").(*User)
if !ok || user == nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
var s settings
if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if s.Endpoint != "" {
UpdateUserEndPoint(user.UserID, s.Endpoint)
}
if s.Sharing != nil {
UpdateUserShare(user.UserID, *s.Sharing)
}
w.WriteHeader(http.StatusOK)
return
}
func JSONResponse(w http.ResponseWriter, statusCode int, data interface{}) { func JSONResponse(w http.ResponseWriter, statusCode int, data interface{}) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")