feat: replace mock mode with real backend dev mode
Add --dev flag to `opal server start` that disables auth (injects userID=1 for all requests) and exposes a /auth/dev-session endpoint, so the frontend can develop against a real backend without OAuth config. Remove VITE_MOCK_MODE and all mock data/branches from the frontend stores. Add scripts/dev.sh to start both services locally. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+16
-5
@@ -97,20 +97,30 @@ var serverStartCmd = &cobra.Command{
|
||||
Examples:
|
||||
opal server start
|
||||
opal server start --addr :8080
|
||||
opal server start --dev
|
||||
opal server start --db /var/lib/opal/opal.db`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
addr, _ := cmd.Flags().GetString("addr")
|
||||
dbPath, _ := cmd.Flags().GetString("db")
|
||||
devMode, _ := cmd.Flags().GetBool("dev")
|
||||
|
||||
// Override DB path if specified
|
||||
if dbPath != "" {
|
||||
os.Setenv("OPAL_DB_PATH", dbPath)
|
||||
}
|
||||
|
||||
// Validate server configuration
|
||||
if err := validateServerConfig(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Server configuration validation failed:\n%v\n", err)
|
||||
os.Exit(1)
|
||||
// In dev mode, skip OAuth config validation
|
||||
if devMode {
|
||||
fmt.Println("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓")
|
||||
fmt.Println("┃ ⚠ DEV MODE ENABLED ⚠ ┃")
|
||||
fmt.Println("┃ Auth disabled — all requests use uid 1 ┃")
|
||||
fmt.Println("┃ Do NOT use in production! ┃")
|
||||
fmt.Println("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛")
|
||||
} else {
|
||||
if err := validateServerConfig(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Server configuration validation failed:\n%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Load config (read-only — uses defaults if no opal.yml exists)
|
||||
@@ -127,7 +137,7 @@ Examples:
|
||||
defer engine.CloseDB()
|
||||
|
||||
// Create and start server
|
||||
server := api.NewServer(addr)
|
||||
server := api.NewServer(addr, devMode)
|
||||
if err := server.Start(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error starting server: %v\n", err)
|
||||
os.Exit(1)
|
||||
@@ -194,6 +204,7 @@ func init() {
|
||||
|
||||
serverStartCmd.Flags().StringP("addr", "a", ":8080", "Server address")
|
||||
serverStartCmd.Flags().StringP("db", "d", "", "Database path (default: config directory)")
|
||||
serverStartCmd.Flags().Bool("dev", false, "Enable dev mode (no auth, no OAuth env vars required)")
|
||||
|
||||
keygenCmd.Flags().StringP("name", "n", "", "Name for this API key (e.g., device name)")
|
||||
keygenCmd.Flags().StringP("db", "d", "", "Database path (default: config directory)")
|
||||
|
||||
@@ -73,6 +73,16 @@ func GetUserID(r *http.Request) int {
|
||||
return userID
|
||||
}
|
||||
|
||||
// DevAuthMiddleware always injects userID=1 into context — for local dev only
|
||||
func DevAuthMiddleware() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.WithValue(r.Context(), userIDKey, 1)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// CORSMiddleware adds CORS headers for future web frontend
|
||||
func CORSMiddleware() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
|
||||
@@ -11,15 +11,17 @@ import (
|
||||
|
||||
// Server represents the API server
|
||||
type Server struct {
|
||||
router chi.Router
|
||||
addr string
|
||||
router chi.Router
|
||||
addr string
|
||||
devMode bool
|
||||
}
|
||||
|
||||
// NewServer creates a new API server
|
||||
func NewServer(addr string) *Server {
|
||||
func NewServer(addr string, devMode bool) *Server {
|
||||
s := &Server{
|
||||
router: chi.NewRouter(),
|
||||
addr: addr,
|
||||
router: chi.NewRouter(),
|
||||
addr: addr,
|
||||
devMode: devMode,
|
||||
}
|
||||
s.setupRoutes()
|
||||
return s
|
||||
@@ -39,16 +41,37 @@ func (s *Server) setupRoutes() {
|
||||
JSON(w, http.StatusOK, map[string]string{"status": "ok"})
|
||||
})
|
||||
|
||||
// OAuth endpoints (no auth required)
|
||||
r.Get("/auth/login", handlers.GetLoginURL)
|
||||
r.Get("/auth/callback", handlers.OAuthCallback)
|
||||
r.Post("/auth/callback", handlers.OAuthCallback)
|
||||
r.Post("/auth/refresh", handlers.RefreshToken)
|
||||
r.Post("/auth/logout", handlers.Logout)
|
||||
if s.devMode {
|
||||
// Dev mode: fake session endpoint so the frontend can "log in"
|
||||
r.Get("/auth/dev-session", func(w http.ResponseWriter, r *http.Request) {
|
||||
JSON(w, http.StatusOK, map[string]interface{}{
|
||||
"access_token": "dev-token",
|
||||
"refresh_token": "",
|
||||
"expires_at": 9999999999,
|
||||
"token_type": "bearer",
|
||||
"user": map[string]interface{}{
|
||||
"id": 1,
|
||||
"username": "dev",
|
||||
"email": "dev@localhost",
|
||||
},
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// OAuth endpoints (no auth required)
|
||||
r.Get("/auth/login", handlers.GetLoginURL)
|
||||
r.Get("/auth/callback", handlers.OAuthCallback)
|
||||
r.Post("/auth/callback", handlers.OAuthCallback)
|
||||
r.Post("/auth/refresh", handlers.RefreshToken)
|
||||
r.Post("/auth/logout", handlers.Logout)
|
||||
}
|
||||
|
||||
// Protected routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(AuthMiddleware())
|
||||
if s.devMode {
|
||||
r.Use(DevAuthMiddleware())
|
||||
} else {
|
||||
r.Use(AuthMiddleware())
|
||||
}
|
||||
|
||||
// Tasks
|
||||
r.Route("/tasks", func(r chi.Router) {
|
||||
|
||||
Reference in New Issue
Block a user