Files
web_server/backend/main.go
Adam French 0360b1f7f1
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 1s
Consolidate frontend REST calls with GraphQL
Replace 5 separate REST calls on home page load with a single GraphQL
query. Add homeData store that fetches posts, favorites, activities,
spotify, and auth in one request. Convert all admin mutations and
auth flows to use GraphQL. Add album images to Spotify GraphQL schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 15:29:22 +00:00

144 lines
4.4 KiB
Go

package main
import (
"fmt"
"io"
"log"
"os"
"time"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/gin-gonic/gin"
"adam-french.co.uk/backend/graph"
"adam-french.co.uk/backend/handlers"
"adam-french.co.uk/backend/services"
)
func main() {
logsDir := "/backend/logs"
logFile, err := os.OpenFile(logsDir+"/go.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
panic(err)
}
gin.DefaultWriter = io.MultiWriter(os.Stdout, logFile)
r := gin.Default()
err = r.SetTrustedProxies([]string{"172.28.0.0/16"})
if err != nil {
panic(err)
}
dbUser := os.Getenv("POSTGRES_USER")
dbPassword := os.Getenv("POSTGRES_PASSWORD")
dbName := os.Getenv("POSTGRES_DB")
dbHost := os.Getenv("POSTGRES_HOST")
dbPort := os.Getenv("POSTGRES_PORT")
dbConfig := services.SQLConfig{User: dbUser, Password: dbPassword, DBName: dbName, Host: dbHost, Port: dbPort}
db, err := services.InitDatabase(&dbConfig)
if err != nil {
log.Fatal(err)
}
if os.Getenv("SEED_DB") == "true" {
services.SeedDatabase(db)
}
domainName := os.Getenv("DOMAIN")
services.InitWebSocket(db, domainName)
// SPOTIFY
spotifyAuthState := os.Getenv("SPOTIFY_AUTH_STATE")
spotifyRedirectURL := os.Getenv("SPOTIFY_REDIRECT_URI")
spotifyClientID := os.Getenv("SPOTIFY_CLIENT_ID")
spotifyClientSecret := os.Getenv("SPOTIFY_CLIENT_SECRET")
spotifyConfig := services.SpotifyConfig{AuthState: spotifyAuthState, RedirectURL: spotifyRedirectURL, ClientID: spotifyClientID, ClientSecret: spotifyClientSecret}
spotifyAuth, spotifyClient := services.InitSpotifyAuth(&spotifyConfig)
// CLAUDE
claudeAPIKey := os.Getenv("CLAUDE_API_KEY")
claudeConfig := services.ClaudeConfig{APIKey: claudeAPIKey}
claudeClient := services.InitClaude(&claudeConfig)
authSecret := os.Getenv("BACKEND_SECRET")
backendEndpoint := os.Getenv("BACKEND_ENDPOINT")
accessTokenLifetime := 24 * time.Hour
refreshTokenLifetime := 365 * 24 * time.Hour
authConfig := services.AuthConfig{Secret: []byte(authSecret), Domain: domainName, RefreshTokenLifetime: refreshTokenLifetime, AccessTokenLifetime: accessTokenLifetime, Endpoint: backendEndpoint}
auth := services.InitAuth(&authConfig)
notesDir := "/backend/notes"
notesConfig := services.NotesConfig{Dir: notesDir}
notes := services.InitNotes(&notesConfig)
store := handlers.Store{DB: db, SpotifyAuth: spotifyAuth, SpotifyClient: spotifyClient, ClaudeClient: claudeClient, Auth: auth, Notes: notes}
protected := r.Group("/", store.AuthMiddlewear)
admin := r.Group("/", store.AuthMiddlewear, store.AdminMiddleware)
// FAVORITES
r.GET("/favorites", store.GetFavorites)
admin.POST("/favorites", store.CreateFavorite)
// ROWING
r.GET("/rowing", store.GetRowing)
admin.POST("/rowing", store.CreateRowing)
// ACTIVITIES
r.GET("/activity", store.GetActivity)
admin.POST("/activity", store.CreateActivity)
// POSTS
r.GET("/posts", store.GetPosts)
admin.POST("/posts", store.CreatePost)
r.GET("/posts/:id", store.GetPost)
admin.PUT("/posts/:id", store.UpdatePost)
admin.DELETE("/posts/:id", store.DeletePost)
// USERS
r.GET("/user/:id", store.GetUser)
admin.PUT("/user/:id", store.UpdateUser)
admin.DELETE("/user/:id", store.DeleteUser)
r.GET("/user", store.GetUsers)
admin.POST("/user", store.CreateUser)
admin.PATCH("/user/:id/admin", store.SetUserAdmin)
// AUTH
r.POST("/auth/login", store.Login)
r.POST("/auth/refresh", store.RefreshToken)
r.GET("/auth/check", store.CheckToken)
r.POST("/auth/logout", store.Logout)
// SPOTIFY
r.GET("/spotify/callback", store.CompleteSpotifyAuth)
r.GET("/spotify/listening", store.ListeningTo)
r.GET("/spotify/recent", store.RecentlyPlayed)
// r.POST("/spotify", store.SendSong)
// MESSAGES
r.GET("/ws", store.ConnectWebSocket)
protected.POST("/messages/upload", store.UploadMessageFile)
// NOTES
r.GET("/notes/*path", store.GetNoteFile)
// GRAPHQL
gqlSrv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{
Resolvers: &graph.Resolver{Store: &store},
}))
r.POST("/graphql", graph.AuthContextMiddleware(auth), func(c *gin.Context) {
gqlSrv.ServeHTTP(c.Writer, c.Request)
})
r.GET("/graphql", func(c *gin.Context) {
playground.Handler("GraphQL Playground", "/graphql").ServeHTTP(c.Writer, c.Request)
})
// HELLO WORLD
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Hello World"})
})
port := os.Getenv("BACKEND_PORT")
r.Run(fmt.Sprintf(":%s", port))
}