The server for Open Course World
at main 256 lines 6.8 kB view raw
1package api 2 3import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "math/rand" 8 "net/http" 9 "smm2_gameserver/orm" 10 "strconv" 11 "strings" 12 13 "net/url" 14 user_state "smm2_gameserver/nex/connection/user" 15 "smm2_gameserver/nex/datastore" 16 "smm2_gameserver/nex/id" 17 18 "github.com/gorilla/mux" 19 // "gorm.io/gorm" 20) 21 22func parsePid(pidStr string) (datastore.Pid, error) { 23 pid, err := strconv.ParseUint(pidStr, 0, 64) 24 if err != nil { 25 pid, err = id.CourseIdToDataId(pidStr) 26 } 27 return datastore.Pid(pid), err 28} 29 30func parseDataId(idStr string) (uint64, error) { 31 dataId, err := strconv.ParseUint(idStr, 0, 64) 32 if err != nil { 33 dataId, err = id.CourseIdToDataId(idStr) 34 } 35 return dataId, err 36} 37 38func parseRange(values url.Values) (int, int, error) { 39 size := uint64(10) 40 offset := uint64(0) 41 42 var err error 43 _size := values.Get("size") 44 if _size != "" { 45 size, err = strconv.ParseUint(_size, 0, 64) 46 if err != nil { 47 return 0, 0, err 48 } 49 if size > 100 { 50 size = 100 51 } 52 } 53 54 _offset := values.Get("offset") 55 if _offset != "" { 56 offset, err = strconv.ParseUint(_offset, 0, 64) 57 if err != nil { 58 return 0, 0, err 59 } 60 } 61 62 return int(size), int(offset), nil 63} 64 65func stateForUser(user orm.User) *user_state.State { 66 return &user_state.State{Pid: uint64(user.ID)} 67} 68 69func initUser() { 70 Secure("/api/user", func(w http.ResponseWriter, r *http.Request, user orm.User) { 71 72 w.Header().Set("Content-Type", "application/json") 73 json.NewEncoder(w).Encode(user.View()) 74 }).Methods("GET") 75 76 Secure("/api/ocw-config.json", func(w http.ResponseWriter, r *http.Request, user orm.User) { 77 buf, err := dataView.GenerateModConfigForUserId(user.ID) 78 if err != nil { 79 reportError(w, r, err) 80 return 81 } 82 83 w.Header().Add("Content-Type", "application/octet-stream") 84 w.Write(buf) 85 }).Methods("GET") 86 87 Insecure("/api/ocw-mod.zip", func(w http.ResponseWriter, r *http.Request) { 88 http.Redirect(w, r, cfg.ModDownloadURL, http.StatusSeeOther) 89 }).Methods("GET") 90 91 Secure("/api/import_async", func(w http.ResponseWriter, r *http.Request, user orm.User) { 92 code := r.URL.Query().Get("maker_code") 93 if code == "" { 94 w.WriteHeader(500) 95 json.NewEncoder(w).Encode(map[string]any{"error": "maker_code not set"}) 96 return 97 } 98 code = strings.ToUpper(strings.ReplaceAll(code, "-", "")) 99 w.Header().Set("Content-Type", "application/json") 100 if ok, err := id.IsMakerId(code); !ok || err != nil { 101 w.WriteHeader(500) 102 json.NewEncoder(w).Encode(map[string]any{"error": "invalid maker_code"}) 103 return 104 } 105 106 err := dataView.AsyncCourseImportPostedBy(&user_state.State{Pid: uint64(user.ID)}, code) 107 if err != nil { 108 w.WriteHeader(500) 109 json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) 110 return 111 } 112 113 json.NewEncoder(w).Encode(map[string]any{"course_import": "running"}) 114 }).Methods("GET") 115 116 Secure("/api/create_and_import", func(w http.ResponseWriter, r *http.Request, user orm.User) { 117 if !cfg.EnableApiDevUser { 118 w.WriteHeader(500) 119 return 120 } 121 w.Header().Set("Content-Type", "application/json") 122 123 code := r.URL.Query().Get("maker_code") 124 if code == "" { 125 w.WriteHeader(500) 126 json.NewEncoder(w).Encode(map[string]any{"error": "maker_code not set"}) 127 return 128 } 129 code = strings.ToUpper(strings.ReplaceAll(code, "-", "")) 130 if ok, err := id.IsMakerId(code); !ok || err != nil { 131 w.WriteHeader(500) 132 json.NewEncoder(w).Encode(map[string]any{"error": "invalid maker_code"}) 133 return 134 } 135 136 name := r.URL.Query().Get("name") 137 if name == "" { 138 w.WriteHeader(500) 139 json.NewEncoder(w).Encode(map[string]any{"error": "name not set"}) 140 return 141 } 142 143 fakeHash := fmt.Sprintf("import-%d", rand.Intn(0xffffffff)) 144 newUser, err := CreateOrUpdateUser("import", fakeHash, name, "some-url") 145 if err != nil { 146 w.WriteHeader(500) 147 json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) 148 return 149 } 150 151 result := db.Model(newUser).Update("auth_token", fakeHash) 152 if result.Error != nil { 153 w.WriteHeader(500) 154 json.NewEncoder(w).Encode(map[string]any{"error": result.Error.Error()}) 155 return 156 } 157 158 if false { 159 err = dataView.RegisterUser(&user_state.State{AuthTokenHash: fakeHash}, datastore.RegisterUserParam{Name: newUser.Username}) 160 if err != nil { 161 w.WriteHeader(500) 162 json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) 163 return 164 } 165 } else { 166 // will overwrite name, but import nso username and mii data 167 } 168 169 err = dataView.AsyncCourseImportPostedBy(&user_state.State{AuthTokenHash: fakeHash}, code) 170 if err != nil { 171 w.WriteHeader(500) 172 json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) 173 return 174 } 175 176 json.NewEncoder(w).Encode(map[string]any{"course_import": "running"}) 177 }).Methods("GET") 178 179 Secure("/api/nso_users", func(w http.ResponseWriter, r *http.Request, user orm.User) { 180 nsoUsers, err := dataView.GetNsoUsers(datastore.Pid(user.ID)) 181 w.Header().Set("Content-Type", "application/json") 182 if err != nil { 183 json.NewEncoder(w).Encode(map[string]any{"error": err.Error()}) 184 return 185 } 186 187 json.NewEncoder(w).Encode(map[string]any{"nso_users": nsoUsers}) 188 }).Methods("GET") 189 190 // update course title and description 191 Secure("/api/update_course", func(w http.ResponseWriter, r *http.Request, user orm.User) { 192 code := r.URL.Query().Get("code") 193 if code == "" { 194 reportError(w, r, fmt.Errorf("no code parameter found")) 195 196 } 197 title := r.URL.Query().Get("title") 198 description := r.URL.Query().Get("description") 199 200 dataId, err := id.CourseIdToDataId(code) 201 if err != nil { 202 reportError(w, r, err) 203 return 204 } 205 206 var course orm.Course 207 result := db.First(&course, "data_id = ? AND uploader = ?", dataId, user.ID) 208 if result.Error != nil { 209 reportError(w, r, result.Error) 210 return 211 } 212 213 if title != "" { 214 course.Title = title 215 } 216 if description != "" { 217 course.Description = description 218 } 219 result = db.Save(&course) 220 if result.Error != nil { 221 reportError(w, r, result.Error) 222 return 223 } 224 }).Methods("POST") 225 226 Secure("/api/accept_eula", func(w http.ResponseWriter, r *http.Request, user orm.User) { 227 err := dataView.SetEulaAccepted(datastore.Pid(user.ID)) 228 if err != nil { 229 reportError(w, r, err) 230 return 231 } 232 }).Methods("POST") 233 234 Secure("/api/admin/user_info/{pid}", func(w http.ResponseWriter, r *http.Request, user orm.User) { 235 if !user.Role.Admin { 236 reportError(w, r, errors.New("you do not have admin permissions")) 237 return 238 } 239 240 pid, err := parsePid(mux.Vars(r)["pid"]) 241 if err != nil { 242 reportError(w, r, err) 243 return 244 } 245 246 var pidUser orm.User 247 result := db.Model(&orm.User{}).Where("id = ?", pid).First(&pidUser) 248 if result.Error != nil { 249 reportError(w, r, result.Error) 250 return 251 } 252 253 w.Header().Set("Content-Type", "application/json") 254 json.NewEncoder(w).Encode(pidUser) 255 }).Methods("GET") 256}