Split schema.graphql and schema.resolvers.go into per-domain files
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 3m36s
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>
This commit is contained in:
@@ -7,7 +7,9 @@ package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/models"
|
||||
)
|
||||
|
||||
@@ -16,6 +18,33 @@ func (r *activityResolver) ID(ctx context.Context, obj *models.Activity) (int, e
|
||||
return int(obj.ID), nil
|
||||
}
|
||||
|
||||
// CreateActivity is the resolver for the createActivity field.
|
||||
func (r *mutationResolver) CreateActivity(ctx context.Context, input model.CreateActivityInput) (*models.Activity, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
activity := models.Activity{Type: input.Type, Name: input.Name, Link: input.Link}
|
||||
if err := r.Store.DB.Create(&activity).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &activity, nil
|
||||
}
|
||||
|
||||
// Activities is the resolver for the activities field.
|
||||
func (r *queryResolver) Activities(ctx context.Context) ([]*models.Activity, error) {
|
||||
var activities []models.Activity
|
||||
if err := r.Store.DB.Order("created_at DESC").Find(&activities).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Activity, len(activities))
|
||||
for i := range activities {
|
||||
result[i] = &activities[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Activity returns ActivityResolver implementation.
|
||||
func (r *Resolver) Activity() ActivityResolver { return &activityResolver{r} }
|
||||
|
||||
|
||||
146
backend/graph/auth.resolvers.go
Normal file
146
backend/graph/auth.resolvers.go
Normal file
@@ -0,0 +1,146 @@
|
||||
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
|
||||
}
|
||||
@@ -7,7 +7,9 @@ package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/models"
|
||||
)
|
||||
|
||||
@@ -16,6 +18,46 @@ func (r *bookmarkResolver) ID(ctx context.Context, obj *models.Bookmark) (int, e
|
||||
return int(obj.ID), nil
|
||||
}
|
||||
|
||||
// CreateBookmark is the resolver for the createBookmark field.
|
||||
func (r *mutationResolver) CreateBookmark(ctx context.Context, input model.CreateBookmarkInput) (*models.Bookmark, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
bookmark := models.Bookmark{Category: input.Category, Name: input.Name, Link: input.Link}
|
||||
if err := r.Store.DB.Create(&bookmark).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bookmark, nil
|
||||
}
|
||||
|
||||
// DeleteBookmark is the resolver for the deleteBookmark field.
|
||||
func (r *mutationResolver) DeleteBookmark(ctx context.Context, id int) (*models.Bookmark, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var bookmark models.Bookmark
|
||||
if err := r.Store.DB.First(&bookmark, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.Store.DB.Delete(&bookmark).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bookmark, nil
|
||||
}
|
||||
|
||||
// Bookmarks is the resolver for the bookmarks field.
|
||||
func (r *queryResolver) Bookmarks(ctx context.Context) ([]*models.Bookmark, error) {
|
||||
var bookmarks []models.Bookmark
|
||||
if err := r.Store.DB.Order("category ASC, created_at ASC").Find(&bookmarks).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Bookmark, len(bookmarks))
|
||||
for i := range bookmarks {
|
||||
result[i] = &bookmarks[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Bookmark returns BookmarkResolver implementation.
|
||||
func (r *Resolver) Bookmark() BookmarkResolver { return &bookmarkResolver{r} }
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/models"
|
||||
)
|
||||
|
||||
@@ -16,6 +18,33 @@ func (r *favoriteResolver) ID(ctx context.Context, obj *models.Favorite) (int, e
|
||||
return int(obj.ID), nil
|
||||
}
|
||||
|
||||
// CreateFavorite is the resolver for the createFavorite field.
|
||||
func (r *mutationResolver) CreateFavorite(ctx context.Context, input model.CreateFavoriteInput) (*models.Favorite, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
favorite := models.Favorite{Type: input.Type, Name: input.Name, Link: input.Link}
|
||||
if err := r.Store.DB.Create(&favorite).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &favorite, nil
|
||||
}
|
||||
|
||||
// Favorites is the resolver for the favorites field.
|
||||
func (r *queryResolver) Favorites(ctx context.Context) ([]*models.Favorite, error) {
|
||||
var favorites []models.Favorite
|
||||
if err := r.Store.DB.Order("created_at DESC").Find(&favorites).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Favorite, len(favorites))
|
||||
for i := range favorites {
|
||||
result[i] = &favorites[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Favorite returns FavoriteResolver implementation.
|
||||
func (r *Resolver) Favorite() FavoriteResolver { return &favoriteResolver{r} }
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,10 @@
|
||||
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"
|
||||
"time"
|
||||
@@ -8,17 +13,6 @@ import (
|
||||
"adam-french.co.uk/backend/services"
|
||||
)
|
||||
|
||||
func mapGiteaFeed(feed *services.GiteaFeedResponse) *model.GiteaFeedItem {
|
||||
return &model.GiteaFeedItem{
|
||||
AvatarURL: feed.ActUser.AvatarURL,
|
||||
RepoURL: feed.Repo.HTMLURL,
|
||||
RepoName: feed.Repo.FullName,
|
||||
OpType: feed.OpType,
|
||||
CommitMessage: services.ParseCommitMessage(feed.Content),
|
||||
CreatedAt: feed.Created,
|
||||
}
|
||||
}
|
||||
|
||||
// GiteaFeed is the resolver for the giteaFeed field.
|
||||
func (r *queryResolver) GiteaFeed(ctx context.Context) (*model.GiteaFeedItem, error) {
|
||||
if r.Store.GiteaFeedFresh() {
|
||||
|
||||
17
backend/graph/gitea_helpers.go
Normal file
17
backend/graph/gitea_helpers.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/services"
|
||||
)
|
||||
|
||||
func mapGiteaFeed(feed *services.GiteaFeedResponse) *model.GiteaFeedItem {
|
||||
return &model.GiteaFeedItem{
|
||||
AvatarURL: feed.ActUser.AvatarURL,
|
||||
RepoURL: feed.Repo.HTMLURL,
|
||||
RepoName: feed.Repo.FullName,
|
||||
OpType: feed.OpType,
|
||||
CommitMessage: services.ParseCommitMessage(feed.Content),
|
||||
CreatedAt: feed.Created,
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,9 @@ package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/models"
|
||||
)
|
||||
|
||||
@@ -16,6 +18,75 @@ func (r *jobAppReferenceResolver) ID(ctx context.Context, obj *models.JobAppRefe
|
||||
return int(obj.ID), nil
|
||||
}
|
||||
|
||||
// CreateJobAppReference is the resolver for the createJobAppReference field.
|
||||
func (r *mutationResolver) CreateJobAppReference(ctx context.Context, input model.CreateJobAppReferenceInput) (*models.JobAppReference, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
ref := models.JobAppReference{
|
||||
Category: input.Category,
|
||||
Label: input.Label,
|
||||
Value: input.Value,
|
||||
}
|
||||
if input.SortOrder != nil {
|
||||
ref.SortOrder = *input.SortOrder
|
||||
}
|
||||
if err := r.Store.DB.Create(&ref).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ref, nil
|
||||
}
|
||||
|
||||
// UpdateJobAppReference is the resolver for the updateJobAppReference field.
|
||||
func (r *mutationResolver) UpdateJobAppReference(ctx context.Context, id int, input model.UpdateJobAppReferenceInput) (*models.JobAppReference, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var ref models.JobAppReference
|
||||
if err := r.Store.DB.First(&ref, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.Category != nil {
|
||||
ref.Category = *input.Category
|
||||
}
|
||||
if input.Label != nil {
|
||||
ref.Label = *input.Label
|
||||
}
|
||||
if input.Value != nil {
|
||||
ref.Value = *input.Value
|
||||
}
|
||||
if input.SortOrder != nil {
|
||||
ref.SortOrder = *input.SortOrder
|
||||
}
|
||||
if err := r.Store.DB.Save(&ref).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ref, nil
|
||||
}
|
||||
|
||||
// DeleteJobAppReference is the resolver for the deleteJobAppReference field.
|
||||
func (r *mutationResolver) DeleteJobAppReference(ctx context.Context, id int) (bool, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return false, fmt.Errorf("admin access required")
|
||||
}
|
||||
if err := r.Store.DB.Delete(&models.JobAppReference{}, id).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// JobAppReferences is the resolver for the jobAppReferences field.
|
||||
func (r *queryResolver) JobAppReferences(ctx context.Context) ([]*models.JobAppReference, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var refs []*models.JobAppReference
|
||||
if err := r.Store.DB.Order("category ASC, sort_order ASC, created_at ASC").Find(&refs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
// JobAppReference returns JobAppReferenceResolver implementation.
|
||||
func (r *Resolver) JobAppReference() JobAppReferenceResolver { return &jobAppReferenceResolver{r} }
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/models"
|
||||
)
|
||||
|
||||
@@ -16,6 +18,97 @@ func (r *jobApplicationResolver) ID(ctx context.Context, obj *models.JobApplicat
|
||||
return int(obj.ID), nil
|
||||
}
|
||||
|
||||
// CreateJobApplication is the resolver for the createJobApplication field.
|
||||
func (r *mutationResolver) CreateJobApplication(ctx context.Context, input model.CreateJobApplicationInput) (*models.JobApplication, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
app := models.JobApplication{
|
||||
JobTitle: input.JobTitle,
|
||||
Company: input.Company,
|
||||
Location: input.Location,
|
||||
URL: input.URL,
|
||||
Status: input.Status,
|
||||
Notes: input.Notes,
|
||||
AppliedAt: input.AppliedAt,
|
||||
}
|
||||
if err := r.Store.DB.Create(&app).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
// UpdateJobApplication is the resolver for the updateJobApplication field.
|
||||
func (r *mutationResolver) UpdateJobApplication(ctx context.Context, id int, input model.UpdateJobApplicationInput) (*models.JobApplication, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var app models.JobApplication
|
||||
if err := r.Store.DB.First(&app, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.JobTitle != nil {
|
||||
app.JobTitle = *input.JobTitle
|
||||
}
|
||||
if input.Company != nil {
|
||||
app.Company = *input.Company
|
||||
}
|
||||
if input.Location != nil {
|
||||
app.Location = input.Location
|
||||
}
|
||||
if input.URL != nil {
|
||||
app.URL = input.URL
|
||||
}
|
||||
if input.Status != nil {
|
||||
app.Status = *input.Status
|
||||
}
|
||||
if input.Notes != nil {
|
||||
app.Notes = input.Notes
|
||||
}
|
||||
if input.AppliedAt != nil {
|
||||
app.AppliedAt = input.AppliedAt
|
||||
}
|
||||
if err := r.Store.DB.Save(&app).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
// DeleteJobApplication is the resolver for the deleteJobApplication field.
|
||||
func (r *mutationResolver) DeleteJobApplication(ctx context.Context, id int) (bool, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return false, fmt.Errorf("admin access required")
|
||||
}
|
||||
if err := r.Store.DB.Delete(&models.JobApplication{}, id).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// JobApplications is the resolver for the jobApplications field.
|
||||
func (r *queryResolver) JobApplications(ctx context.Context) ([]*models.JobApplication, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var apps []*models.JobApplication
|
||||
if err := r.Store.DB.Order("created_at desc").Find(&apps).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
// JobApplication is the resolver for the jobApplication field.
|
||||
func (r *queryResolver) JobApplication(ctx context.Context, id int) (*models.JobApplication, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var app models.JobApplication
|
||||
if err := r.Store.DB.First(&app, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
// JobApplication returns JobApplicationResolver implementation.
|
||||
func (r *Resolver) JobApplication() JobApplicationResolver { return &jobApplicationResolver{r} }
|
||||
|
||||
|
||||
@@ -21,6 +21,19 @@ func (r *messageResolver) AuthorID(ctx context.Context, obj *models.Message) (in
|
||||
return int(obj.AuthorID), nil
|
||||
}
|
||||
|
||||
// Messages is the resolver for the messages field.
|
||||
func (r *queryResolver) Messages(ctx context.Context) ([]*models.Message, error) {
|
||||
var messages []models.Message
|
||||
if err := r.Store.DB.Order("created_at DESC").Find(&messages).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Message, len(messages))
|
||||
for i := range messages {
|
||||
result[i] = &messages[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Message returns MessageResolver implementation.
|
||||
func (r *Resolver) Message() MessageResolver { return &messageResolver{r} }
|
||||
|
||||
|
||||
@@ -7,15 +7,111 @@ package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/models"
|
||||
)
|
||||
|
||||
// CreatePost is the resolver for the createPost field.
|
||||
func (r *mutationResolver) CreatePost(ctx context.Context, input model.CreatePostInput) (*models.Post, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
userID, ok := UserIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
post := models.Post{Title: input.Title, Content: input.Content, AuthorID: userID}
|
||||
if err := r.Store.DB.Create(&post).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// UpdatePost is the resolver for the updatePost field.
|
||||
func (r *mutationResolver) UpdatePost(ctx context.Context, id int, input model.UpdatePostInput) (*models.Post, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
userID, ok := UserIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
var post models.Post
|
||||
if err := r.Store.DB.First(&post, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("post not found")
|
||||
}
|
||||
|
||||
if post.AuthorID != userID {
|
||||
return nil, fmt.Errorf("you can only update your own posts")
|
||||
}
|
||||
|
||||
post.Title = input.Title
|
||||
post.Content = input.Content
|
||||
if err := r.Store.DB.Save(&post).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// DeletePost is the resolver for the deletePost field.
|
||||
func (r *mutationResolver) DeletePost(ctx context.Context, id int) (*models.Post, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
userID, ok := UserIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
var post models.Post
|
||||
if err := r.Store.DB.First(&post, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("post not found")
|
||||
}
|
||||
|
||||
if post.AuthorID != userID {
|
||||
return nil, fmt.Errorf("you can only delete your own posts")
|
||||
}
|
||||
|
||||
r.Store.DB.Delete(&post)
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// ID is the resolver for the id field.
|
||||
func (r *postResolver) ID(ctx context.Context, obj *models.Post) (int, error) {
|
||||
return int(obj.ID), nil
|
||||
}
|
||||
|
||||
// Posts is the resolver for the posts field.
|
||||
func (r *queryResolver) Posts(ctx context.Context) ([]*models.Post, error) {
|
||||
var posts []models.Post
|
||||
if err := r.Store.DB.Preload("Author").Order("created_at DESC").Find(&posts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Post, len(posts))
|
||||
for i := range posts {
|
||||
result[i] = &posts[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Post is the resolver for the post field.
|
||||
func (r *queryResolver) Post(ctx context.Context, id int) (*models.Post, error) {
|
||||
var post models.Post
|
||||
if err := r.Store.DB.Preload("Author").First(&post, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("post not found")
|
||||
}
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// Post returns PostResolver implementation.
|
||||
func (r *Resolver) Post() PostResolver { return &postResolver{r} }
|
||||
|
||||
|
||||
@@ -11,6 +11,19 @@ import (
|
||||
"adam-french.co.uk/backend/models"
|
||||
)
|
||||
|
||||
// RowingSessions is the resolver for the rowingSessions field.
|
||||
func (r *queryResolver) RowingSessions(ctx context.Context) ([]*models.Rowing, error) {
|
||||
var rows []models.Rowing
|
||||
if err := r.Store.DB.Order("created_at DESC").Find(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Rowing, len(rows))
|
||||
for i := range rows {
|
||||
result[i] = &rows[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ID is the resolver for the id field.
|
||||
func (r *rowingResolver) ID(ctx context.Context, obj *models.Rowing) (int, error) {
|
||||
return int(obj.ID), nil
|
||||
|
||||
@@ -5,620 +5,11 @@ package graph
|
||||
// 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
|
||||
}
|
||||
|
||||
// CreatePost is the resolver for the createPost field.
|
||||
func (r *mutationResolver) CreatePost(ctx context.Context, input model.CreatePostInput) (*models.Post, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
userID, ok := UserIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
post := models.Post{Title: input.Title, Content: input.Content, AuthorID: userID}
|
||||
if err := r.Store.DB.Create(&post).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// UpdatePost is the resolver for the updatePost field.
|
||||
func (r *mutationResolver) UpdatePost(ctx context.Context, id int, input model.UpdatePostInput) (*models.Post, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
userID, ok := UserIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
var post models.Post
|
||||
if err := r.Store.DB.First(&post, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("post not found")
|
||||
}
|
||||
|
||||
if post.AuthorID != userID {
|
||||
return nil, fmt.Errorf("you can only update your own posts")
|
||||
}
|
||||
|
||||
post.Title = input.Title
|
||||
post.Content = input.Content
|
||||
if err := r.Store.DB.Save(&post).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// DeletePost is the resolver for the deletePost field.
|
||||
func (r *mutationResolver) DeletePost(ctx context.Context, id int) (*models.Post, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
userID, ok := UserIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
var post models.Post
|
||||
if err := r.Store.DB.First(&post, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("post not found")
|
||||
}
|
||||
|
||||
if post.AuthorID != userID {
|
||||
return nil, fmt.Errorf("you can only delete your own posts")
|
||||
}
|
||||
|
||||
r.Store.DB.Delete(&post)
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// CreateUser is the resolver for the createUser field.
|
||||
func (r *mutationResolver) CreateUser(ctx context.Context, input model.CreateUserInput) (*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(input.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := models.User{Username: input.Username, Password: hashedPassword}
|
||||
if err := r.Store.DB.Create(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// DeleteUser is the resolver for the deleteUser field.
|
||||
func (r *mutationResolver) DeleteUser(ctx context.Context, id int) (*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := r.Store.DB.First(&user, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
if err := r.Store.DB.Delete(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// SetUserAdmin is the resolver for the setUserAdmin field.
|
||||
func (r *mutationResolver) SetUserAdmin(ctx context.Context, id int, admin bool) (*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
callerID, ok := UserIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
if uint(id) == callerID {
|
||||
return nil, fmt.Errorf("cannot change your own admin status")
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := r.Store.DB.First(&user, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
user.Admin = admin
|
||||
if err := r.Store.DB.Save(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// CreateFavorite is the resolver for the createFavorite field.
|
||||
func (r *mutationResolver) CreateFavorite(ctx context.Context, input model.CreateFavoriteInput) (*models.Favorite, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
favorite := models.Favorite{Type: input.Type, Name: input.Name, Link: input.Link}
|
||||
if err := r.Store.DB.Create(&favorite).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &favorite, nil
|
||||
}
|
||||
|
||||
// CreateActivity is the resolver for the createActivity field.
|
||||
func (r *mutationResolver) CreateActivity(ctx context.Context, input model.CreateActivityInput) (*models.Activity, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
activity := models.Activity{Type: input.Type, Name: input.Name, Link: input.Link}
|
||||
if err := r.Store.DB.Create(&activity).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &activity, nil
|
||||
}
|
||||
|
||||
// CreateJobApplication is the resolver for the createJobApplication field.
|
||||
func (r *mutationResolver) CreateJobApplication(ctx context.Context, input model.CreateJobApplicationInput) (*models.JobApplication, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
app := models.JobApplication{
|
||||
JobTitle: input.JobTitle,
|
||||
Company: input.Company,
|
||||
Location: input.Location,
|
||||
URL: input.URL,
|
||||
Status: input.Status,
|
||||
Notes: input.Notes,
|
||||
AppliedAt: input.AppliedAt,
|
||||
}
|
||||
if err := r.Store.DB.Create(&app).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
// UpdateJobApplication is the resolver for the updateJobApplication field.
|
||||
func (r *mutationResolver) UpdateJobApplication(ctx context.Context, id int, input model.UpdateJobApplicationInput) (*models.JobApplication, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var app models.JobApplication
|
||||
if err := r.Store.DB.First(&app, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.JobTitle != nil {
|
||||
app.JobTitle = *input.JobTitle
|
||||
}
|
||||
if input.Company != nil {
|
||||
app.Company = *input.Company
|
||||
}
|
||||
if input.Location != nil {
|
||||
app.Location = input.Location
|
||||
}
|
||||
if input.URL != nil {
|
||||
app.URL = input.URL
|
||||
}
|
||||
if input.Status != nil {
|
||||
app.Status = *input.Status
|
||||
}
|
||||
if input.Notes != nil {
|
||||
app.Notes = input.Notes
|
||||
}
|
||||
if input.AppliedAt != nil {
|
||||
app.AppliedAt = input.AppliedAt
|
||||
}
|
||||
if err := r.Store.DB.Save(&app).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
// CreateBookmark is the resolver for the createBookmark field.
|
||||
func (r *mutationResolver) CreateBookmark(ctx context.Context, input model.CreateBookmarkInput) (*models.Bookmark, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
bookmark := models.Bookmark{Category: input.Category, Name: input.Name, Link: input.Link}
|
||||
if err := r.Store.DB.Create(&bookmark).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bookmark, nil
|
||||
}
|
||||
|
||||
// DeleteBookmark is the resolver for the deleteBookmark field.
|
||||
func (r *mutationResolver) DeleteBookmark(ctx context.Context, id int) (*models.Bookmark, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var bookmark models.Bookmark
|
||||
if err := r.Store.DB.First(&bookmark, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.Store.DB.Delete(&bookmark).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &bookmark, nil
|
||||
}
|
||||
|
||||
// DeleteJobApplication is the resolver for the deleteJobApplication field.
|
||||
func (r *mutationResolver) DeleteJobApplication(ctx context.Context, id int) (bool, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return false, fmt.Errorf("admin access required")
|
||||
}
|
||||
if err := r.Store.DB.Delete(&models.JobApplication{}, id).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CreateJobAppReference is the resolver for the createJobAppReference field.
|
||||
func (r *mutationResolver) CreateJobAppReference(ctx context.Context, input model.CreateJobAppReferenceInput) (*models.JobAppReference, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
ref := models.JobAppReference{
|
||||
Category: input.Category,
|
||||
Label: input.Label,
|
||||
Value: input.Value,
|
||||
}
|
||||
if input.SortOrder != nil {
|
||||
ref.SortOrder = *input.SortOrder
|
||||
}
|
||||
if err := r.Store.DB.Create(&ref).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ref, nil
|
||||
}
|
||||
|
||||
// UpdateJobAppReference is the resolver for the updateJobAppReference field.
|
||||
func (r *mutationResolver) UpdateJobAppReference(ctx context.Context, id int, input model.UpdateJobAppReferenceInput) (*models.JobAppReference, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var ref models.JobAppReference
|
||||
if err := r.Store.DB.First(&ref, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if input.Category != nil {
|
||||
ref.Category = *input.Category
|
||||
}
|
||||
if input.Label != nil {
|
||||
ref.Label = *input.Label
|
||||
}
|
||||
if input.Value != nil {
|
||||
ref.Value = *input.Value
|
||||
}
|
||||
if input.SortOrder != nil {
|
||||
ref.SortOrder = *input.SortOrder
|
||||
}
|
||||
if err := r.Store.DB.Save(&ref).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ref, nil
|
||||
}
|
||||
|
||||
// DeleteJobAppReference is the resolver for the deleteJobAppReference field.
|
||||
func (r *mutationResolver) DeleteJobAppReference(ctx context.Context, id int) (bool, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return false, fmt.Errorf("admin access required")
|
||||
}
|
||||
if err := r.Store.DB.Delete(&models.JobAppReference{}, id).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Users is the resolver for the users field.
|
||||
func (r *queryResolver) Users(ctx context.Context) ([]*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var users []models.User
|
||||
if err := r.Store.DB.Find(&users).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.User, len(users))
|
||||
for i := range users {
|
||||
result[i] = &users[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// User is the resolver for the user field.
|
||||
func (r *queryResolver) User(ctx context.Context, id int) (*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var user models.User
|
||||
if err := r.Store.DB.First(&user, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// Posts is the resolver for the posts field.
|
||||
func (r *queryResolver) Posts(ctx context.Context) ([]*models.Post, error) {
|
||||
var posts []models.Post
|
||||
if err := r.Store.DB.Preload("Author").Order("created_at DESC").Find(&posts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Post, len(posts))
|
||||
for i := range posts {
|
||||
result[i] = &posts[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Post is the resolver for the post field.
|
||||
func (r *queryResolver) Post(ctx context.Context, id int) (*models.Post, error) {
|
||||
var post models.Post
|
||||
if err := r.Store.DB.Preload("Author").First(&post, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("post not found")
|
||||
}
|
||||
return &post, nil
|
||||
}
|
||||
|
||||
// Activities is the resolver for the activities field.
|
||||
func (r *queryResolver) Activities(ctx context.Context) ([]*models.Activity, error) {
|
||||
var activities []models.Activity
|
||||
if err := r.Store.DB.Order("created_at DESC").Find(&activities).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Activity, len(activities))
|
||||
for i := range activities {
|
||||
result[i] = &activities[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Favorites is the resolver for the favorites field.
|
||||
func (r *queryResolver) Favorites(ctx context.Context) ([]*models.Favorite, error) {
|
||||
var favorites []models.Favorite
|
||||
if err := r.Store.DB.Order("created_at DESC").Find(&favorites).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Favorite, len(favorites))
|
||||
for i := range favorites {
|
||||
result[i] = &favorites[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// RowingSessions is the resolver for the rowingSessions field.
|
||||
func (r *queryResolver) RowingSessions(ctx context.Context) ([]*models.Rowing, error) {
|
||||
var rows []models.Rowing
|
||||
if err := r.Store.DB.Order("created_at DESC").Find(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Rowing, len(rows))
|
||||
for i := range rows {
|
||||
result[i] = &rows[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Messages is the resolver for the messages field.
|
||||
func (r *queryResolver) Messages(ctx context.Context) ([]*models.Message, error) {
|
||||
var messages []models.Message
|
||||
if err := r.Store.DB.Order("created_at DESC").Find(&messages).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Message, len(messages))
|
||||
for i := range messages {
|
||||
result[i] = &messages[i]
|
||||
}
|
||||
return result, 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
|
||||
}
|
||||
|
||||
// Bookmarks is the resolver for the bookmarks field.
|
||||
func (r *queryResolver) Bookmarks(ctx context.Context) ([]*models.Bookmark, error) {
|
||||
var bookmarks []models.Bookmark
|
||||
if err := r.Store.DB.Order("category ASC, created_at ASC").Find(&bookmarks).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.Bookmark, len(bookmarks))
|
||||
for i := range bookmarks {
|
||||
result[i] = &bookmarks[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// JobApplications is the resolver for the jobApplications field.
|
||||
func (r *queryResolver) JobApplications(ctx context.Context) ([]*models.JobApplication, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var apps []*models.JobApplication
|
||||
if err := r.Store.DB.Order("created_at desc").Find(&apps).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
// JobApplication is the resolver for the jobApplication field.
|
||||
func (r *queryResolver) JobApplication(ctx context.Context, id int) (*models.JobApplication, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var app models.JobApplication
|
||||
if err := r.Store.DB.First(&app, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
// JobAppReferences is the resolver for the jobAppReferences field.
|
||||
func (r *queryResolver) JobAppReferences(ctx context.Context) ([]*models.JobAppReference, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var refs []*models.JobAppReference
|
||||
if err := r.Store.DB.Order("category ASC, sort_order ASC, created_at ASC").Find(&refs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
// Mutation returns MutationResolver implementation.
|
||||
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
|
||||
|
||||
// Query returns QueryResolver implementation.
|
||||
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
|
||||
|
||||
type (
|
||||
mutationResolver struct{ *Resolver }
|
||||
queryResolver struct{ *Resolver }
|
||||
)
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
|
||||
@@ -12,3 +12,11 @@ input CreateActivityInput {
|
||||
name: String!
|
||||
link: String
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
activities: [Activity!]!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createActivity(input: CreateActivityInput!): Activity!
|
||||
}
|
||||
|
||||
@@ -6,3 +6,13 @@ input LoginInput {
|
||||
type AuthPayload {
|
||||
user: User!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
me: User
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
login(input: LoginInput!): AuthPayload!
|
||||
logout: Boolean!
|
||||
refreshToken: AuthPayload!
|
||||
}
|
||||
|
||||
@@ -12,3 +12,12 @@ input CreateBookmarkInput {
|
||||
name: String!
|
||||
link: String!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
bookmarks: [Bookmark!]!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createBookmark(input: CreateBookmarkInput!): Bookmark!
|
||||
deleteBookmark(id: ID!): Bookmark!
|
||||
}
|
||||
|
||||
@@ -12,3 +12,11 @@ input CreateFavoriteInput {
|
||||
name: String!
|
||||
link: String
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
favorites: [Favorite!]!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createFavorite(input: CreateFavoriteInput!): Favorite!
|
||||
}
|
||||
|
||||
@@ -6,3 +6,7 @@ type GiteaFeedItem {
|
||||
commitMessage: String!
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
giteaFeed: GiteaFeedItem
|
||||
}
|
||||
|
||||
@@ -21,3 +21,13 @@ input UpdateJobAppReferenceInput {
|
||||
value: String
|
||||
sortOrder: Int
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
jobAppReferences: [JobAppReference!]!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createJobAppReference(input: CreateJobAppReferenceInput!): JobAppReference!
|
||||
updateJobAppReference(id: ID!, input: UpdateJobAppReferenceInput!): JobAppReference!
|
||||
deleteJobAppReference(id: ID!): Boolean!
|
||||
}
|
||||
|
||||
@@ -30,3 +30,14 @@ input UpdateJobApplicationInput {
|
||||
notes: String
|
||||
appliedAt: Time
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
jobApplications: [JobApplication!]!
|
||||
jobApplication(id: ID!): JobApplication
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createJobApplication(input: CreateJobApplicationInput!): JobApplication!
|
||||
updateJobApplication(id: ID!, input: UpdateJobApplicationInput!): JobApplication!
|
||||
deleteJobApplication(id: ID!): Boolean!
|
||||
}
|
||||
|
||||
@@ -5,3 +5,7 @@ type Message {
|
||||
fileUrl: String
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
messages: [Message!]!
|
||||
}
|
||||
|
||||
@@ -16,3 +16,14 @@ input UpdatePostInput {
|
||||
title: String!
|
||||
content: String!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
posts: [Post!]!
|
||||
post(id: ID!): Post
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createPost(input: CreatePostInput!): Post!
|
||||
updatePost(id: ID!, input: UpdatePostInput!): Post!
|
||||
deletePost(id: ID!): Post!
|
||||
}
|
||||
|
||||
@@ -7,3 +7,7 @@ type Rowing {
|
||||
timePer500m: Float!
|
||||
calories: Float!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
rowingSessions: [Rowing!]!
|
||||
}
|
||||
|
||||
@@ -1,43 +1,4 @@
|
||||
scalar Time
|
||||
|
||||
type Query {
|
||||
users: [User!]!
|
||||
user(id: ID!): User
|
||||
posts: [Post!]!
|
||||
post(id: ID!): Post
|
||||
activities: [Activity!]!
|
||||
favorites: [Favorite!]!
|
||||
rowingSessions: [Rowing!]!
|
||||
messages: [Message!]!
|
||||
spotifyListening: SpotifyPlaying
|
||||
spotifyRecent: [SpotifyRecentItem!]
|
||||
giteaFeed: GiteaFeedItem
|
||||
steamStatus: SteamStatus
|
||||
me: User
|
||||
bookmarks: [Bookmark!]!
|
||||
jobApplications: [JobApplication!]!
|
||||
jobApplication(id: ID!): JobApplication
|
||||
jobAppReferences: [JobAppReference!]!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
login(input: LoginInput!): AuthPayload!
|
||||
logout: Boolean!
|
||||
refreshToken: AuthPayload!
|
||||
createPost(input: CreatePostInput!): Post!
|
||||
updatePost(id: ID!, input: UpdatePostInput!): Post!
|
||||
deletePost(id: ID!): Post!
|
||||
createUser(input: CreateUserInput!): User!
|
||||
deleteUser(id: ID!): User!
|
||||
setUserAdmin(id: ID!, admin: Boolean!): User!
|
||||
createFavorite(input: CreateFavoriteInput!): Favorite!
|
||||
createActivity(input: CreateActivityInput!): Activity!
|
||||
createJobApplication(input: CreateJobApplicationInput!): JobApplication!
|
||||
updateJobApplication(id: ID!, input: UpdateJobApplicationInput!): JobApplication!
|
||||
createBookmark(input: CreateBookmarkInput!): Bookmark!
|
||||
deleteBookmark(id: ID!): Bookmark!
|
||||
deleteJobApplication(id: ID!): Boolean!
|
||||
createJobAppReference(input: CreateJobAppReferenceInput!): JobAppReference!
|
||||
updateJobAppReference(id: ID!, input: UpdateJobAppReferenceInput!): JobAppReference!
|
||||
deleteJobAppReference(id: ID!): Boolean!
|
||||
}
|
||||
type Query
|
||||
type Mutation
|
||||
|
||||
@@ -26,3 +26,8 @@ type SpotifyRecentItem {
|
||||
track: SpotifyTrack!
|
||||
playedAt: Time!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
spotifyListening: SpotifyPlaying
|
||||
spotifyRecent: [SpotifyRecentItem!]
|
||||
}
|
||||
|
||||
@@ -10,3 +10,7 @@ type SteamStatus {
|
||||
online: Boolean!
|
||||
recentGames: [SteamGame!]!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
steamStatus: SteamStatus
|
||||
}
|
||||
|
||||
@@ -10,3 +10,14 @@ input CreateUserInput {
|
||||
username: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
users: [User!]!
|
||||
user(id: ID!): User
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createUser(input: CreateUserInput!): User!
|
||||
deleteUser(id: ID!): User!
|
||||
setUserAdmin(id: ID!, admin: Boolean!): User!
|
||||
}
|
||||
|
||||
@@ -1,58 +1,18 @@
|
||||
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"
|
||||
"time"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"github.com/zmb3/spotify/v2"
|
||||
spotify "github.com/zmb3/spotify/v2"
|
||||
)
|
||||
|
||||
func mapSpotifyImages(images []spotify.Image) []*model.SpotifyImage {
|
||||
result := make([]*model.SpotifyImage, len(images))
|
||||
for i, img := range images {
|
||||
result[i] = &model.SpotifyImage{URL: img.URL}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func mapSpotifyTrack(track *spotify.FullTrack) *model.SpotifyTrack {
|
||||
artists := make([]*model.SpotifyArtist, len(track.Artists))
|
||||
for i, a := range track.Artists {
|
||||
artists[i] = &model.SpotifyArtist{Name: a.Name}
|
||||
}
|
||||
return &model.SpotifyTrack{
|
||||
Name: track.Name,
|
||||
Artists: artists,
|
||||
Album: &model.SpotifyAlbum{
|
||||
Name: track.Album.Name,
|
||||
Images: mapSpotifyImages(track.Album.Images),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mapRecentItems(items []spotify.RecentlyPlayedItem) []*model.SpotifyRecentItem {
|
||||
result := make([]*model.SpotifyRecentItem, len(items))
|
||||
for i, item := range items {
|
||||
artists := make([]*model.SpotifyArtist, len(item.Track.Artists))
|
||||
for j, a := range item.Track.Artists {
|
||||
artists[j] = &model.SpotifyArtist{Name: a.Name}
|
||||
}
|
||||
result[i] = &model.SpotifyRecentItem{
|
||||
PlayedAt: item.PlayedAt,
|
||||
Track: &model.SpotifyTrack{
|
||||
Name: item.Track.Name,
|
||||
Artists: artists,
|
||||
Album: &model.SpotifyAlbum{
|
||||
Name: item.Track.Album.Name,
|
||||
Images: mapSpotifyImages(item.Track.Album.Images),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SpotifyListening is the resolver for the spotifyListening field.
|
||||
func (r *queryResolver) SpotifyListening(ctx context.Context) (*model.SpotifyPlaying, error) {
|
||||
if r.Store.SpotifyClient == nil {
|
||||
|
||||
51
backend/graph/spotify_helpers.go
Normal file
51
backend/graph/spotify_helpers.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"github.com/zmb3/spotify/v2"
|
||||
)
|
||||
|
||||
func mapSpotifyImages(images []spotify.Image) []*model.SpotifyImage {
|
||||
result := make([]*model.SpotifyImage, len(images))
|
||||
for i, img := range images {
|
||||
result[i] = &model.SpotifyImage{URL: img.URL}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func mapSpotifyTrack(track *spotify.FullTrack) *model.SpotifyTrack {
|
||||
artists := make([]*model.SpotifyArtist, len(track.Artists))
|
||||
for i, a := range track.Artists {
|
||||
artists[i] = &model.SpotifyArtist{Name: a.Name}
|
||||
}
|
||||
return &model.SpotifyTrack{
|
||||
Name: track.Name,
|
||||
Artists: artists,
|
||||
Album: &model.SpotifyAlbum{
|
||||
Name: track.Album.Name,
|
||||
Images: mapSpotifyImages(track.Album.Images),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mapRecentItems(items []spotify.RecentlyPlayedItem) []*model.SpotifyRecentItem {
|
||||
result := make([]*model.SpotifyRecentItem, len(items))
|
||||
for i, item := range items {
|
||||
artists := make([]*model.SpotifyArtist, len(item.Track.Artists))
|
||||
for j, a := range item.Track.Artists {
|
||||
artists[j] = &model.SpotifyArtist{Name: a.Name}
|
||||
}
|
||||
result[i] = &model.SpotifyRecentItem{
|
||||
PlayedAt: item.PlayedAt,
|
||||
Track: &model.SpotifyTrack{
|
||||
Name: item.Track.Name,
|
||||
Artists: artists,
|
||||
Album: &model.SpotifyAlbum{
|
||||
Name: item.Track.Album.Name,
|
||||
Images: mapSpotifyImages(item.Track.Album.Images),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,28 +1,18 @@
|
||||
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"
|
||||
"time"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/services"
|
||||
)
|
||||
|
||||
func mapSteamGames(games []services.SteamRecentGame) []*model.SteamGame {
|
||||
result := make([]*model.SteamGame, len(games))
|
||||
for i, g := range games {
|
||||
result[i] = &model.SteamGame{
|
||||
AppID: g.AppID,
|
||||
Name: g.Name,
|
||||
Playtime2Weeks: g.Playtime2Weeks,
|
||||
PlaytimeForever: g.PlaytimeForever,
|
||||
HeaderImageURL: fmt.Sprintf("https://cdn.akamai.steamstatic.com/steam/apps/%d/header.jpg", g.AppID),
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SteamStatus is the resolver for the steamStatus field.
|
||||
func (r *queryResolver) SteamStatus(ctx context.Context) (*model.SteamStatus, error) {
|
||||
if r.Store.SteamAPIKey == "" {
|
||||
|
||||
22
backend/graph/steam_helpers.go
Normal file
22
backend/graph/steam_helpers.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/services"
|
||||
)
|
||||
|
||||
func mapSteamGames(games []services.SteamRecentGame) []*model.SteamGame {
|
||||
result := make([]*model.SteamGame, len(games))
|
||||
for i, g := range games {
|
||||
result[i] = &model.SteamGame{
|
||||
AppID: g.AppID,
|
||||
Name: g.Name,
|
||||
Playtime2Weeks: g.Playtime2Weeks,
|
||||
PlaytimeForever: g.PlaytimeForever,
|
||||
HeaderImageURL: fmt.Sprintf("https://cdn.akamai.steamstatic.com/steam/apps/%d/header.jpg", g.AppID),
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -7,10 +7,106 @@ package graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"adam-french.co.uk/backend/graph/model"
|
||||
"adam-french.co.uk/backend/models"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// CreateUser is the resolver for the createUser field.
|
||||
func (r *mutationResolver) CreateUser(ctx context.Context, input model.CreateUserInput) (*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(input.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := models.User{Username: input.Username, Password: hashedPassword}
|
||||
if err := r.Store.DB.Create(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// DeleteUser is the resolver for the deleteUser field.
|
||||
func (r *mutationResolver) DeleteUser(ctx context.Context, id int) (*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := r.Store.DB.First(&user, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
if err := r.Store.DB.Delete(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// SetUserAdmin is the resolver for the setUserAdmin field.
|
||||
func (r *mutationResolver) SetUserAdmin(ctx context.Context, id int, admin bool) (*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
|
||||
callerID, ok := UserIDFromCtx(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
if uint(id) == callerID {
|
||||
return nil, fmt.Errorf("cannot change your own admin status")
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := r.Store.DB.First(&user, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
|
||||
user.Admin = admin
|
||||
if err := r.Store.DB.Save(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// Users is the resolver for the users field.
|
||||
func (r *queryResolver) Users(ctx context.Context) ([]*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var users []models.User
|
||||
if err := r.Store.DB.Find(&users).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*models.User, len(users))
|
||||
for i := range users {
|
||||
result[i] = &users[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// User is the resolver for the user field.
|
||||
func (r *queryResolver) User(ctx context.Context, id int) (*models.User, error) {
|
||||
if !IsAdminFromCtx(ctx) {
|
||||
return nil, fmt.Errorf("admin access required")
|
||||
}
|
||||
var user models.User
|
||||
if err := r.Store.DB.First(&user, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// ID is the resolver for the id field.
|
||||
func (r *userResolver) ID(ctx context.Context, obj *models.User) (int, error) {
|
||||
return int(obj.ID), nil
|
||||
|
||||
Reference in New Issue
Block a user