github.com/oinume/lekcije@v0.0.0-20231017100347-5b4c5eb6ab24/backend/interface/http/util.go (about) 1 package http 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "html/template" 8 "net/http" 9 "os" 10 "path" 11 "path/filepath" 12 "strings" 13 14 "github.com/morikuni/failure" 15 16 "github.com/oinume/lekcije/backend/context_data" 17 "github.com/oinume/lekcije/backend/domain/config" 18 "github.com/oinume/lekcije/backend/errors" 19 "github.com/oinume/lekcije/backend/usecase" 20 "github.com/oinume/lekcije/backend/util" 21 ) 22 23 const ( 24 APITokenCookieName = "apiToken" 25 TrackingIDCookieName = "trackingId" 26 ) 27 28 func TemplateDir() string { 29 koDataPath := os.Getenv("KO_DATA_PATH") 30 if koDataPath != "" { 31 return filepath.Join(koDataPath, "html") 32 } 33 wd, err := os.Getwd() 34 if err != nil { 35 panic(err) 36 } 37 _, file := filepath.Split(wd) 38 switch file { 39 case "backend": 40 return filepath.Join(wd, "..", "frontend", "html") 41 default: 42 return filepath.Join(wd, "frontend", "html") 43 } 44 } 45 46 func TemplatePath(file string) string { 47 return path.Join(TemplateDir(), file) 48 } 49 50 func ParseHTMLTemplates(files ...string) *template.Template { 51 f := []string{ 52 TemplatePath("_base.html"), 53 } 54 f = append(f, files...) 55 return template.Must(template.ParseFiles(f...)) 56 } 57 58 func internalServerError(ctx context.Context, errorRecorder *usecase.ErrorRecorder, w http.ResponseWriter, err error, userID uint32) { 59 //switch _ := errors.Cause(err).(type) { // TODO: 60 //default: 61 // unknown error 62 sUserID := "" 63 if userID == 0 { 64 sUserID = fmt.Sprint(userID) 65 } 66 errorRecorder.Record(ctx, err, sUserID) 67 http.Error(w, fmt.Sprintf("Internal Server Error\n\n%v", err), http.StatusInternalServerError) 68 if !config.IsProductionEnv() { 69 fmt.Fprintf(w, "----- stacktrace -----\n") 70 if e, ok := err.(errors.StackTracer); ok { 71 for _, f := range e.StackTrace() { 72 _, _ = fmt.Fprintf(w, "%+v\n", f) 73 } 74 } 75 if callStack, ok := failure.CallStackOf(err); ok { 76 for _, f := range callStack.Frames() { 77 _, _ = fmt.Fprintf(w, "[%s] %v:%v\n", f.Func(), f.File(), f.Line()) 78 } 79 } 80 } 81 } 82 83 func writeJSON(w http.ResponseWriter, code int, body interface{}) { 84 w.Header().Set("Content-Type", "application/json; charset=utf-8") 85 //w.Header().Set("X-Content-Type-Options", "nosniff") 86 w.WriteHeader(code) 87 if err := json.NewEncoder(w).Encode(body); err != nil { 88 http.Error(w, `{ "status": "Failed to Encode as writeJSON" }`, http.StatusInternalServerError) 89 return 90 } 91 } 92 93 type commonTemplateData struct { 94 StaticURL string 95 GoogleAnalyticsID string 96 CurrentURL string 97 CanonicalURL string 98 TrackingID string 99 IsUserAgentPC bool 100 IsUserAgentSP bool 101 IsUserAgentTablet bool 102 UserID string 103 NavigationItems []navigationItem 104 ServiceEnv string 105 } 106 107 type navigationItem struct { 108 Text string 109 URL string 110 NewWindow bool 111 } 112 113 var loggedInNavigationItems = []navigationItem{ 114 {"ホーム", "/me", false}, 115 {"設定", "/me/setting", false}, 116 {"お問い合わせ", "https://goo.gl/forms/CIGO3kpiQCGjtFD42", true}, 117 {"ログアウト", "/me/logout", false}, 118 } 119 120 var loggedOutNavigationItems = []navigationItem{ 121 {"ホーム", "/", false}, 122 } 123 124 func (s *server) getCommonTemplateData(req *http.Request, loggedIn bool, userID uint32) commonTemplateData { 125 canonicalURL := fmt.Sprintf("%s://%s%s", config.WebURLScheme(req), req.Host, req.RequestURI) 126 canonicalURL = (strings.SplitN(canonicalURL, "?", 2))[0] // TODO: use url.Parse 127 data := commonTemplateData{ 128 StaticURL: config.StaticURL(), 129 GoogleAnalyticsID: config.DefaultVars.GoogleAnalyticsID, 130 CurrentURL: req.RequestURI, 131 CanonicalURL: canonicalURL, 132 IsUserAgentPC: util.IsUserAgentPC(req), 133 IsUserAgentSP: util.IsUserAgentSP(req), 134 IsUserAgentTablet: util.IsUserAgentTablet(req), 135 ServiceEnv: config.DefaultVars.ServiceEnv, 136 } 137 138 if loggedIn { 139 data.NavigationItems = loggedInNavigationItems 140 } else { 141 data.NavigationItems = loggedOutNavigationItems 142 } 143 data.TrackingID = context_data.MustTrackingID(req.Context()) 144 if userID != 0 { 145 data.UserID = fmt.Sprint(userID) 146 } 147 148 return data 149 } 150 151 func getRemoteAddress(req *http.Request) string { 152 xForwardedFor := req.Header.Get("X-Forwarded-For") 153 if xForwardedFor == "" { 154 return (strings.Split(req.RemoteAddr, ":"))[0] 155 } 156 return strings.TrimSpace((strings.Split(xForwardedFor, ","))[0]) 157 }