home to your local SPACEGIRL 💫 arimelody.space
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

very rough updates to admin pages, reduced reliance on global.DB

+192 -106
+38
admin/accounthttp.go
··· 1 + package admin 2 + 3 + import ( 4 + "fmt" 5 + "net/http" 6 + 7 + "arimelody-web/controller" 8 + "arimelody-web/model" 9 + 10 + "github.com/jmoiron/sqlx" 11 + ) 12 + 13 + func AccountHandler(db *sqlx.DB) http.Handler { 14 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 15 + account := r.Context().Value("account").(*model.Account) 16 + 17 + totps, err := controller.GetTOTPsForAccount(db, account.ID) 18 + if err != nil { 19 + fmt.Printf("WARN: Failed to fetch TOTPs: %v\n", err) 20 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 21 + } 22 + 23 + type AccountResponse struct { 24 + Account *model.Account 25 + TOTPs []model.TOTP 26 + } 27 + 28 + err = pages["account"].Execute(w, AccountResponse{ 29 + Account: account, 30 + TOTPs: totps, 31 + }) 32 + if err != nil { 33 + fmt.Printf("WARN: Failed to render admin account page: %v\n", err) 34 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 35 + } 36 + }) 37 + } 38 +
+26 -26
admin/http.go
··· 22 22 Token string 23 23 } 24 24 25 - func Handler() http.Handler { 25 + func Handler(db *sqlx.DB) http.Handler { 26 26 mux := http.NewServeMux() 27 27 28 - mux.Handle("/login", LoginHandler()) 29 - mux.Handle("/register", createAccountHandler()) 30 - mux.Handle("/logout", RequireAccount(global.DB, LogoutHandler())) 31 - // TODO: /admin/account 28 + mux.Handle("/login", LoginHandler(db)) 29 + mux.Handle("/register", createAccountHandler(db)) 30 + mux.Handle("/logout", RequireAccount(db, LogoutHandler(db))) 31 + mux.Handle("/account", RequireAccount(db, AccountHandler(db))) 32 32 mux.Handle("/static/", http.StripPrefix("/static", staticHandler())) 33 - mux.Handle("/release/", RequireAccount(global.DB, http.StripPrefix("/release", serveRelease()))) 34 - mux.Handle("/artist/", RequireAccount(global.DB, http.StripPrefix("/artist", serveArtist()))) 35 - mux.Handle("/track/", RequireAccount(global.DB, http.StripPrefix("/track", serveTrack()))) 33 + mux.Handle("/release/", RequireAccount(db, http.StripPrefix("/release", serveRelease()))) 34 + mux.Handle("/artist/", RequireAccount(db, http.StripPrefix("/artist", serveArtist()))) 35 + mux.Handle("/track/", RequireAccount(db, http.StripPrefix("/track", serveTrack()))) 36 36 mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 37 37 if r.URL.Path != "/" { 38 38 http.NotFound(w, r) 39 39 return 40 40 } 41 41 42 - account, err := controller.GetAccountByRequest(global.DB, r) 42 + account, err := controller.GetAccountByRequest(db, r) 43 43 if err != nil { 44 44 fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %s\n", err) 45 45 } ··· 48 48 return 49 49 } 50 50 51 - releases, err := controller.GetAllReleases(global.DB, false, 0, true) 51 + releases, err := controller.GetAllReleases(db, false, 0, true) 52 52 if err != nil { 53 53 fmt.Fprintf(os.Stderr, "WARN: Failed to pull releases: %s\n", err) 54 54 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 55 55 return 56 56 } 57 57 58 - artists, err := controller.GetAllArtists(global.DB) 58 + artists, err := controller.GetAllArtists(db) 59 59 if err != nil { 60 60 fmt.Fprintf(os.Stderr, "WARN: Failed to pull artists: %s\n", err) 61 61 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 62 62 return 63 63 } 64 64 65 - tracks, err := controller.GetOrphanTracks(global.DB) 65 + tracks, err := controller.GetOrphanTracks(db) 66 66 if err != nil { 67 67 fmt.Fprintf(os.Stderr, "WARN: Failed to pull orphan tracks: %s\n", err) 68 68 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) ··· 112 112 }) 113 113 } 114 114 115 - func LoginHandler() http.Handler { 115 + func LoginHandler(db *sqlx.DB) http.Handler { 116 116 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 117 117 if r.Method == http.MethodGet { 118 - account, err := controller.GetAccountByRequest(global.DB, r) 118 + account, err := controller.GetAccountByRequest(db, r) 119 119 if err != nil { 120 120 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 121 121 fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %v\n", err) ··· 157 157 TOTP: r.Form.Get("totp"), 158 158 } 159 159 160 - account, err := controller.GetAccount(global.DB, credentials.Username) 160 + account, err := controller.GetAccount(db, credentials.Username) 161 161 if err != nil { 162 162 http.Error(w, "Invalid username or password", http.StatusBadRequest) 163 163 return ··· 176 176 // TODO: check TOTP 177 177 178 178 // login success! 179 - token, err := controller.CreateToken(global.DB, account.ID, r.UserAgent()) 179 + token, err := controller.CreateToken(db, account.ID, r.UserAgent()) 180 180 if err != nil { 181 181 fmt.Fprintf(os.Stderr, "WARN: Failed to create token: %v\n", err) 182 182 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) ··· 206 206 }) 207 207 } 208 208 209 - func LogoutHandler() http.Handler { 209 + func LogoutHandler(db *sqlx.DB) http.Handler { 210 210 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 211 211 if r.Method != http.MethodGet { 212 212 http.NotFound(w, r) 213 213 return 214 214 } 215 215 216 - tokenStr := controller.GetTokenFromRequest(global.DB, r) 216 + tokenStr := controller.GetTokenFromRequest(db, r) 217 217 218 218 if len(tokenStr) > 0 { 219 - err := controller.DeleteToken(global.DB, tokenStr) 219 + err := controller.DeleteToken(db, tokenStr) 220 220 if err != nil { 221 221 fmt.Fprintf(os.Stderr, "WARN: Failed to revoke token: %v\n", err) 222 222 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) ··· 238 238 }) 239 239 } 240 240 241 - func createAccountHandler() http.Handler { 241 + func createAccountHandler(db *sqlx.DB) http.Handler { 242 242 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 243 - checkAccount, err := controller.GetAccountByRequest(global.DB, r) 243 + checkAccount, err := controller.GetAccountByRequest(db, r) 244 244 if err != nil { 245 245 fmt.Printf("WARN: Failed to fetch account: %s\n", err) 246 246 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) ··· 297 297 } 298 298 299 299 // make sure code exists in DB 300 - invite, err := controller.GetInvite(global.DB, credentials.Invite) 300 + invite, err := controller.GetInvite(db, credentials.Invite) 301 301 if err != nil { 302 302 fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve invite: %v\n", err) 303 303 render(CreateAccountResponse{ ··· 307 307 } 308 308 if invite == nil || time.Now().After(invite.ExpiresAt) { 309 309 if invite != nil { 310 - err := controller.DeleteInvite(global.DB, invite.Code) 310 + err := controller.DeleteInvite(db, invite.Code) 311 311 if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) } 312 312 } 313 313 render(CreateAccountResponse{ ··· 331 331 Email: credentials.Email, 332 332 AvatarURL: "/img/default-avatar.png", 333 333 } 334 - err = controller.CreateAccount(global.DB, &account) 334 + err = controller.CreateAccount(db, &account) 335 335 if err != nil { 336 336 if strings.HasPrefix(err.Error(), "pq: duplicate key") { 337 337 render(CreateAccountResponse{ ··· 346 346 return 347 347 } 348 348 349 - err = controller.DeleteInvite(global.DB, invite.Code) 349 + err = controller.DeleteInvite(db, invite.Code) 350 350 if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to delete expired invite: %v\n", err) } 351 351 352 352 // registration success! 353 - token, err := controller.CreateToken(global.DB, account.ID, r.UserAgent()) 353 + token, err := controller.CreateToken(db, account.ID, r.UserAgent()) 354 354 if err != nil { 355 355 fmt.Fprintf(os.Stderr, "WARN: Failed to create token: %v\n", err) 356 356 // gracefully redirect user to login page
+41
admin/static/edit-account.css
··· 1 + @import url("/admin/static/index.css"); 2 + 3 + form#change-password { 4 + width: 100%; 5 + display: flex; 6 + flex-direction: column; 7 + align-items: start; 8 + } 9 + 10 + form div { 11 + width: 20rem; 12 + } 13 + 14 + form button { 15 + margin-top: 1rem; 16 + } 17 + 18 + label { 19 + width: 100%; 20 + margin: 1rem 0 .5rem 0; 21 + display: block; 22 + color: #10101080; 23 + } 24 + input { 25 + width: 100%; 26 + margin: .5rem 0; 27 + padding: .3rem .5rem; 28 + display: block; 29 + border-radius: 4px; 30 + border: 1px solid #808080; 31 + font-size: inherit; 32 + font-family: inherit; 33 + color: inherit; 34 + } 35 + 36 + #error { 37 + background: #ffa9b8; 38 + border: 1px solid #dc5959; 39 + padding: 1em; 40 + border-radius: 4px; 41 + }
+45
admin/static/index.css
··· 99 99 opacity: 0.75; 100 100 } 101 101 102 + 103 + 104 + button, .button { 105 + padding: .5em .8em; 106 + font-family: inherit; 107 + font-size: inherit; 108 + border-radius: .5em; 109 + border: 1px solid #a0a0a0; 110 + background: #f0f0f0; 111 + color: inherit; 112 + } 113 + button:hover, .button:hover { 114 + background: #fff; 115 + border-color: #d0d0d0; 116 + } 117 + button:active, .button:active { 118 + background: #d0d0d0; 119 + border-color: #808080; 120 + } 121 + 122 + button { 123 + color: inherit; 124 + } 125 + button.save { 126 + background: #6fd7ff; 127 + border-color: #6f9eb0; 128 + } 129 + button.delete { 130 + background: #ff7171; 131 + border-color: #7d3535; 132 + } 133 + button:hover { 134 + background: #fff; 135 + border-color: #d0d0d0; 136 + } 137 + button:active { 138 + background: #d0d0d0; 139 + border-color: #808080; 140 + } 141 + button[disabled] { 142 + background: #d0d0d0 !important; 143 + border-color: #808080 !important; 144 + opacity: .5; 145 + cursor: not-allowed !important; 146 + }
+5
admin/templates.go
··· 28 28 filepath.Join("views", "prideflag.html"), 29 29 filepath.Join("admin", "views", "logout.html"), 30 30 )), 31 + "account": template.Must(template.ParseFiles( 32 + filepath.Join("admin", "views", "layout.html"), 33 + filepath.Join("views", "prideflag.html"), 34 + filepath.Join("admin", "views", "edit-account.html"), 35 + )), 31 36 32 37 "release": template.Must(template.ParseFiles( 33 38 filepath.Join("admin", "views", "layout.html"),
+1 -23
admin/views/create-account.html
··· 1 1 {{define "head"}} 2 2 <title>Register - ari melody 💫</title> 3 3 <link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon"> 4 - 4 + <link rel="stylesheet" href="/admin/static/index.css"> 5 5 <style> 6 6 p a { 7 7 color: #2a67c8; ··· 42 42 font-size: inherit; 43 43 font-family: inherit; 44 44 color: inherit; 45 - } 46 - 47 - button { 48 - padding: .5em .8em; 49 - font-family: inherit; 50 - font-size: inherit; 51 - border-radius: .5em; 52 - border: 1px solid #a0a0a0; 53 - background: #f0f0f0; 54 - color: inherit; 55 - } 56 - button.new { 57 - background: #c4ff6a; 58 - border-color: #84b141; 59 - } 60 - button:hover { 61 - background: #fff; 62 - border-color: #d0d0d0; 63 - } 64 - button:active { 65 - background: #d0d0d0; 66 - border-color: #808080; 67 45 } 68 46 69 47 #error {
+3 -2
admin/views/edit-account.html
··· 1 1 {{define "head"}} 2 2 <title>Account Settings - ari melody 💫</title> 3 3 <link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon"> 4 - <link rel="stylesheet" href="/admin/static/index.css"> 4 + <link rel="stylesheet" href="/admin/static/edit-account.css"> 5 5 {{end}} 6 6 7 7 {{define "content"}} ··· 10 10 11 11 <div class="card-title"> 12 12 <h2>Change Password</h2> 13 - 13 + </div> 14 + <div class="card"> 14 15 <form action="/api/v1/change-password" method="POST" id="change-password"> 15 16 <div> 16 17 <label for="current-password">Current Password</label>
-1
admin/views/edit-artist.html
··· 1 1 {{define "head"}} 2 2 <title>Editing {{.Artist.Name}} - ari melody 💫</title> 3 3 <link rel="shortcut icon" href="{{.Artist.GetAvatar}}" type="image/x-icon"> 4 - 5 4 <link rel="stylesheet" href="/admin/static/edit-artist.css"> 6 5 {{end}} 7 6
-1
admin/views/edit-release.html
··· 1 1 {{define "head"}} 2 2 <title>Editing {{.Release.Title}} - ari melody 💫</title> 3 3 <link rel="shortcut icon" href="{{.Release.GetArtwork}}" type="image/x-icon"> 4 - 5 4 <link rel="stylesheet" href="/admin/static/edit-release.css"> 6 5 {{end}} 7 6
+1 -1
admin/views/edit-track.html
··· 1 1 {{define "head"}} 2 2 <title>Editing Track - ari melody 💫</title> 3 - 3 + <link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon"> 4 4 <link rel="stylesheet" href="/admin/static/edit-track.css"> 5 5 {{end}} 6 6
-22
admin/views/login.html
··· 47 47 opacity: .5; 48 48 cursor: not-allowed; 49 49 } 50 - 51 - button { 52 - padding: .5em .8em; 53 - font-family: inherit; 54 - font-size: inherit; 55 - border-radius: .5em; 56 - border: 1px solid #a0a0a0; 57 - background: #f0f0f0; 58 - color: inherit; 59 - } 60 - button.save { 61 - background: #6fd7ff; 62 - border-color: #6f9eb0; 63 - } 64 - button:hover { 65 - background: #fff; 66 - border-color: #d0d0d0; 67 - } 68 - button:active { 69 - background: #d0d0d0; 70 - border-color: #808080; 71 - } 72 50 </style> 73 51 {{end}} 74 52
+17 -16
api/api.go
··· 6 6 "strings" 7 7 8 8 "arimelody-web/admin" 9 - "arimelody-web/global" 10 9 "arimelody-web/controller" 10 + 11 + "github.com/jmoiron/sqlx" 11 12 ) 12 13 13 - func Handler() http.Handler { 14 + func Handler(db *sqlx.DB) http.Handler { 14 15 mux := http.NewServeMux() 15 16 16 17 // ACCOUNT ENDPOINTS ··· 31 32 32 33 mux.Handle("/v1/artist/", http.StripPrefix("/v1/artist", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 33 34 var artistID = strings.Split(r.URL.Path[1:], "/")[0] 34 - artist, err := controller.GetArtist(global.DB, artistID) 35 + artist, err := controller.GetArtist(db, artistID) 35 36 if err != nil { 36 37 if strings.Contains(err.Error(), "no rows") { 37 38 http.NotFound(w, r) ··· 48 49 ServeArtist(artist).ServeHTTP(w, r) 49 50 case http.MethodPut: 50 51 // PUT /api/v1/artist/{id} (admin) 51 - admin.RequireAccount(global.DB, UpdateArtist(artist)).ServeHTTP(w, r) 52 + admin.RequireAccount(db, UpdateArtist(artist)).ServeHTTP(w, r) 52 53 case http.MethodDelete: 53 54 // DELETE /api/v1/artist/{id} (admin) 54 - admin.RequireAccount(global.DB, DeleteArtist(artist)).ServeHTTP(w, r) 55 + admin.RequireAccount(db, DeleteArtist(artist)).ServeHTTP(w, r) 55 56 default: 56 57 http.NotFound(w, r) 57 58 } ··· 63 64 ServeAllArtists().ServeHTTP(w, r) 64 65 case http.MethodPost: 65 66 // POST /api/v1/artist (admin) 66 - admin.RequireAccount(global.DB, CreateArtist()).ServeHTTP(w, r) 67 + admin.RequireAccount(db, CreateArtist()).ServeHTTP(w, r) 67 68 default: 68 69 http.NotFound(w, r) 69 70 } ··· 73 74 74 75 mux.Handle("/v1/music/", http.StripPrefix("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 75 76 var releaseID = strings.Split(r.URL.Path[1:], "/")[0] 76 - release, err := controller.GetRelease(global.DB, releaseID, true) 77 + release, err := controller.GetRelease(db, releaseID, true) 77 78 if err != nil { 78 79 if strings.Contains(err.Error(), "no rows") { 79 80 http.NotFound(w, r) ··· 90 91 ServeRelease(release).ServeHTTP(w, r) 91 92 case http.MethodPut: 92 93 // PUT /api/v1/music/{id} (admin) 93 - admin.RequireAccount(global.DB, UpdateRelease(release)).ServeHTTP(w, r) 94 + admin.RequireAccount(db, UpdateRelease(release)).ServeHTTP(w, r) 94 95 case http.MethodDelete: 95 96 // DELETE /api/v1/music/{id} (admin) 96 - admin.RequireAccount(global.DB, DeleteRelease(release)).ServeHTTP(w, r) 97 + admin.RequireAccount(db, DeleteRelease(release)).ServeHTTP(w, r) 97 98 default: 98 99 http.NotFound(w, r) 99 100 } ··· 105 106 ServeCatalog().ServeHTTP(w, r) 106 107 case http.MethodPost: 107 108 // POST /api/v1/music (admin) 108 - admin.RequireAccount(global.DB, CreateRelease()).ServeHTTP(w, r) 109 + admin.RequireAccount(db, CreateRelease()).ServeHTTP(w, r) 109 110 default: 110 111 http.NotFound(w, r) 111 112 } ··· 115 116 116 117 mux.Handle("/v1/track/", http.StripPrefix("/v1/track", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 117 118 var trackID = strings.Split(r.URL.Path[1:], "/")[0] 118 - track, err := controller.GetTrack(global.DB, trackID) 119 + track, err := controller.GetTrack(db, trackID) 119 120 if err != nil { 120 121 if strings.Contains(err.Error(), "no rows") { 121 122 http.NotFound(w, r) ··· 129 130 switch r.Method { 130 131 case http.MethodGet: 131 132 // GET /api/v1/track/{id} (admin) 132 - admin.RequireAccount(global.DB, ServeTrack(track)).ServeHTTP(w, r) 133 + admin.RequireAccount(db, ServeTrack(track)).ServeHTTP(w, r) 133 134 case http.MethodPut: 134 135 // PUT /api/v1/track/{id} (admin) 135 - admin.RequireAccount(global.DB, UpdateTrack(track)).ServeHTTP(w, r) 136 + admin.RequireAccount(db, UpdateTrack(track)).ServeHTTP(w, r) 136 137 case http.MethodDelete: 137 138 // DELETE /api/v1/track/{id} (admin) 138 - admin.RequireAccount(global.DB, DeleteTrack(track)).ServeHTTP(w, r) 139 + admin.RequireAccount(db, DeleteTrack(track)).ServeHTTP(w, r) 139 140 default: 140 141 http.NotFound(w, r) 141 142 } ··· 144 145 switch r.Method { 145 146 case http.MethodGet: 146 147 // GET /api/v1/track (admin) 147 - admin.RequireAccount(global.DB, ServeAllTracks()).ServeHTTP(w, r) 148 + admin.RequireAccount(db, ServeAllTracks()).ServeHTTP(w, r) 148 149 case http.MethodPost: 149 150 // POST /api/v1/track (admin) 150 - admin.RequireAccount(global.DB, CreateTrack()).ServeHTTP(w, r) 151 + admin.RequireAccount(db, CreateTrack()).ServeHTTP(w, r) 151 152 default: 152 153 http.NotFound(w, r) 153 154 }
+2 -2
api/track.go
··· 126 126 127 127 err = controller.UpdateTrack(global.DB, track) 128 128 if err != nil { 129 - fmt.Printf("Failed to update track %s: %s\n", track.ID, err) 129 + fmt.Printf("WARN: Failed to update track %s: %s\n", track.ID, err) 130 130 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 131 131 return 132 132 } ··· 151 151 var trackID = r.URL.Path[1:] 152 152 err := controller.DeleteTrack(global.DB, trackID) 153 153 if err != nil { 154 - fmt.Printf("Failed to delete track %s: %s\n", trackID, err) 154 + fmt.Printf("WARN: Failed to delete track %s: %s\n", trackID, err) 155 155 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 156 156 } 157 157 })
+3 -3
main.go
··· 341 341 func createServeMux() *http.ServeMux { 342 342 mux := http.NewServeMux() 343 343 344 - mux.Handle("/admin/", http.StripPrefix("/admin", admin.Handler())) 345 - mux.Handle("/api/", http.StripPrefix("/api", api.Handler())) 346 - mux.Handle("/music/", http.StripPrefix("/music", view.MusicHandler())) 344 + mux.Handle("/admin/", http.StripPrefix("/admin", admin.Handler(global.DB))) 345 + mux.Handle("/api/", http.StripPrefix("/api", api.Handler(global.DB))) 346 + mux.Handle("/music/", http.StripPrefix("/music", view.MusicHandler(global.DB))) 347 347 mux.Handle("/uploads/", http.StripPrefix("/uploads", staticHandler(filepath.Join(global.Config.DataDirectory, "uploads")))) 348 348 mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 349 349 if r.Method == http.MethodHead {
+10 -9
view/music.go
··· 6 6 "os" 7 7 8 8 "arimelody-web/controller" 9 - "arimelody-web/global" 10 9 "arimelody-web/model" 11 10 "arimelody-web/templates" 11 + 12 + "github.com/jmoiron/sqlx" 12 13 ) 13 14 14 15 // HTTP HANDLER METHODS 15 16 16 - func MusicHandler() http.Handler { 17 + func MusicHandler(db *sqlx.DB) http.Handler { 17 18 mux := http.NewServeMux() 18 19 19 20 mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 20 21 if r.URL.Path == "/" { 21 - ServeCatalog().ServeHTTP(w, r) 22 + ServeCatalog(db).ServeHTTP(w, r) 22 23 return 23 24 } 24 25 25 - release, err := controller.GetRelease(global.DB, r.URL.Path[1:], true) 26 + release, err := controller.GetRelease(db, r.URL.Path[1:], true) 26 27 if err != nil { 27 28 http.NotFound(w, r) 28 29 return 29 30 } 30 31 31 - ServeGateway(release).ServeHTTP(w, r) 32 + ServeGateway(db, release).ServeHTTP(w, r) 32 33 })) 33 34 34 35 return mux 35 36 } 36 37 37 - func ServeCatalog() http.Handler { 38 + func ServeCatalog(db *sqlx.DB) http.Handler { 38 39 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 39 - releases, err := controller.GetAllReleases(global.DB, true, 0, true) 40 + releases, err := controller.GetAllReleases(db, true, 0, true) 40 41 if err != nil { 41 42 fmt.Printf("FATAL: Failed to pull releases for catalog: %s\n", err) 42 43 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) ··· 56 57 }) 57 58 } 58 59 59 - func ServeGateway(release *model.Release) http.Handler { 60 + func ServeGateway(db *sqlx.DB, release *model.Release) http.Handler { 60 61 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 61 62 // only allow authorised users to view hidden releases 62 63 privileged := false 63 64 if !release.Visible { 64 - account, err := controller.GetAccountByRequest(global.DB, r) 65 + account, err := controller.GetAccountByRequest(db, r) 65 66 if err != nil { 66 67 fmt.Fprintf(os.Stderr, "WARN: Failed to fetch account: %v\n", err) 67 68 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)