package router import ( "os" "strings" "time" ) // compileWithMiddleware combines middleware and handler source, then compiles func (r *LuaRouter) compileWithMiddleware(n *node, fsPath, scriptPath string) error { if scriptPath == "" { return nil } // Check if we need to recompile by comparing modification times sourceKey := r.getSourceCacheKey(fsPath, scriptPath) needsRecompile := false // Check handler modification time handlerInfo, err := os.Stat(scriptPath) if err != nil { n.err = err return err } lastCompiled, exists := r.sourceMtimes[sourceKey] if !exists || handlerInfo.ModTime().After(lastCompiled) { needsRecompile = true } // Check middleware modification times if !needsRecompile { middlewareChain := r.getMiddlewareChain(fsPath) for _, mwPath := range middlewareChain { mwInfo, err := os.Stat(mwPath) if err != nil { n.err = err return err } if mwInfo.ModTime().After(lastCompiled) { needsRecompile = true break } } } // Use cached bytecode if available and fresh if !needsRecompile { if bytecode, exists := r.sourceCache[sourceKey]; exists { bytecodeKey := getBytecodeKey(scriptPath) r.bytecodeCache.Set(bytecodeKey, bytecode) return nil } } // Build combined source combinedSource, err := r.buildCombinedSource(fsPath, scriptPath) if err != nil { n.err = err return err } // Compile combined source using shared state r.compileStateMu.Lock() bytecode, err := r.compileState.CompileBytecode(combinedSource, scriptPath) r.compileStateMu.Unlock() if err != nil { n.err = err return err } // Cache everything bytecodeKey := getBytecodeKey(scriptPath) r.bytecodeCache.Set(bytecodeKey, bytecode) r.sourceCache[sourceKey] = bytecode r.sourceMtimes[sourceKey] = time.Now() n.err = nil return nil } // buildCombinedSource builds the combined middleware + handler source func (r *LuaRouter) buildCombinedSource(fsPath, scriptPath string) (string, error) { var combinedSource strings.Builder // Get middleware chain using filesystem path middlewareChain := r.getMiddlewareChain(fsPath) // Add middleware in order for _, mwPath := range middlewareChain { content, err := r.getFileContent(mwPath) if err != nil { return "", err } combinedSource.WriteString("-- Middleware: ") combinedSource.WriteString(mwPath) combinedSource.WriteString("\n") combinedSource.Write(content) combinedSource.WriteString("\n") } // Add main handler content, err := r.getFileContent(scriptPath) if err != nil { return "", err } combinedSource.WriteString("-- Handler: ") combinedSource.WriteString(scriptPath) combinedSource.WriteString("\n") combinedSource.Write(content) return combinedSource.String(), nil } // getFileContent reads file content with caching func (r *LuaRouter) getFileContent(path string) ([]byte, error) { // Check cache first if content, exists := r.middlewareCache[path]; exists { // Verify file hasn't changed info, err := os.Stat(path) if err == nil { if cachedTime, exists := r.sourceMtimes[path]; exists && !info.ModTime().After(cachedTime) { return content, nil } } } // Read from disk content, err := os.ReadFile(path) if err != nil { return nil, err } // Cache it r.middlewareCache[path] = content r.sourceMtimes[path] = time.Now() return content, nil } // getSourceCacheKey generates a unique key for combined source func (r *LuaRouter) getSourceCacheKey(fsPath, scriptPath string) string { middlewareChain := r.getMiddlewareChain(fsPath) var keyParts []string keyParts = append(keyParts, middlewareChain...) keyParts = append(keyParts, scriptPath) return strings.Join(keyParts, "|") } // getMiddlewareChain returns middleware files that apply to the given filesystem path func (r *LuaRouter) getMiddlewareChain(fsPath string) []string { var chain []string // Collect middleware from root to specific path using filesystem path (includes groups) pathParts := strings.Split(strings.Trim(fsPath, "/"), "/") if pathParts[0] == "" { pathParts = []string{} } // Add root middleware if mw, exists := r.middlewareFiles["/"]; exists { chain = append(chain, mw...) } // Add middleware from each path level (including groups) currentPath := "" for _, part := range pathParts { currentPath += "/" + part if mw, exists := r.middlewareFiles[currentPath]; exists { chain = append(chain, mw...) } } return chain }