package api import ( "encoding/json" "errors" "fmt" "math/rand" "net/http" "smm2_gameserver/orm" "strconv" "strings" "net/url" user_state "smm2_gameserver/nex/connection/user" "smm2_gameserver/nex/datastore" "smm2_gameserver/nex/id" "github.com/gorilla/mux" // "gorm.io/gorm" ) func parsePid(pidStr string) (datastore.Pid, error) { pid, err := strconv.ParseUint(pidStr, 0, 64) if err != nil { pid, err = id.CourseIdToDataId(pidStr) } return datastore.Pid(pid), err } func parseDataId(idStr string) (uint64, error) { dataId, err := strconv.ParseUint(idStr, 0, 64) if err != nil { dataId, err = id.CourseIdToDataId(idStr) } return dataId, err } func parseRange(values url.Values) (int, int, error) { size := uint64(10) offset := uint64(0) var err error _size := values.Get("size") if _size != "" { size, err = strconv.ParseUint(_size, 0, 64) if err != nil { return 0, 0, err } if size > 100 { size = 100 } } _offset := values.Get("offset") if _offset != "" { offset, err = strconv.ParseUint(_offset, 0, 64) if err != nil { return 0, 0, err } } return int(size), int(offset), nil } func stateForUser(user orm.User) *user_state.State { return &user_state.State{Pid: uint64(user.ID)} } func initUser() { Secure("/api/user", func(w http.ResponseWriter, r *http.Request, user orm.User) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(user.View()) }).Methods("GET") Secure("/api/ocw-config.json", func(w http.ResponseWriter, r *http.Request, user orm.User) { buf, err := dataView.GenerateModConfigForUserId(user.ID) if err != nil { reportError(w, r, err) return } w.Header().Add("Content-Type", "application/octet-stream") w.Write(buf) }).Methods("GET") Insecure("/api/ocw-mod.zip", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, cfg.ModDownloadURL, http.StatusSeeOther) }).Methods("GET") Secure("/api/import_async", func(w http.ResponseWriter, r *http.Request, user orm.User) { code := r.URL.Query().Get("maker_code") if code == "" { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": "maker_code not set"}) return } code = strings.ToUpper(strings.ReplaceAll(code, "-", "")) w.Header().Set("Content-Type", "application/json") if ok, err := id.IsMakerId(code); !ok || err != nil { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": "invalid maker_code"}) return } err := dataView.AsyncCourseImportPostedBy(&user_state.State{Pid: uint64(user.ID)}, code) if err != nil { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) return } json.NewEncoder(w).Encode(map[string]any{"course_import": "running"}) }).Methods("GET") Secure("/api/create_and_import", func(w http.ResponseWriter, r *http.Request, user orm.User) { if !cfg.EnableApiDevUser { w.WriteHeader(500) return } w.Header().Set("Content-Type", "application/json") code := r.URL.Query().Get("maker_code") if code == "" { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": "maker_code not set"}) return } code = strings.ToUpper(strings.ReplaceAll(code, "-", "")) if ok, err := id.IsMakerId(code); !ok || err != nil { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": "invalid maker_code"}) return } name := r.URL.Query().Get("name") if name == "" { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": "name not set"}) return } fakeHash := fmt.Sprintf("import-%d", rand.Intn(0xffffffff)) newUser, err := CreateOrUpdateUser("import", fakeHash, name, "some-url") if err != nil { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) return } result := db.Model(newUser).Update("auth_token", fakeHash) if result.Error != nil { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": result.Error.Error()}) return } if false { err = dataView.RegisterUser(&user_state.State{AuthTokenHash: fakeHash}, datastore.RegisterUserParam{Name: newUser.Username}) if err != nil { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) return } } else { // will overwrite name, but import nso username and mii data } err = dataView.AsyncCourseImportPostedBy(&user_state.State{AuthTokenHash: fakeHash}, code) if err != nil { w.WriteHeader(500) json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) return } json.NewEncoder(w).Encode(map[string]any{"course_import": "running"}) }).Methods("GET") Secure("/api/nso_users", func(w http.ResponseWriter, r *http.Request, user orm.User) { nsoUsers, err := dataView.GetNsoUsers(datastore.Pid(user.ID)) w.Header().Set("Content-Type", "application/json") if err != nil { json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) return } json.NewEncoder(w).Encode(map[string]any{"nso_users": nsoUsers}) }).Methods("GET") // update course title and description Secure("/api/update_course", func(w http.ResponseWriter, r *http.Request, user orm.User) { code := r.URL.Query().Get("code") if code == "" { reportError(w, r, fmt.Errorf("no code parameter found")) } title := r.URL.Query().Get("title") description := r.URL.Query().Get("description") dataId, err := id.CourseIdToDataId(code) if err != nil { reportError(w, r, err) return } var course orm.Course result := db.First(&course, "data_id = ? AND uploader = ?", dataId, user.ID) if result.Error != nil { reportError(w, r, result.Error) return } if title != "" { course.Title = title } if description != "" { course.Description = description } result = db.Save(&course) if result.Error != nil { reportError(w, r, result.Error) return } }).Methods("POST") Secure("/api/accept_eula", func(w http.ResponseWriter, r *http.Request, user orm.User) { err := dataView.SetEulaAccepted(datastore.Pid(user.ID)) if err != nil { reportError(w, r, err) return } }).Methods("POST") Secure("/api/admin/user_info/{pid}", func(w http.ResponseWriter, r *http.Request, user orm.User) { if !user.Role.Admin { reportError(w, r, errors.New("you do not have admin permissions")) return } pid, err := parsePid(mux.Vars(r)["pid"]) if err != nil { reportError(w, r, err) return } var pidUser orm.User result := db.Model(&orm.User{}).Where("id = ?", pid).First(&pidUser) if result.Error != nil { reportError(w, r, result.Error) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(pidUser) }).Methods("GET") }