feat(api,db): add /user routes
parent
071ff8ac68
commit
f24d6b6bda
@ -0,0 +1,86 @@
|
|||||||
|
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.8.2 --package=api --generate types -o types.gen.go ../schema/schema.yaml
|
||||||
|
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.8.2 --package=api --generate chi-server -o routes.gen.go ../schema/schema.yaml
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/hhhapz/hackathon/models"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// inProd tells the server whether it is in prod.
|
||||||
|
// It should be set using ldflags on build time.
|
||||||
|
var inProd bool
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
*AuthService
|
||||||
|
*UserService
|
||||||
|
}
|
||||||
|
|
||||||
|
type OAuthStore interface {
|
||||||
|
Create(callback string) (code string)
|
||||||
|
Validate(code string) (string, bool)
|
||||||
|
Exchange(ctx context.Context, code string) (*oauth2.Token, error)
|
||||||
|
Remove(code string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserStore interface {
|
||||||
|
CreateToken(ctx context.Context, user *models.User) (*models.Token, error)
|
||||||
|
RevokeToken(ctx context.Context, token string) error
|
||||||
|
RevokeUserTokens(ctx context.Context, user *models.User) (int64, error)
|
||||||
|
|
||||||
|
User(ctx context.Context, email string) (*models.User, error)
|
||||||
|
Users(ctx context.Context) ([]*models.User, error)
|
||||||
|
|
||||||
|
DecodeUser(ctx context.Context, buf []byte) (*models.User, error)
|
||||||
|
UserByToken(ctx context.Context, token string) (*models.User, error)
|
||||||
|
|
||||||
|
UpdateUser(ctx context.Context, user *models.User) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ServerInterface = (*Server)(nil)
|
||||||
|
|
||||||
|
func NewServer(oaStore OAuthStore, userStore UserStore) *Server {
|
||||||
|
return &Server{
|
||||||
|
AuthService: &AuthService{
|
||||||
|
oauthStore: oaStore,
|
||||||
|
userStore: userStore,
|
||||||
|
},
|
||||||
|
UserService: &UserService{
|
||||||
|
userStore: userStore,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverError(w http.ResponseWriter, code int, message string) {
|
||||||
|
w.WriteHeader(code)
|
||||||
|
json.NewEncoder(w).Encode(Error{
|
||||||
|
Code: code,
|
||||||
|
Message: message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseError struct {
|
||||||
|
typ string
|
||||||
|
value string
|
||||||
|
reason string
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newParseError(typ, value, reason string) error {
|
||||||
|
return parseError{
|
||||||
|
typ: typ,
|
||||||
|
value: value,
|
||||||
|
reason: reason,
|
||||||
|
message: "invalid %s(%q): %s",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err parseError) Error() string {
|
||||||
|
return fmt.Sprintf(err.message, err.typ, err.value, err.reason)
|
||||||
|
}
|
@ -1,341 +0,0 @@
|
|||||||
// Package api provides primitives to interact with the openapi HTTP API.
|
|
||||||
//
|
|
||||||
// Code generated by github.com/deepmap/oapi-codegen version v1.8.2 DO NOT EDIT.
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/deepmap/oapi-codegen/pkg/runtime"
|
|
||||||
"github.com/getkin/kin-openapi/openapi3"
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ConsentPage defines model for ConsentPage.
|
|
||||||
type ConsentPage struct {
|
|
||||||
Url string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error defines model for Error.
|
|
||||||
type Error struct {
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultResponse defines model for DefaultResponse.
|
|
||||||
type DefaultResponse Error
|
|
||||||
|
|
||||||
// AuthorizeCallbackParams defines parameters for AuthorizeCallback.
|
|
||||||
type AuthorizeCallbackParams struct {
|
|
||||||
State string `json:"state"`
|
|
||||||
Code string `json:"code"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenOauthParams defines parameters for GenOauth.
|
|
||||||
type GenOauthParams struct {
|
|
||||||
Callback string `json:"callback"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteTokenParams defines parameters for DeleteToken.
|
|
||||||
type DeleteTokenParams struct {
|
|
||||||
All *bool `json:"all,omitempty"`
|
|
||||||
|
|
||||||
// User authentication token
|
|
||||||
Token string `json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerInterface represents all server handlers.
|
|
||||||
type ServerInterface interface {
|
|
||||||
|
|
||||||
// (GET /auth/authorize)
|
|
||||||
AuthorizeCallback(w http.ResponseWriter, r *http.Request, params AuthorizeCallbackParams)
|
|
||||||
|
|
||||||
// (GET /auth/code)
|
|
||||||
GenOauth(w http.ResponseWriter, r *http.Request, params GenOauthParams)
|
|
||||||
|
|
||||||
// (DELETE /auth/token)
|
|
||||||
DeleteToken(w http.ResponseWriter, r *http.Request, params DeleteTokenParams)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerInterfaceWrapper converts contexts to parameters.
|
|
||||||
type ServerInterfaceWrapper struct {
|
|
||||||
Handler ServerInterface
|
|
||||||
HandlerMiddlewares []MiddlewareFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
|
|
||||||
|
|
||||||
// AuthorizeCallback operation middleware
|
|
||||||
func (siw *ServerInterfaceWrapper) AuthorizeCallback(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Parameter object where we will unmarshal all parameters from the context
|
|
||||||
var params AuthorizeCallbackParams
|
|
||||||
|
|
||||||
// ------------- Required query parameter "state" -------------
|
|
||||||
if paramValue := r.URL.Query().Get("state"); paramValue != "" {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
http.Error(w, "Query argument state is required, but not found", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = runtime.BindQueryParameter("form", true, true, "state", r.URL.Query(), ¶ms.State)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("Invalid format for parameter state: %s", err), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------- Required query parameter "code" -------------
|
|
||||||
if paramValue := r.URL.Query().Get("code"); paramValue != "" {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
http.Error(w, "Query argument code is required, but not found", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = runtime.BindQueryParameter("form", true, true, "code", r.URL.Query(), ¶ms.Code)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("Invalid format for parameter code: %s", err), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler = func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
siw.Handler.AuthorizeCallback(w, r, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, middleware := range siw.HandlerMiddlewares {
|
|
||||||
handler = middleware(handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
handler(w, r.WithContext(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenOauth operation middleware
|
|
||||||
func (siw *ServerInterfaceWrapper) GenOauth(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Parameter object where we will unmarshal all parameters from the context
|
|
||||||
var params GenOauthParams
|
|
||||||
|
|
||||||
// ------------- Required query parameter "callback" -------------
|
|
||||||
if paramValue := r.URL.Query().Get("callback"); paramValue != "" {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
http.Error(w, "Query argument callback is required, but not found", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = runtime.BindQueryParameter("form", true, true, "callback", r.URL.Query(), ¶ms.Callback)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("Invalid format for parameter callback: %s", err), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler = func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
siw.Handler.GenOauth(w, r, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, middleware := range siw.HandlerMiddlewares {
|
|
||||||
handler = middleware(handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
handler(w, r.WithContext(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteToken operation middleware
|
|
||||||
func (siw *ServerInterfaceWrapper) DeleteToken(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Parameter object where we will unmarshal all parameters from the context
|
|
||||||
var params DeleteTokenParams
|
|
||||||
|
|
||||||
// ------------- Optional query parameter "all" -------------
|
|
||||||
if paramValue := r.URL.Query().Get("all"); paramValue != "" {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
err = runtime.BindQueryParameter("form", true, false, "all", r.URL.Query(), ¶ms.All)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("Invalid format for parameter all: %s", err), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var cookie *http.Cookie
|
|
||||||
|
|
||||||
if cookie, err = r.Cookie("token"); err == nil {
|
|
||||||
var value string
|
|
||||||
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
params.Token = value
|
|
||||||
|
|
||||||
} else {
|
|
||||||
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler = func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
siw.Handler.DeleteToken(w, r, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, middleware := range siw.HandlerMiddlewares {
|
|
||||||
handler = middleware(handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
handler(w, r.WithContext(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handler creates http.Handler with routing matching OpenAPI spec.
|
|
||||||
func Handler(si ServerInterface) http.Handler {
|
|
||||||
return HandlerWithOptions(si, ChiServerOptions{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChiServerOptions struct {
|
|
||||||
BaseURL string
|
|
||||||
BaseRouter chi.Router
|
|
||||||
Middlewares []MiddlewareFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
|
|
||||||
func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
|
|
||||||
return HandlerWithOptions(si, ChiServerOptions{
|
|
||||||
BaseRouter: r,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler {
|
|
||||||
return HandlerWithOptions(si, ChiServerOptions{
|
|
||||||
BaseURL: baseURL,
|
|
||||||
BaseRouter: r,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandlerWithOptions creates http.Handler with additional options
|
|
||||||
func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler {
|
|
||||||
r := options.BaseRouter
|
|
||||||
|
|
||||||
if r == nil {
|
|
||||||
r = chi.NewRouter()
|
|
||||||
}
|
|
||||||
wrapper := ServerInterfaceWrapper{
|
|
||||||
Handler: si,
|
|
||||||
HandlerMiddlewares: options.Middlewares,
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
|
||||||
r.Get(options.BaseURL+"/auth/authorize", wrapper.AuthorizeCallback)
|
|
||||||
})
|
|
||||||
r.Group(func(r chi.Router) {
|
|
||||||
r.Get(options.BaseURL+"/auth/code", wrapper.GenOauth)
|
|
||||||
})
|
|
||||||
r.Group(func(r chi.Router) {
|
|
||||||
r.Delete(options.BaseURL+"/auth/token", wrapper.DeleteToken)
|
|
||||||
})
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base64 encoded, gzipped, json marshaled Swagger object
|
|
||||||
var swaggerSpec = []string{
|
|
||||||
|
|
||||||
"H4sIAAAAAAAC/7RVy1P7NhD+VzTbHl3bBU4+lYFOSYcODI8Tw0GRN7aILAlpDQ2Z/O+dlZ0HJC1Mh98F",
|
|
||||||
"7PU+vv2+3c0SlOu8s2gpQrWEgNE7GzG9nONM9oZuRhublLOElvhRem+0kqSdLZ6is2yLqsVO8tPPAWdQ",
|
|
||||||
"wU/FNn8xfI3F7yG4AKvVKoMaowracxKo4N7i3x4VYS0ihhcMAtlVuCC0fZFG16KPyC++J+D4MSVXPGOM",
|
|
||||||
"lq5lk5D64DwG0kMrfTD8b+ZCJwkq6IOGDGjhESqIFLRtUr6Az70OWEP1kGIeN05u+oSKYJXBAH+vhHJ1",
|
|
||||||
"KlwPrEF1UpabaG0JGwwc3mGMI8b/rp8Sbv33oXCAtjO3FkaqJAx2UhuooMPfWtm9SUvS5P4VMrCy4/gL",
|
|
||||||
"topTo2FPgVPRSjWX1DqbidZFlkJb8efkVpxeT0T0qPRsVF2ogJIdpguxSZlDBkYrHOdlrPjX5E5cjtZs",
|
|
||||||
"EANaIh+rokgY807TL2Nc7kJTMDTSZDj69lU2DQZxsYYGGbxgiAPkMv81L9ndebTSa6jgOC/zEjLwktqk",
|
|
||||||
"TSF7atMfF/RbQtZgIosVTN1Mam5/7XEmjZlKNU9JguyQMESoHpagueZzj2GxJTSSJG5sqx6FHrOdddhT",
|
|
||||||
"+nCiUfKv53nM3q/scXk0zOCuqDdY64CKBDnxilPP05RBi7JOTS3h0g2Kvl/hT5ZlWN9x1g8v/AZa8fGU",
|
|
||||||
"cDDJhilNpMMjWwaZ1ot0UKE/0F6x19eEUVsZ/53TT/r8SPFRWX7bJdy9WQfu4RVTI0YnwV7i/mYCP4p4",
|
|
||||||
"cnO0w/gYJNwn/zzZ75Lbl/iXxsAu1RvYM2kibrieOmdQ2mEvPvwk8L1ndGhpfXdoBJAqKufmGrcl1x//",
|
|
||||||
"/w4dlSf7O5RwxF4pjHHWG7MQxjUN1sL19O2CrFb/BAAA//9U4MuzmAcAAA==",
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSwagger returns the content of the embedded swagger specification file
|
|
||||||
// or error if failed to decode
|
|
||||||
func decodeSpec() ([]byte, error) {
|
|
||||||
zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error base64 decoding spec: %s", err)
|
|
||||||
}
|
|
||||||
zr, err := gzip.NewReader(bytes.NewReader(zipped))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error decompressing spec: %s", err)
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
_, err = buf.ReadFrom(zr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error decompressing spec: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawSpec = decodeSpecCached()
|
|
||||||
|
|
||||||
// a naive cached of a decoded swagger spec
|
|
||||||
func decodeSpecCached() func() ([]byte, error) {
|
|
||||||
data, err := decodeSpec()
|
|
||||||
return func() ([]byte, error) {
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
|
|
||||||
func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
|
|
||||||
var res = make(map[string]func() ([]byte, error))
|
|
||||||
if len(pathToFile) > 0 {
|
|
||||||
res[pathToFile] = rawSpec
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSwagger returns the Swagger specification corresponding to the generated code
|
|
||||||
// in this file. The external references of Swagger specification are resolved.
|
|
||||||
// The logic of resolving external references is tightly connected to "import-mapping" feature.
|
|
||||||
// Externally referenced files must be embedded in the corresponding golang packages.
|
|
||||||
// Urls can be supported but this task was out of the scope.
|
|
||||||
func GetSwagger() (swagger *openapi3.T, err error) {
|
|
||||||
var resolvePath = PathToRawSpec("")
|
|
||||||
|
|
||||||
loader := openapi3.NewLoader()
|
|
||||||
loader.IsExternalRefsAllowed = true
|
|
||||||
loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
|
|
||||||
var pathToFile = url.String()
|
|
||||||
pathToFile = path.Clean(pathToFile)
|
|
||||||
getSpec, ok := resolvePath[pathToFile]
|
|
||||||
if !ok {
|
|
||||||
err1 := fmt.Errorf("path not found: %s", pathToFile)
|
|
||||||
return nil, err1
|
|
||||||
}
|
|
||||||
return getSpec()
|
|
||||||
}
|
|
||||||
var specData []byte
|
|
||||||
specData, err = rawSpec()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
swagger, err = loader.LoadFromData(specData)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.8.2 --package=api --generate types,chi-server,spec -o hackathon.gen.go ../schema/schema.yaml
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/hhhapz/hackathon/auth"
|
|
||||||
"github.com/hhhapz/hackathon/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
// inProd tells the server whether it is in prod.
|
|
||||||
// It should be set using ldflags on build time.
|
|
||||||
var inProd bool
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
*AuthService
|
|
||||||
}
|
|
||||||
|
|
||||||
type OAuthStore interface {
|
|
||||||
Create(callback string) (code string)
|
|
||||||
Validate(code string) (callback string, valid bool)
|
|
||||||
Remove(code string)
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserStore interface {
|
|
||||||
CreateToken(ctx context.Context, user *models.User) (*models.Token, error)
|
|
||||||
RevokeToken(ctx context.Context, token string) error
|
|
||||||
RevokeUserTokens(ctx context.Context, user *models.User) (int64, error)
|
|
||||||
|
|
||||||
DecodeUser(ctx context.Context, buf []byte) (*models.User, error)
|
|
||||||
UserByToken(ctx context.Context, token string) (*models.User, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ ServerInterface = (*Server)(nil)
|
|
||||||
|
|
||||||
func NewServer(oaConfig auth.OAuthConfig, oaStore OAuthStore, userStore UserStore) *Server {
|
|
||||||
return &Server{
|
|
||||||
AuthService: &AuthService{
|
|
||||||
oauthConfig: oaConfig,
|
|
||||||
oauthStore: oaStore,
|
|
||||||
userStore: userStore,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func serverError(w http.ResponseWriter, code int, message string) {
|
|
||||||
w.WriteHeader(code)
|
|
||||||
json.NewEncoder(w).Encode(Error{
|
|
||||||
Code: code,
|
|
||||||
Message: message,
|
|
||||||
})
|
|
||||||
}
|
|
@ -0,0 +1,450 @@
|
|||||||
|
// Package api provides primitives to interact with the openapi HTTP API.
|
||||||
|
//
|
||||||
|
// Code generated by github.com/deepmap/oapi-codegen version v1.8.2 DO NOT EDIT.
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/deepmap/oapi-codegen/pkg/runtime"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerInterface represents all server handlers.
|
||||||
|
type ServerInterface interface {
|
||||||
|
|
||||||
|
// (GET /auth/authorize)
|
||||||
|
AuthorizeCallback(w http.ResponseWriter, r *http.Request, params AuthorizeCallbackParams)
|
||||||
|
|
||||||
|
// (GET /auth/code)
|
||||||
|
GenOauth(w http.ResponseWriter, r *http.Request, params GenOauthParams)
|
||||||
|
|
||||||
|
// (DELETE /auth/token)
|
||||||
|
DeleteToken(w http.ResponseWriter, r *http.Request, params DeleteTokenParams)
|
||||||
|
|
||||||
|
// (GET /users/all)
|
||||||
|
GetAllUsers(w http.ResponseWriter, r *http.Request, params GetAllUsersParams)
|
||||||
|
|
||||||
|
// (GET /users/email)
|
||||||
|
GetUserByEmail(w http.ResponseWriter, r *http.Request, params GetUserByEmailParams)
|
||||||
|
|
||||||
|
// (PATCH /users/email)
|
||||||
|
ModifyOtherUser(w http.ResponseWriter, r *http.Request, params ModifyOtherUserParams)
|
||||||
|
|
||||||
|
// (GET /users/me)
|
||||||
|
GetMe(w http.ResponseWriter, r *http.Request, params GetMeParams)
|
||||||
|
|
||||||
|
// (PATCH /users/me)
|
||||||
|
ModifyUser(w http.ResponseWriter, r *http.Request, params ModifyUserParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerInterfaceWrapper converts contexts to parameters.
|
||||||
|
type ServerInterfaceWrapper struct {
|
||||||
|
Handler ServerInterface
|
||||||
|
HandlerMiddlewares []MiddlewareFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
|
||||||
|
|
||||||
|
// AuthorizeCallback operation middleware
|
||||||
|
func (siw *ServerInterfaceWrapper) AuthorizeCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parameter object where we will unmarshal all parameters from the context
|
||||||
|
var params AuthorizeCallbackParams
|
||||||
|
|
||||||
|
// ------------- Required query parameter "state" -------------
|
||||||
|
if paramValue := r.URL.Query().Get("state"); paramValue != "" {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument state is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runtime.BindQueryParameter("form", true, true, "state", r.URL.Query(), ¶ms.State)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Invalid format for parameter state: %s", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------- Required query parameter "code" -------------
|
||||||
|
if paramValue := r.URL.Query().Get("code"); paramValue != "" {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument code is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runtime.BindQueryParameter("form", true, true, "code", r.URL.Query(), ¶ms.Code)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Invalid format for parameter code: %s", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
siw.Handler.AuthorizeCallback(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, middleware := range siw.HandlerMiddlewares {
|
||||||
|
handler = middleware(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenOauth operation middleware
|
||||||
|
func (siw *ServerInterfaceWrapper) GenOauth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parameter object where we will unmarshal all parameters from the context
|
||||||
|
var params GenOauthParams
|
||||||
|
|
||||||
|
// ------------- Required query parameter "callback" -------------
|
||||||
|
if paramValue := r.URL.Query().Get("callback"); paramValue != "" {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument callback is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runtime.BindQueryParameter("form", true, true, "callback", r.URL.Query(), ¶ms.Callback)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Invalid format for parameter callback: %s", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
siw.Handler.GenOauth(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, middleware := range siw.HandlerMiddlewares {
|
||||||
|
handler = middleware(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteToken operation middleware
|
||||||
|
func (siw *ServerInterfaceWrapper) DeleteToken(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parameter object where we will unmarshal all parameters from the context
|
||||||
|
var params DeleteTokenParams
|
||||||
|
|
||||||
|
// ------------- Optional query parameter "all" -------------
|
||||||
|
if paramValue := r.URL.Query().Get("all"); paramValue != "" {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runtime.BindQueryParameter("form", true, false, "all", r.URL.Query(), ¶ms.All)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Invalid format for parameter all: %s", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookie *http.Cookie
|
||||||
|
|
||||||
|
if cookie, err = r.Cookie("token"); err == nil {
|
||||||
|
var value string
|
||||||
|
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Token = value
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
siw.Handler.DeleteToken(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, middleware := range siw.HandlerMiddlewares {
|
||||||
|
handler = middleware(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllUsers operation middleware
|
||||||
|
func (siw *ServerInterfaceWrapper) GetAllUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parameter object where we will unmarshal all parameters from the context
|
||||||
|
var params GetAllUsersParams
|
||||||
|
|
||||||
|
var cookie *http.Cookie
|
||||||
|
|
||||||
|
if cookie, err = r.Cookie("token"); err == nil {
|
||||||
|
var value string
|
||||||
|
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Token = value
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
siw.Handler.GetAllUsers(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, middleware := range siw.HandlerMiddlewares {
|
||||||
|
handler = middleware(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByEmail operation middleware
|
||||||
|
func (siw *ServerInterfaceWrapper) GetUserByEmail(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parameter object where we will unmarshal all parameters from the context
|
||||||
|
var params GetUserByEmailParams
|
||||||
|
|
||||||
|
// ------------- Required query parameter "email" -------------
|
||||||
|
if paramValue := r.URL.Query().Get("email"); paramValue != "" {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument email is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runtime.BindQueryParameter("form", true, true, "email", r.URL.Query(), ¶ms.Email)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Invalid format for parameter email: %s", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookie *http.Cookie
|
||||||
|
|
||||||
|
if cookie, err = r.Cookie("token"); err == nil {
|
||||||
|
var value string
|
||||||
|
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Token = value
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
siw.Handler.GetUserByEmail(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, middleware := range siw.HandlerMiddlewares {
|
||||||
|
handler = middleware(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyOtherUser operation middleware
|
||||||
|
func (siw *ServerInterfaceWrapper) ModifyOtherUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parameter object where we will unmarshal all parameters from the context
|
||||||
|
var params ModifyOtherUserParams
|
||||||
|
|
||||||
|
// ------------- Required query parameter "email" -------------
|
||||||
|
if paramValue := r.URL.Query().Get("email"); paramValue != "" {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument email is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runtime.BindQueryParameter("form", true, true, "email", r.URL.Query(), ¶ms.Email)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Invalid format for parameter email: %s", err), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookie *http.Cookie
|
||||||
|
|
||||||
|
if cookie, err = r.Cookie("token"); err == nil {
|
||||||
|
var value string
|
||||||
|
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Token = value
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
siw.Handler.ModifyOtherUser(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, middleware := range siw.HandlerMiddlewares {
|
||||||
|
handler = middleware(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMe operation middleware
|
||||||
|
func (siw *ServerInterfaceWrapper) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parameter object where we will unmarshal all parameters from the context
|
||||||
|
var params GetMeParams
|
||||||
|
|
||||||
|
var cookie *http.Cookie
|
||||||
|
|
||||||
|
if cookie, err = r.Cookie("token"); err == nil {
|
||||||
|
var value string
|
||||||
|
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Token = value
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
siw.Handler.GetMe(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, middleware := range siw.HandlerMiddlewares {
|
||||||
|
handler = middleware(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyUser operation middleware
|
||||||
|
func (siw *ServerInterfaceWrapper) ModifyUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parameter object where we will unmarshal all parameters from the context
|
||||||
|
var params ModifyUserParams
|
||||||
|
|
||||||
|
var cookie *http.Cookie
|
||||||
|
|
||||||
|
if cookie, err = r.Cookie("token"); err == nil {
|
||||||
|
var value string
|
||||||
|
err = runtime.BindStyledParameter("simple", true, "token", cookie.Value, &value)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid format for parameter token: %s", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Token = value
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.Error(w, "Query argument token is required, but not found", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
siw.Handler.ModifyUser(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, middleware := range siw.HandlerMiddlewares {
|
||||||
|
handler = middleware(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler creates http.Handler with routing matching OpenAPI spec.
|
||||||
|
func Handler(si ServerInterface) http.Handler {
|
||||||
|
return HandlerWithOptions(si, ChiServerOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChiServerOptions struct {
|
||||||
|
BaseURL string
|
||||||
|
BaseRouter chi.Router
|
||||||
|
Middlewares []MiddlewareFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
|
||||||
|
func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
|
||||||
|
return HandlerWithOptions(si, ChiServerOptions{
|
||||||
|
BaseRouter: r,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler {
|
||||||
|
return HandlerWithOptions(si, ChiServerOptions{
|
||||||
|
BaseURL: baseURL,
|
||||||
|
BaseRouter: r,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerWithOptions creates http.Handler with additional options
|
||||||
|
func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler {
|
||||||
|
r := options.BaseRouter
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
r = chi.NewRouter()
|
||||||
|
}
|
||||||
|
wrapper := ServerInterfaceWrapper{
|
||||||
|
Handler: si,
|
||||||
|
HandlerMiddlewares: options.Middlewares,
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Get(options.BaseURL+"/auth/authorize", wrapper.AuthorizeCallback)
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Get(options.BaseURL+"/auth/code", wrapper.GenOauth)
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Delete(options.BaseURL+"/auth/token", wrapper.DeleteToken)
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Get(options.BaseURL+"/users/all", wrapper.GetAllUsers)
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Get(options.BaseURL+"/users/email", wrapper.GetUserByEmail)
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Patch(options.BaseURL+"/users/email", wrapper.ModifyOtherUser)
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Get(options.BaseURL+"/users/me", wrapper.GetMe)
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Patch(options.BaseURL+"/users/me", wrapper.ModifyUser)
|
||||||
|
})
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
// Package api provides primitives to interact with the openapi HTTP API.
|
||||||
|
//
|
||||||
|
// Code generated by github.com/deepmap/oapi-codegen version v1.8.2 DO NOT EDIT.
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
openapi_types "github.com/deepmap/oapi-codegen/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConsentPage defines model for ConsentPage.
|
||||||
|
type ConsentPage struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error defines model for Error.
|
||||||
|
type Error struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// User defines model for User.
|
||||||
|
type User struct {
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
Email openapi_types.Email `json:"email"`
|
||||||
|
|
||||||
|
// GradeLevel is only present if teacher is false.
|
||||||
|
GradeLevel *int `json:"grade_level,omitempty"`
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Teacher bool `json:"teacher"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultResponse defines model for DefaultResponse.
|
||||||
|
type DefaultResponse Error
|
||||||
|
|
||||||
|
// AuthorizeCallbackParams defines parameters for AuthorizeCallback.
|
||||||
|
type AuthorizeCallbackParams struct {
|
||||||
|
State string `json:"state"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenOauthParams defines parameters for GenOauth.
|
||||||
|
type GenOauthParams struct {
|
||||||
|
Callback string `json:"callback"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTokenParams defines parameters for DeleteToken.
|
||||||
|
type DeleteTokenParams struct {
|
||||||
|
All *bool `json:"all,omitempty"`
|
||||||
|
|
||||||
|
// User authentication token.
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllUsersParams defines parameters for GetAllUsers.
|
||||||
|
type GetAllUsersParams struct {
|
||||||
|
// User authentication token.
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByEmailParams defines parameters for GetUserByEmail.
|
||||||
|
type GetUserByEmailParams struct {
|
||||||
|
// User email.
|
||||||
|
Email openapi_types.Email `json:"email"`
|
||||||
|
|
||||||
|
// User authentication token.
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyOtherUserJSONBody defines parameters for ModifyOtherUser.
|
||||||
|
type ModifyOtherUserJSONBody struct {
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
GradeLevel int `json:"grade_level"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NewEmail *openapi_types.Email `json:"new_email,omitempty"`
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Teacher bool `json:"teacher"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyOtherUserParams defines parameters for ModifyOtherUser.
|
||||||
|
type ModifyOtherUserParams struct {
|
||||||
|
// User email.
|
||||||
|
Email openapi_types.Email `json:"email"`
|
||||||
|
|
||||||
|
// User authentication token.
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMeParams defines parameters for GetMe.
|
||||||
|
type GetMeParams struct {
|
||||||
|
// User authentication token.
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyUserJSONBody defines parameters for ModifyUser.
|
||||||
|
type ModifyUserJSONBody struct {
|
||||||
|
GradeLevel int `json:"grade_level"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyUserParams defines parameters for ModifyUser.
|
||||||
|
type ModifyUserParams struct {
|
||||||
|
// User authentication token.
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyOtherUserJSONRequestBody defines body for ModifyOtherUser for application/json ContentType.
|
||||||
|
type ModifyOtherUserJSONRequestBody ModifyOtherUserJSONBody
|
||||||
|
|
||||||
|
// ModifyUserJSONRequestBody defines body for ModifyUser for application/json ContentType.
|
||||||
|
type ModifyUserJSONRequestBody ModifyUserJSONBody
|
@ -0,0 +1,210 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/deepmap/oapi-codegen/pkg/types"
|
||||||
|
"github.com/hhhapz/hackathon/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserService struct {
|
||||||
|
userStore UserStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *UserService) GetMe(w http.ResponseWriter, r *http.Request, params GetMeParams) {
|
||||||
|
u, err := us.userStore.UserByToken(r.Context(), params.Token)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, http.StatusUnauthorized, "Invalid token provided")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUser(w, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *UserService) ModifyUser(w http.ResponseWriter, r *http.Request, params ModifyUserParams) {
|
||||||
|
u, err := us.userStore.UserByToken(r.Context(), params.Token)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, http.StatusUnauthorized, "Invalid token provided")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var body ModifyUserJSONBody
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||||
|
serverError(w, http.StatusBadRequest, "Invalid JSON body for ModifyUser")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u.Name = strings.TrimSpace(body.Name)
|
||||||
|
if len(u.Name) < 3 || len(u.Name) > 30 {
|
||||||
|
serverError(w, http.StatusUnprocessableEntity, "Name must be between 3 and 30 characters long")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !u.Teacher {
|
||||||
|
if body.GradeLevel < 9 || body.GradeLevel > 12 {
|
||||||
|
serverError(w, http.StatusUnprocessableEntity, "Grade Level must be between 9 and 12")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u.GradeLevel = sql.NullInt64{Int64: int64(body.GradeLevel), Valid: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = us.userStore.UpdateUser(r.Context(), u)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not modify user %s: %v", u.Email, err)
|
||||||
|
serverError(w, http.StatusInternalServerError, "Could not update user")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUser(w, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *UserService) GetUserByEmail(w http.ResponseWriter, r *http.Request, params GetUserByEmailParams) {
|
||||||
|
u, err := us.userStore.UserByToken(r.Context(), params.Token)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, http.StatusUnauthorized, "Invalid token provided")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
email := string(params.Email)
|
||||||
|
if u.Email == email {
|
||||||
|
writeUser(w, u)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !u.Admin {
|
||||||
|
log.Printf("user accessing unauthorized endpoint: %s", u.Email)
|
||||||
|
serverError(w, http.StatusForbidden, "You are not authorized to do this")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := us.userStore.User(r.Context(), email)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not fetch user(%q): %v", email, err)
|
||||||
|
serverError(w, http.StatusNotFound, "Could not find user with the specified email")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUser(w, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *UserService) ModifyOtherUser(w http.ResponseWriter, r *http.Request, params ModifyOtherUserParams) {
|
||||||
|
u, err := us.userStore.UserByToken(r.Context(), params.Token)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, http.StatusUnauthorized, "Invalid token provided")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !u.Admin {
|
||||||
|
log.Printf("user accessing unauthorized endpoint: %s", u.Email)
|
||||||
|
serverError(w, http.StatusForbidden, "You are not authorized to do this")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := us.userStore.User(r.Context(), string(params.Email))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not fetch user(%q): %v", params.Email, err)
|
||||||
|
serverError(w, http.StatusNotFound, "Could not find user with the specified email")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var body ModifyOtherUserJSONBody
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||||
|
serverError(w, http.StatusBadRequest, "Invalid JSON body for ModifyOtherUser")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Name = strings.TrimSpace(body.Name)
|
||||||
|
user.Email = string(*body.NewEmail)
|
||||||
|
user.Picture = body.Picture
|
||||||
|
user.Teacher = body.Teacher
|
||||||
|
user.Admin = body.Admin
|
||||||
|
|
||||||
|
if len(user.Name) < 3 || len(user.Name) > 30 {
|
||||||
|
serverError(w, http.StatusUnprocessableEntity, "Name must be between 3 and 30 characters long")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.GradeLevel = sql.NullInt64{}
|
||||||
|
if !user.Teacher {
|
||||||
|
if body.GradeLevel < 9 || body.GradeLevel > 12 {
|
||||||
|
serverError(w, http.StatusUnprocessableEntity, "Grade Level must be between 9 and 12")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.GradeLevel = sql.NullInt64{Int64: int64(body.GradeLevel), Valid: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUser(w, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us *UserService) GetAllUsers(w http.ResponseWriter, r *http.Request, params GetAllUsersParams) {
|
||||||
|
u, err := us.userStore.UserByToken(r.Context(), params.Token)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, http.StatusUnauthorized, "Invalid token provided")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !u.Admin {
|
||||||
|
log.Printf("user accessing unauthorized endpoint: %s", u.Email)
|
||||||
|
serverError(w, http.StatusForbidden, "You are not authorized to do this")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := us.userStore.Users(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("user accessing unauthorized endpoint: %s", u.Email)
|
||||||
|
serverError(w, http.StatusInternalServerError, "Could not fetch users")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUsers(w, users)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeUser(w http.ResponseWriter, u *models.User) {
|
||||||
|
var grade *int
|
||||||
|
if u.GradeLevel.Valid {
|
||||||
|
g := int(u.GradeLevel.Int64)
|
||||||
|
grade = &g
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(200)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(User{
|
||||||
|
Admin: u.Admin,
|
||||||
|
CreatedAt: u.CreatedAt.Time(),
|
||||||
|
Email: types.Email(u.Email),
|
||||||
|
GradeLevel: grade,
|
||||||
|
Id: u.ID,
|
||||||
|
Name: u.Name,
|
||||||
|
Picture: u.Picture,
|
||||||
|
Teacher: u.Teacher,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeUsers(w http.ResponseWriter, us []*models.User) {
|
||||||
|
var users []User
|
||||||
|
|
||||||
|
for _, u := range us {
|
||||||
|
var grade *int
|
||||||
|
if u.GradeLevel.Valid {
|
||||||
|
g := int(u.GradeLevel.Int64)
|
||||||
|
grade = &g
|
||||||
|
}
|
||||||
|
|
||||||
|
users = append(users, User{
|
||||||
|
Admin: u.Admin,
|
||||||
|
CreatedAt: u.CreatedAt.Time(),
|
||||||
|
Email: types.Email(u.Email),
|
||||||
|
GradeLevel: grade,
|
||||||
|
Id: u.ID,
|
||||||
|
Name: u.Name,
|
||||||
|
Picture: u.Picture,
|
||||||
|
Teacher: u.Teacher,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(200)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(users)
|
||||||
|
}
|
@ -1,62 +0,0 @@
|
|||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
"golang.org/x/oauth2/google"
|
|
||||||
)
|
|
||||||
|
|
||||||
type OAuthConfig struct {
|
|
||||||
*oauth2.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
base = "https://www.googleapis.com"
|
|
||||||
scopeEmail = base + "/auth/userinfo.email"
|
|
||||||
scopeProfile = base + "/auth/userinfo.profile"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewOauthConfig(path string) (OAuthConfig, error) {
|
|
||||||
key, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return OAuthConfig{}, fmt.Errorf("could not open file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := google.ConfigFromJSON(key, scopeEmail, scopeProfile)
|
|
||||||
if err != nil {
|
|
||||||
return OAuthConfig{}, fmt.Errorf("could not load config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return OAuthConfig{
|
|
||||||
Config: config,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var ErrInvalidState = errors.New("invalid ouath state provided")
|
|
||||||
|
|
||||||
type OAuthManager interface {
|
|
||||||
Create(callback string) (code string)
|
|
||||||
Validate(code string) (callback string, valid bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg OAuthConfig) AuthCodeURL(manager OAuthManager, callback string) string {
|
|
||||||
state := manager.Create(callback)
|
|
||||||
fmt.Printf("state: %v\n", state)
|
|
||||||
|
|
||||||
return cfg.Config.AuthCodeURL(state, oauth2.AccessTypeOffline)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg OAuthConfig) Exchange(ctx context.Context, manager OAuthManager, state, code string) (string, *oauth2.Token, error) {
|
|
||||||
var ok bool
|
|
||||||
var callback string
|
|
||||||
if callback, ok = manager.Validate(state); !ok {
|
|
||||||
return "", nil, ErrInvalidState
|
|
||||||
}
|
|
||||||
|
|
||||||
tk, err := cfg.Config.Exchange(ctx, code, oauth2.AccessTypeOffline)
|
|
||||||
return callback, tk, err
|
|
||||||
}
|
|
@ -1,153 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
// Code generated by xo. DO NOT EDIT.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UserInfo represents a row from 'user_info'.
|
|
||||||
type UserInfo struct {
|
|
||||||
UserID string `json:"user_id"` // user_id
|
|
||||||
GradeLevel int `json:"grade_level"` // grade_level
|
|
||||||
Teacher bool `json:"teacher"` // teacher
|
|
||||||
Admin bool `json:"admin"` // admin
|
|
||||||
// xo fields
|
|
||||||
_exists, _deleted bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exists returns true when the UserInfo exists in the database.
|
|
||||||
func (ui *UserInfo) Exists() bool {
|
|
||||||
return ui._exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deleted returns true when the UserInfo has been marked for deletion from
|
|
||||||
// the database.
|
|
||||||
func (ui *UserInfo) Deleted() bool {
|
|
||||||
return ui._deleted
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert inserts the UserInfo to the database.
|
|
||||||
func (ui *UserInfo) Insert(ctx context.Context, db DB) error {
|
|
||||||
switch {
|
|
||||||
case ui._exists: // already exists
|
|
||||||
return logerror(&ErrInsertFailed{ErrAlreadyExists})
|
|
||||||
case ui._deleted: // deleted
|
|
||||||
return logerror(&ErrInsertFailed{ErrMarkedForDeletion})
|
|
||||||
}
|
|
||||||
// insert (manual)
|
|
||||||
const sqlstr = `INSERT INTO user_info (` +
|
|
||||||
`user_id, grade_level, teacher, admin` +
|
|
||||||
`) VALUES (` +
|
|
||||||
`$1, $2, $3, $4` +
|
|
||||||
`)`
|
|
||||||
// run
|
|
||||||
logf(sqlstr, ui.UserID, ui.GradeLevel, ui.Teacher, ui.Admin)
|
|
||||||
if _, err := db.ExecContext(ctx, sqlstr, ui.UserID, ui.GradeLevel, ui.Teacher, ui.Admin); err != nil {
|
|
||||||
return logerror(err)
|
|
||||||
}
|
|
||||||
// set exists
|
|
||||||
ui._exists = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update updates a UserInfo in the database.
|
|
||||||
func (ui *UserInfo) Update(ctx context.Context, db DB) error {
|
|
||||||
switch {
|
|
||||||
case !ui._exists: // doesn't exist
|
|
||||||
return logerror(&ErrUpdateFailed{ErrDoesNotExist})
|
|
||||||
case ui._deleted: // deleted
|
|
||||||
return logerror(&ErrUpdateFailed{ErrMarkedForDeletion})
|
|
||||||
}
|
|
||||||
// update with primary key
|
|
||||||
const sqlstr = `UPDATE user_info SET ` +
|
|
||||||
`grade_level = $1, teacher = $2, admin = $3 ` +
|
|
||||||
`WHERE user_id = $4`
|
|
||||||
// run
|
|
||||||
logf(sqlstr, ui.GradeLevel, ui.Teacher, ui.Admin, ui.UserID)
|
|
||||||
if _, err := db.ExecContext(ctx, sqlstr, ui.GradeLevel, ui.Teacher, ui.Admin, ui.UserID); err != nil {
|
|
||||||
return logerror(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save saves the UserInfo to the database.
|
|
||||||
func (ui *UserInfo) Save(ctx context.Context, db DB) error {
|
|
||||||
if ui.Exists() {
|
|
||||||
return ui.Update(ctx, db)
|
|
||||||
}
|
|
||||||
return ui.Insert(ctx, db)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upsert performs an upsert for UserInfo.
|
|
||||||
func (ui *UserInfo) Upsert(ctx context.Context, db DB) error {
|
|
||||||
switch {
|
|
||||||
case ui._deleted: // deleted
|
|
||||||
return logerror(&ErrUpsertFailed{ErrMarkedForDeletion})
|
|
||||||
}
|
|
||||||
// upsert
|
|
||||||
const sqlstr = `INSERT INTO user_info (` +
|
|
||||||
`user_id, grade_level, teacher, admin` +
|
|
||||||
`) VALUES (` +
|
|
||||||
`$1, $2, $3, $4` +
|
|
||||||
`)` +
|
|
||||||
` ON CONFLICT (user_id) DO ` +
|
|
||||||
`UPDATE SET ` +
|
|
||||||
`grade_level = EXCLUDED.grade_level, teacher = EXCLUDED.teacher, admin = EXCLUDED.admin `
|
|
||||||
// run
|
|
||||||
logf(sqlstr, ui.UserID, ui.GradeLevel, ui.Teacher, ui.Admin)
|
|
||||||
if _, err := db.ExecContext(ctx, sqlstr, ui.UserID, ui.GradeLevel, ui.Teacher, ui.Admin); err != nil {
|
|
||||||
return logerror(err)
|
|
||||||
}
|
|
||||||
// set exists
|
|
||||||
ui._exists = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes the UserInfo from the database.
|
|
||||||
func (ui *UserInfo) Delete(ctx context.Context, db DB) error {
|
|
||||||
switch {
|
|
||||||
case !ui._exists: // doesn't exist
|
|
||||||
return nil
|
|
||||||
case ui._deleted: // deleted
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// delete with single primary key
|
|
||||||
const sqlstr = `DELETE FROM user_info ` +
|
|
||||||
`WHERE user_id = $1`
|
|
||||||
// run
|
|
||||||
logf(sqlstr, ui.UserID)
|
|
||||||
if _, err := db.ExecContext(ctx, sqlstr, ui.UserID); err != nil {
|
|
||||||
return logerror(err)
|
|
||||||
}
|
|
||||||
// set deleted
|
|
||||||
ui._deleted = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserInfoByUserID retrieves a row from 'user_info' as a UserInfo.
|
|
||||||
//
|
|
||||||
// Generated from index 'sqlite_autoindex_user_info_1'.
|
|
||||||
func UserInfoByUserID(ctx context.Context, db DB, userID string) (*UserInfo, error) {
|
|
||||||
// query
|
|
||||||
const sqlstr = `SELECT ` +
|
|
||||||
`user_id, grade_level, teacher, admin ` +
|
|
||||||
`FROM user_info ` +
|
|
||||||
`WHERE user_id = $1`
|
|
||||||
// run
|
|
||||||
logf(sqlstr, userID)
|
|
||||||
ui := UserInfo{
|
|
||||||
_exists: true,
|
|
||||||
}
|
|
||||||
if err := db.QueryRowContext(ctx, sqlstr, userID).Scan(&ui.UserID, &ui.GradeLevel, &ui.Teacher, &ui.Admin); err != nil {
|
|
||||||
return nil, logerror(err)
|
|
||||||
}
|
|
||||||
return &ui, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// User returns the User associated with the UserInfo's (UserID).
|
|
||||||
//
|
|
||||||
// Generated from foreign key 'user_info_user_id_fkey'.
|
|
||||||
func (ui *UserInfo) User(ctx context.Context, db DB) (*User, error) {
|
|
||||||
return UserByID(ctx, db, ui.UserID)
|
|
||||||
}
|
|
Loading…
Reference in New Issue