All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 3m36s
Move Query/Mutation field declarations from the monolithic schema.graphql into each domain's .graphql file using extend type, so gqlgen places resolvers in the matching *.resolvers.go files. Extract helper functions into *_helpers.go files to prevent gqlgen from commenting them out. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
147 lines
3.7 KiB
Go
147 lines
3.7 KiB
Go
package graph
|
|
|
|
// This file will be automatically regenerated based on the schema, any resolver
|
|
// implementations
|
|
// will be copied through when generating and any unknown code will be moved to the end.
|
|
// Code generated by github.com/99designs/gqlgen version v0.17.88
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"adam-french.co.uk/backend/graph/model"
|
|
"adam-french.co.uk/backend/models"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// Login is the resolver for the login field.
|
|
func (r *mutationResolver) Login(ctx context.Context, input model.LoginInput) (*model.AuthPayload, error) {
|
|
gc := GinContextFromCtx(ctx)
|
|
if gc == nil {
|
|
return nil, fmt.Errorf("could not get gin context")
|
|
}
|
|
|
|
if !r.Store.LoginLimiter.Allow(gc.ClientIP()) {
|
|
return nil, fmt.Errorf("too many login attempts, please try again later")
|
|
}
|
|
|
|
var user models.User
|
|
if err := r.Store.DB.Where("username = ?", input.Username).First(&user).Error; err != nil {
|
|
return nil, fmt.Errorf("invalid credentials")
|
|
}
|
|
|
|
if err := bcrypt.CompareHashAndPassword(user.Password, []byte(input.Password)); err != nil {
|
|
return nil, fmt.Errorf("invalid credentials")
|
|
}
|
|
|
|
tokens, err := r.Store.Auth.GenerateJWT(&user)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate tokens")
|
|
}
|
|
|
|
gc.SetSameSite(http.SameSiteLaxMode)
|
|
gc.SetCookie(
|
|
"access_token",
|
|
tokens.AccessToken,
|
|
int(r.Store.Auth.Config.AccessTokenLifetime.Seconds()),
|
|
"/",
|
|
r.Store.Auth.Config.Domain,
|
|
true, true,
|
|
)
|
|
gc.SetCookie(
|
|
"refresh_token",
|
|
tokens.RefreshToken,
|
|
int(r.Store.Auth.Config.RefreshTokenLifetime.Seconds()),
|
|
"/",
|
|
r.Store.Auth.Config.Domain,
|
|
true, true,
|
|
)
|
|
|
|
return &model.AuthPayload{User: &user}, nil
|
|
}
|
|
|
|
// Logout is the resolver for the logout field.
|
|
func (r *mutationResolver) Logout(ctx context.Context) (bool, error) {
|
|
gc := GinContextFromCtx(ctx)
|
|
if gc == nil {
|
|
return false, fmt.Errorf("could not get gin context")
|
|
}
|
|
|
|
gc.SetSameSite(http.SameSiteLaxMode)
|
|
gc.SetCookie("access_token", "", -1, "/", r.Store.Auth.Config.Domain, true, true)
|
|
gc.SetCookie("refresh_token", "", -1, "/", r.Store.Auth.Config.Domain, true, true)
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// RefreshToken is the resolver for the refreshToken field.
|
|
func (r *mutationResolver) RefreshToken(ctx context.Context) (*model.AuthPayload, error) {
|
|
gc := GinContextFromCtx(ctx)
|
|
if gc == nil {
|
|
return nil, fmt.Errorf("could not get gin context")
|
|
}
|
|
|
|
refreshToken, err := gc.Cookie("refresh_token")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
claims, err := r.Store.Auth.VerifyJWT(refreshToken)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
userIDF, ok := (*claims)["id"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid token claims")
|
|
}
|
|
|
|
var user models.User
|
|
user.ID = uint(userIDF)
|
|
if err := r.Store.DB.First(&user).Error; err != nil {
|
|
return nil, fmt.Errorf("user not found")
|
|
}
|
|
|
|
tokens, err := r.Store.Auth.GenerateJWT(&user)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate tokens")
|
|
}
|
|
|
|
gc.SetSameSite(http.SameSiteLaxMode)
|
|
gc.SetCookie(
|
|
"access_token",
|
|
tokens.AccessToken,
|
|
int(r.Store.Auth.Config.AccessTokenLifetime.Seconds()),
|
|
"/",
|
|
r.Store.Auth.Config.Domain,
|
|
true, true,
|
|
)
|
|
gc.SetCookie(
|
|
"refresh_token",
|
|
tokens.RefreshToken,
|
|
int(r.Store.Auth.Config.RefreshTokenLifetime.Seconds()),
|
|
"/",
|
|
r.Store.Auth.Config.Domain,
|
|
true, true,
|
|
)
|
|
|
|
return &model.AuthPayload{User: &user}, nil
|
|
}
|
|
|
|
// Me is the resolver for the me field.
|
|
func (r *queryResolver) Me(ctx context.Context) (*models.User, error) {
|
|
userID, ok := UserIDFromCtx(ctx)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unauthorized")
|
|
}
|
|
|
|
var user models.User
|
|
user.ID = userID
|
|
if err := r.Store.DB.First(&user).Error; err != nil {
|
|
return nil, fmt.Errorf("user not found")
|
|
}
|
|
|
|
return &user, nil
|
|
}
|