package middleware import ( "net/http" "source.toby3d.me/toby3d/home/internal/domain" "source.toby3d.me/toby3d/home/internal/server" "source.toby3d.me/toby3d/home/internal/urlutil" ) type ( RedirectConfig struct { Skipper Skipper Serverer server.UseCase } redirectResponse struct { http.ResponseWriter error error statusCode int } ) func Redirect(config RedirectConfig) Interceptor { if config.Skipper == nil { config.Skipper = DefaultSkipper } if config.Serverer == nil { panic("middleware: redirect: Serverer is nil") } return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if config.Skipper(r) { next(w, r) return } lang, path := domain.LanguageUnd, r.URL.Path if head, tail := urlutil.ShiftPath(r.URL.Path); head != "" { if lang = domain.NewLanguage(head); lang != domain.LanguageUnd { path = tail } } server, err := config.Serverer.Do(r.Context(), lang) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } redirect, ok := server.Redirects.Match(path) if !ok { next(w, r) return } // NOTE(toby3d): always redirect no matter what exists on // requested URL. if redirect.Force { http.Redirect(w, r, redirect.To, redirect.Status) return } tx := &redirectResponse{ error: nil, statusCode: http.StatusOK, ResponseWriter: w, } next(tx, r) // NOTE(toby3d): redirect only if something bad on requested // URL. if tx.error == nil && http.StatusOK < tx.statusCode && tx.statusCode < http.StatusBadRequest { return } http.Redirect(w, r, redirect.To, redirect.Status) } } func (r *redirectResponse) WriteHeader(status int) { r.statusCode = status r.ResponseWriter.WriteHeader(status) } func (r *redirectResponse) Write(src []byte) (int, error) { var length int length, r.error = r.ResponseWriter.Write(src) return length, r.error }