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" "time" "adam-french.co.uk/backend/graph/model" "adam-french.co.uk/backend/models" "adam-french.co.uk/backend/services" spotify "github.com/zmb3/spotify/v2" "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 } // SpotifyListening is the resolver for the spotifyListening field. func (r *queryResolver) SpotifyListening(ctx context.Context) (*model.SpotifyPlaying, error) { if r.Store.SpotifyClient == nil { return nil, nil } playing, err := r.Store.SpotifyClient.PlayerCurrentlyPlaying(ctx) if err != nil { return nil, err } result := &model.SpotifyPlaying{Playing: playing.Playing} if playing.Item != nil { result.Track = mapSpotifyTrack(playing.Item) } return result, nil } // SpotifyRecent is the resolver for the spotifyRecent field. func (r *queryResolver) SpotifyRecent(ctx context.Context) ([]*model.SpotifyRecentItem, error) { if r.Store.SpotifyClient == nil { return []*model.SpotifyRecentItem{}, nil } if r.Store.RecentSongsFresh() { return mapRecentItems(*r.Store.RecentSongs), nil } opts := spotify.RecentlyPlayedOptions{Limit: 3} played, err := r.Store.SpotifyClient.PlayerRecentlyPlayedOpt(ctx, &opts) if err != nil { return nil, err } r.Store.RecentSongs = &played r.Store.RecentSongsFetchedAt = time.Now() return mapRecentItems(played), nil } // GiteaFeed is the resolver for the giteaFeed field. func (r *queryResolver) GiteaFeed(ctx context.Context) (*model.GiteaFeedItem, error) { if r.Store.GiteaFeedFresh() { return mapGiteaFeed(r.Store.GiteaFeed), nil } feed, err := services.FetchLatestFeed(r.Store.GiteaHost, r.Store.GiteaPort) if err != nil { return nil, err } if feed == nil { return nil, nil } r.Store.GiteaFeed = feed r.Store.GiteaFeedFetchedAt = time.Now() return mapGiteaFeed(feed), nil } // SteamStatus is the resolver for the steamStatus field. func (r *queryResolver) SteamStatus(ctx context.Context) (*model.SteamStatus, error) { if r.Store.SteamAPIKey == "" { return nil, nil } if r.Store.SteamFresh() { return &model.SteamStatus{ Online: r.Store.SteamOnline, RecentGames: mapSteamGames(r.Store.SteamRecentGames), }, nil } games, err := services.FetchRecentlyPlayedGames(r.Store.SteamAPIKey, r.Store.SteamID) if err != nil { return nil, err } summary, err := services.FetchPlayerSummary(r.Store.SteamAPIKey, r.Store.SteamID) if err != nil { return nil, err } online := false if summary != nil { online = summary.PersonaState > 0 } r.Store.SteamRecentGames = games r.Store.SteamOnline = online r.Store.SteamFetchedAt = time.Now() return &model.SteamStatus{ Online: online, RecentGames: mapSteamGames(games), }, 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 } type queryResolver struct{ *Resolver }