package auth import ( "context" "crypto/rand" "encoding/base64" "errors" "codeberg.org/nextgo/dbots/internal/db" "codeberg.org/nextgo/dbots/internal/discord" "github.com/jackc/pgx/v5" ) // todo: api keysssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss // or sessions???????????? type Service struct { q *db.Queries client *discord.Client } func NewService(q *db.Queries, client *discord.Client) *Service { return &Service{q: q, client: client} } // GenerateState produces a random OAuth state parameter. func GenerateState() (string, error) { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { return "", err } return base64.URLEncoding.EncodeToString(b), nil } // Callback handles the OAuth callback: exchanges the code, fetches the user, // and upserts them in the database. Returns the local db.User. func (s *Service) Callback(ctx context.Context, code string) (*db.User, error) { token, err := s.client.ExchangeCode(ctx, code) if err != nil { return nil, err } dUser, err := s.client.GetCurrentUser(ctx, token.AccessToken) if err != nil { return nil, err } user, err := s.q.UpdateUser(ctx, db.UpdateUserParams{ ID: dUser.ID, Username: &dUser.Username, }) if err != nil { if errors.Is(err, pgx.ErrNoRows) { user, err = s.q.CreateUser(ctx, db.CreateUserParams{ ID: dUser.ID, Username: dUser.Username, }) if err != nil { return nil, err } } return nil, err } return user, nil }