feat: initial backend scaffolding

migrating away from convex

Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
2025-11-10 00:19:30 +00:00
parent 5cc13a34b2
commit 1feac70f7f
16 changed files with 861 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
package drexa
import (
"fmt"
"strings"
)
type ServerConfigError struct {
Errors []error
}
func NewServerConfigError(errs ...error) *ServerConfigError {
return &ServerConfigError{Errors: errs}
}
func (e *ServerConfigError) Error() string {
sb := strings.Builder{}
sb.WriteString("invalid server config:\n")
for _, err := range e.Errors {
sb.WriteString(fmt.Sprintf(" - %s\n", err.Error()))
}
return sb.String()
}

View File

@@ -0,0 +1,84 @@
package drexa
import (
"encoding/hex"
"errors"
"fmt"
"os"
"strconv"
"github.com/get-drexa/drexa/internal/auth"
"github.com/get-drexa/drexa/internal/database"
"github.com/gofiber/fiber/v2"
)
type ServerConfig struct {
Port int
PostgresURL string
JWTIssuer string
JWTAudience string
JWTSecretKey []byte
}
func NewServer(c ServerConfig) *fiber.App {
app := fiber.New()
db := database.NewFromPostgres(c.PostgresURL)
authService := auth.NewService(db, auth.TokenConfig{
Issuer: c.JWTIssuer,
Audience: c.JWTAudience,
SecretKey: c.JWTSecretKey,
})
api := app.Group("/api")
auth.RegisterAPIRoutes(api, authService)
return app
}
// ServerConfigFromEnv creates a ServerConfig from environment variables.
func ServerConfigFromEnv() (*ServerConfig, error) {
c := ServerConfig{
PostgresURL: os.Getenv("POSTGRES_URL"),
JWTIssuer: os.Getenv("JWT_ISSUER"),
JWTAudience: os.Getenv("JWT_AUDIENCE"),
}
errs := []error{}
keyHex := os.Getenv("JWT_SECRET_KEY")
if keyHex == "" {
errs = append(errs, errors.New("JWT_SECRET_KEY is required"))
} else {
k, err := hex.DecodeString(keyHex)
if err != nil {
errs = append(errs, fmt.Errorf("failed to decode JWT_SECRET_KEY: %w", err))
}
c.JWTSecretKey = k
}
p, err := strconv.Atoi(os.Getenv("PORT"))
if err != nil {
errs = append(errs, fmt.Errorf("failed to parse PORT: %w", err))
}
c.Port = p
if c.PostgresURL == "" {
errs = append(errs, errors.New("POSTGRES_URL is required"))
}
if c.JWTIssuer == "" {
errs = append(errs, errors.New("JWT_ISSUER is required"))
}
if c.JWTAudience == "" {
errs = append(errs, errors.New("JWT_AUDIENCE is required"))
}
if len(c.JWTSecretKey) == 0 {
errs = append(errs, errors.New("JWT_SECRET_KEY is required"))
}
if len(errs) > 0 {
return nil, NewServerConfigError(errs...)
}
return &c, nil
}