diff options
Diffstat (limited to 'backend-go/internal/api')
| -rw-r--r-- | backend-go/internal/api/setup.go | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/backend-go/internal/api/setup.go b/backend-go/internal/api/setup.go new file mode 100644 index 0000000..738deea --- /dev/null +++ b/backend-go/internal/api/setup.go @@ -0,0 +1,231 @@ +package api + +import ( +"bytes" +"compress/bzip2" +"encoding/base64" +"encoding/json" +"fmt" +"io" +"net/http" +"net/url" +"os" +"path/filepath" +"strings" +"time" +) + +const ( +baseURL = "https://www.fuzzwork.co.uk/dump/latest" +tokenURL = "https://login.eveonline.com/v2/oauth/token" +authURL = "https://login.eveonline.com/v2/oauth/authorize" +redirectURI = "https://localhost/callback/" +dataDirPath = "../data" +authFilePath = "./cache/auth.json" +envFilePath = "../../.env" +) + +var scopes = []string{ +"esi-assets.read_assets.v1", +"esi-location.read_location.v1", +"esi-location.read_online.v1", +"esi-wallet.read_character_wallet.v1", +"esi-ui.open_window.v1", +"esi-ui.write_waypoint.v1", +} + +var requiredFiles = map[string]string{ +"mapDenormalize.csv": fmt.Sprintf("%s/mapDenormalize.csv.bz2", baseURL), +"mapJumps.csv": fmt.Sprintf("%s/mapJumps.csv.bz2", baseURL), +"invTypes.csv": fmt.Sprintf("%s/invTypes.csv.bz2", baseURL), +"mapRegions.csv": fmt.Sprintf("%s/mapRegions.csv.bz2", baseURL), +} + +func StatusHandler(w http.ResponseWriter, r *http.Request) { + // Check if data is ready + dataReady := true + for fileName := range requiredFiles { + destPath := filepath.Join(dataDirPath, fileName) + if _, err := os.Stat(destPath); err != nil { + dataReady = false + break + } + } + + // Check if auth is ready + authReady := false + if _, err := os.Stat(authFilePath); err == nil { + authReady = true + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]bool{ + "data_ready": dataReady, + "auth_ready": authReady, + }) +} + +func DownloadDataHandler(w http.ResponseWriter, r *http.Request) { +if err := os.MkdirAll(dataDirPath, os.ModePerm); err != nil { +http.Error(w, fmt.Sprintf("Failed to create data dir: %v", err), http.StatusInternalServerError) +return +} + +for fileName, downloadURL := range requiredFiles { +destPath := filepath.Join(dataDirPath, fileName) +if _, err := os.Stat(destPath); err == nil { +fmt.Printf("Skipping %s; already exists.\n", fileName) +continue +} + +fmt.Printf("Downloading %s from %s\n", fileName, downloadURL) +resp, err := http.Get(downloadURL) +if err != nil || resp.StatusCode != 200 { +http.Error(w, fmt.Sprintf("Failed to download %s", fileName), http.StatusInternalServerError) +if resp != nil { +resp.Body.Close() +} +return +} + +out, err := os.Create(destPath) +if err != nil { +http.Error(w, "Failed to create destination file", http.StatusInternalServerError) +resp.Body.Close() +return +} + +bzReader := bzip2.NewReader(resp.Body) +if _, err := io.Copy(out, bzReader); err != nil { +http.Error(w, "Decompression failed", http.StatusInternalServerError) +out.Close() +resp.Body.Close() +return +} + +out.Close() +resp.Body.Close() +fmt.Printf("Wrote %s\n", destPath) +} + +w.Header().Set("Content-Type", "application/json") +w.WriteHeader(http.StatusOK) +w.Write([]byte(`{"message": "EVE static data is ready."}`)) +} + +func GetAuthURLHandler(w http.ResponseWriter, r *http.Request) { +clientID := os.Getenv("CLIENT_ID") +if clientID == "" { +http.Error(w, "CLIENT_ID not set in environment.", http.StatusInternalServerError) +return +} + +queryParams := url.Values{} +queryParams.Add("response_type", "code") +queryParams.Add("redirect_uri", redirectURI) +queryParams.Add("client_id", clientID) +queryParams.Add("state", "haul-local") +queryParams.Add("scope", strings.Join(scopes, " ")) + +fullURL := fmt.Sprintf("%s?%s", authURL, queryParams.Encode()) +w.Header().Set("Content-Type", "application/json") +json.NewEncoder(w).Encode(map[string]string{"url": fullURL}) +} + +func CompleteAuthHandler(w http.ResponseWriter, r *http.Request) { +var request struct { +Code string `json:"code"` +} +if err := json.NewDecoder(r.Body).Decode(&request); err != nil || request.Code == "" { +http.Error(w, "Valid 'code' is required.", http.StatusBadRequest) +return +} + +clientID := os.Getenv("CLIENT_ID") +secretKey := os.Getenv("SECRET_KEY") + +if clientID == "" || secretKey == "" { +http.Error(w, "Missing CLIENT_ID or SECRET_KEY", http.StatusInternalServerError) +return +} + +formValues := url.Values{} +formValues.Set("grant_type", "authorization_code") +formValues.Set("code", request.Code) + +req, err := http.NewRequest("POST", tokenURL, strings.NewReader(formValues.Encode())) +if err != nil { +http.Error(w, "Failed to create request.", http.StatusInternalServerError) +return +} + +userPass := clientID + ":" + secretKey +encodedAuth := base64.StdEncoding.EncodeToString([]byte(userPass)) +req.Header.Add("Authorization", fmt.Sprintf("Basic %s", encodedAuth)) +req.Header.Add("Content-Type", "application/x-www-form-urlencoded") +req.Header.Add("Host", "login.eveonline.com") + +client := &http.Client{Timeout: 30 * time.Second} +resp, err := client.Do(req) +if err != nil || resp.StatusCode >= 400 { +http.Error(w, "Failed to swap token", http.StatusBadGateway) +return +} +defer resp.Body.Close() + +var authData map[string]interface{} +if err := json.NewDecoder(resp.Body).Decode(&authData); err != nil { +http.Error(w, "Failed to decode ESI response", http.StatusInternalServerError) +return +} + +expiresIn := authData["expires_in"].(float64) +authData["expires_at"] = time.Now().UTC().Unix() + int64(expiresIn) + +os.MkdirAll(filepath.Dir(authFilePath), os.ModePerm) +cacheData, _ := json.MarshalIndent(authData, "", " ") +os.WriteFile(authFilePath, cacheData, 0644) + +tokenStr, _ := authData["access_token"].(string) +charInfo := extractJWTSubject(tokenStr) +appendEnvValue("character_id", charInfo) + +w.Header().Set("Content-Type", "application/json") +w.WriteHeader(http.StatusOK) +w.Write([]byte(`{"message": "Auth successful, cache built."}`)) +} + +func extractJWTSubject(token string) string { +parts := strings.Split(token, ".") +if len(parts) < 2 { +return "unknown" +} +payload, err := base64.RawURLEncoding.DecodeString(parts[1]) +if err != nil { +return "unknown" +} + +var claims map[string]interface{} +if err := json.Unmarshal(payload, &claims); err != nil { +return "unknown" +} + +if sub, ok := claims["sub"].(string); ok { +subParts := strings.Split(sub, ":") +return subParts[len(subParts)-1] +} +return "unknown" +} + +func appendEnvValue(key, value string) { +content, err := os.ReadFile(envFilePath) +if err != nil { +content = []byte{} +} + +if !bytes.Contains(content, []byte(key+"=")) { +f, _ := os.OpenFile(envFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) +defer f.Close() +f.WriteString(fmt.Sprintf("\n%s=%s\n", key, value)) +} +} |
