Refactor web build process and update documentation

- Removed Node.js build artifacts from .gitignore and adjusted Makefile to reflect changes in web UI build process, now using server-rendered Go templates instead of React.
- Updated README to clarify the new web UI architecture and output formats, emphasizing the removal of the Node.js build step.
- Added a command to set the number of frames per render task in manager configuration, enhancing user control over rendering settings.
- Improved Gitea workflow by removing unnecessary npm install step, streamlining the CI process.
This commit is contained in:
2026-03-12 19:44:40 -05:00
parent d3c5ee0dba
commit 2deb47e5ad
78 changed files with 3895 additions and 12499 deletions

View File

@@ -59,6 +59,7 @@ type Manager struct {
secrets *authpkg.Secrets
storage *storage.Storage
router *chi.Mux
ui *uiRenderer
// WebSocket connections
wsUpgrader websocket.Upgrader
@@ -125,10 +126,24 @@ type ClientConnection struct {
type UploadSession struct {
SessionID string
UserID int64
TempDir string
Progress float64
Status string // "uploading", "processing", "extracting_metadata", "creating_context", "completed", "error"
Phase string // "upload", "processing", "ready", "error", "action_required"
Message string
CreatedAt time.Time
// Result fields set when Status is "completed" (for async processing)
ResultContextArchive string
ResultMetadata interface{} // *types.BlendMetadata when set
ResultMainBlendFile string
ResultFileName string
ResultFileSize int64
ResultZipExtracted bool
ResultExtractedFilesCnt int
ResultMetadataExtracted bool
ResultMetadataError string // set when Status is "completed" but metadata extraction failed
ErrorMessage string // set when Status is "error"
ResultBlendFiles []string // set when Status is "select_blend" (relative paths for user to pick)
}
// NewManager creates a new manager server
@@ -137,6 +152,10 @@ func NewManager(db *database.DB, cfg *config.Config, auth *authpkg.Auth, storage
if err != nil {
return nil, fmt.Errorf("failed to initialize secrets: %w", err)
}
ui, err := newUIRenderer()
if err != nil {
return nil, fmt.Errorf("failed to initialize UI renderer: %w", err)
}
s := &Manager{
db: db,
@@ -145,6 +164,7 @@ func NewManager(db *database.DB, cfg *config.Config, auth *authpkg.Auth, storage
secrets: secrets,
storage: storage,
router: chi.NewRouter(),
ui: ui,
startTime: time.Now(),
wsUpgrader: websocket.Upgrader{
CheckOrigin: checkWebSocketOrigin,
@@ -450,6 +470,7 @@ func (w *gzipResponseWriter) WriteHeader(statusCode int) {
func (s *Manager) setupRoutes() {
// Health check endpoint (unauthenticated)
s.router.Get("/api/health", s.handleHealthCheck)
s.setupUIRoutes()
// Public routes (with stricter rate limiting for auth endpoints)
s.router.Route("/api/auth", func(r chi.Router) {
@@ -477,6 +498,7 @@ func (s *Manager) setupRoutes() {
})
r.Post("/", s.handleCreateJob)
r.Post("/upload", s.handleUploadFileForJobCreation) // Upload before job creation
r.Get("/upload/status", s.handleUploadStatus) // Poll upload processing status (session_id query param)
r.Get("/", s.handleListJobs)
r.Get("/summary", s.handleListJobsSummary)
r.Post("/batch", s.handleBatchGetJobs)
@@ -487,6 +509,7 @@ func (s *Manager) setupRoutes() {
r.Get("/{id}/files", s.handleListJobFiles)
r.Get("/{id}/files/count", s.handleGetJobFilesCount)
r.Get("/{id}/context", s.handleListContextArchive)
r.Get("/{id}/files/exr-zip", s.handleDownloadEXRZip)
r.Get("/{id}/files/{fileId}/download", s.handleDownloadJobFile)
r.Get("/{id}/files/{fileId}/preview-exr", s.handlePreviewEXR)
r.Get("/{id}/video", s.handleStreamVideo)
@@ -522,7 +545,6 @@ func (s *Manager) setupRoutes() {
r.Delete("/{id}", s.handleDeleteRunnerAPIKey)
})
r.Get("/", s.handleListRunnersAdmin)
r.Post("/{id}/verify", s.handleVerifyRunner)
r.Delete("/{id}", s.handleDeleteRunner)
})
r.Route("/users", func(r chi.Router) {
@@ -555,6 +577,7 @@ func (s *Manager) setupRoutes() {
return http.HandlerFunc(s.runnerAuthMiddleware(next.ServeHTTP))
})
r.Get("/blender/download", s.handleDownloadBlender)
r.Get("/jobs/{jobId}/status", s.handleGetJobStatusForRunner)
r.Get("/jobs/{jobId}/files", s.handleGetJobFilesForRunner)
r.Get("/jobs/{jobId}/metadata", s.handleGetJobMetadataForRunner)
r.Get("/files/{jobId}/{fileName}", s.handleDownloadFileForRunner)
@@ -564,8 +587,8 @@ func (s *Manager) setupRoutes() {
// Blender versions API (public, for job submission page)
s.router.Get("/api/blender/versions", s.handleGetBlenderVersions)
// Serve static files (embedded React app with SPA fallback)
s.router.Handle("/*", web.SPAHandler())
// Static assets for server-rendered UI.
s.router.Handle("/assets/*", web.StaticHandler())
}
// ServeHTTP implements http.Handler