99 lines
1.9 KiB
Go
99 lines
1.9 KiB
Go
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
|
|
}
|