Files
shorebird-server/cmd/server/main.go
T
Tony 5f72264b06 feat: add organization management features and password reset functionality
- Introduced a new section for managing organizations, including creating organizations and adding users to them.
- Added a password reset modal and functionality to request a password reset link.
- Updated the settings page to include personal settings for changing passwords and admin settings for configuring registration options.
- Enhanced the app detail view with tabs for releases, insights, collaborators, tracks, and settings.
- Improved user management with admin capabilities to verify user emails and change user roles.
- Updated navigation and UI elements to accommodate new features and improve user experience.
2026-06-12 20:03:58 +08:00

117 lines
3.3 KiB
Go

package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/shorebird-server/internal/api/handlers"
"github.com/shorebird-server/internal/auth"
"github.com/shorebird-server/internal/config"
"github.com/shorebird-server/internal/db"
"github.com/shorebird-server/internal/email"
"github.com/shorebird-server/internal/storage"
)
func main() {
cfg := config.Load()
ctx := context.Background()
// --- Database ---
var dsn string
switch cfg.DB.Driver {
case "postgres", "postgresql", "pg":
dsn = cfg.DB.URL
default:
dsn = cfg.DB.Path
}
database, err := db.NewStore(ctx, cfg.DB.Driver, dsn)
if err != nil {
log.Fatalf("Failed to open database: %v", err)
}
defer database.Close()
log.Printf("Database: %s (%s)", cfg.DB.Driver, cfg.DB.Path)
adminHash, err := auth.HashPassword(cfg.Admin.Password)
if err != nil {
log.Fatalf("Failed to hash default admin password: %v", err)
}
createdAdmin, err := database.EnsureDefaultAdmin(ctx, cfg.Admin.Email, cfg.Admin.Name, adminHash)
if err != nil {
log.Fatalf("Failed to ensure default admin account: %v", err)
}
if createdAdmin {
log.Printf("Created default admin account %s with default password; password change required at first login", cfg.Admin.Email)
}
// --- Auth ---
authService := auth.NewService(cfg.Auth.JWTSecret, cfg.Auth.TokenDuration)
mailer := email.NewMailer(email.Config{
Host: cfg.Email.Host,
Port: cfg.Email.Port,
Username: cfg.Email.Username,
Password: cfg.Email.Password,
From: cfg.Email.From,
})
// --- Storage ---
store, err := storage.NewStore(storage.Config{
Type: cfg.Storage.Driver,
LocalDir: cfg.Storage.LocalDir,
ServerBaseURL: cfg.Server.BaseURL,
S3Endpoint: cfg.Storage.S3Endpoint,
S3AccessKey: cfg.Storage.S3AccessKey,
S3SecretKey: cfg.Storage.S3SecretKey,
S3UseSSL: cfg.Storage.S3UseSSL,
S3ReleaseBucket: cfg.Storage.S3ReleaseBucket,
S3PatchBucket: cfg.Storage.S3PatchBucket,
})
if err != nil {
log.Fatalf("Failed to initialize storage: %v", err)
}
log.Printf("Storage: %s", store.BackendName())
// --- Router ---
router := handlers.NewRouter(authService, database, store, mailer, cfg.Server.BaseURL)
// --- HTTP Server ---
addr := fmt.Sprintf("%s:%s", cfg.Server.Host, cfg.Server.Port)
srv := &http.Server{
Addr: addr,
Handler: router,
ReadTimeout: cfg.Server.ReadTimeout,
WriteTimeout: cfg.Server.WriteTimeout,
IdleTimeout: 60 * time.Second,
}
go func() {
log.Printf("Shorebird server starting on %s", addr)
log.Printf(" Dashboard: http://%s", addr)
log.Printf(" API: http://%s/api/v1", addr)
log.Printf(" Auth: http://%s/auth", addr)
log.Printf(" Storage: %s", store.BackendName())
log.Printf(" Database: %s", cfg.DB.Driver)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed: %v", err)
}
}()
// Graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down...")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
log.Fatalf("Shutdown error: %v", err)
}
log.Println("Server stopped")
}