All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 3m34s
Auth-protected CRUD for personal info (LinkedIn, GitHub, etc.) and experience entries, stored in the database so nothing sensitive is in the public repo. Displayed as a categorized panel on the Job Applications page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
715 lines
20 KiB
Go
715 lines
20 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"
|
|
"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")
|
|
}
|
|
|
|
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) {
|
|
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) {
|
|
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 }
|