The server for Open Course World
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}