Merge pull request #632 from ansible-semaphore/fix-api-trailing-slash

fix(api): remove redirect for calls without trailing slash
This commit is contained in:
Denis Gukov 2020-11-27 03:53:45 +05:00 committed by GitHub
commit eee34fb266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 28 deletions

View File

@ -51,7 +51,7 @@ func notFoundHandler(w http.ResponseWriter, r *http.Request) {
// Route declares all routes // Route declares all routes
func Route() *mux.Router { func Route() *mux.Router {
r := mux.NewRouter().StrictSlash(true) r := mux.NewRouter()
r.NotFoundHandler = http.HandlerFunc(servePublic) r.NotFoundHandler = http.HandlerFunc(servePublic)
webPath := "/" webPath := "/"
@ -90,9 +90,9 @@ func Route() *mux.Router {
authenticatedAPI.Path("/users").HandlerFunc(getUsers).Methods("GET", "HEAD") authenticatedAPI.Path("/users").HandlerFunc(getUsers).Methods("GET", "HEAD")
authenticatedAPI.Path("/users").HandlerFunc(addUser).Methods("POST") authenticatedAPI.Path("/users").HandlerFunc(addUser).Methods("POST")
tokenAPI := authenticatedAPI.PathPrefix("/user").Subrouter() authenticatedAPI.Path("/user").HandlerFunc(getUser).Methods("GET", "HEAD")
tokenAPI.Path("/").HandlerFunc(getUser).Methods("GET", "HEAD") tokenAPI := authenticatedAPI.PathPrefix("/user").Subrouter()
tokenAPI.Path("/tokens").HandlerFunc(getAPITokens).Methods("GET", "HEAD") tokenAPI.Path("/tokens").HandlerFunc(getAPITokens).Methods("GET", "HEAD")
tokenAPI.Path("/tokens").HandlerFunc(createAPIToken).Methods("POST") tokenAPI.Path("/tokens").HandlerFunc(createAPIToken).Methods("POST")
tokenAPI.HandleFunc("/tokens/{token_id}", expireAPIToken).Methods("DELETE") tokenAPI.HandleFunc("/tokens/{token_id}", expireAPIToken).Methods("DELETE")
@ -100,15 +100,21 @@ func Route() *mux.Router {
userAPI := authenticatedAPI.PathPrefix("/users/{user_id}").Subrouter() userAPI := authenticatedAPI.PathPrefix("/users/{user_id}").Subrouter()
userAPI.Use(getUserMiddleware) userAPI.Use(getUserMiddleware)
userAPI.Path("/").HandlerFunc(getUser).Methods("GET", "HEAD") userAPI.Methods("GET", "HEAD").HandlerFunc(getUser)
userAPI.Path("/").HandlerFunc(updateUser).Methods("PUT") userAPI.Methods("PUT").HandlerFunc(updateUser)
userAPI.Path("/").HandlerFunc(deleteUser).Methods("DELETE") userAPI.Methods("DELETE").HandlerFunc(deleteUser)
userAPI.Path("/password").HandlerFunc(updateUserPassword).Methods("POST")
userPasswordAPI := authenticatedAPI.PathPrefix("/users/{user_id}").Subrouter()
userPasswordAPI.Use(getUserMiddleware)
userPasswordAPI.Path("/password").HandlerFunc(updateUserPassword).Methods("POST")
projectGet := authenticatedAPI.Path("/project/{project_id}").Subrouter()
projectGet.Use(projects.ProjectMiddleware)
projectGet.Methods("GET", "HEAD").HandlerFunc(projects.GetProject)
projectUserAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter() projectUserAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
projectUserAPI.Use(projects.ProjectMiddleware) projectUserAPI.Use(projects.ProjectMiddleware)
projectUserAPI.Path("/").HandlerFunc(projects.GetProject).Methods("GET", "HEAD")
projectUserAPI.Path("/events").HandlerFunc(getAllEvents).Methods("GET", "HEAD") projectUserAPI.Path("/events").HandlerFunc(getAllEvents).Methods("GET", "HEAD")
projectUserAPI.HandleFunc("/events/last", getLastEvents).Methods("GET", "HEAD") projectUserAPI.HandleFunc("/events/last", getLastEvents).Methods("GET", "HEAD")
@ -136,9 +142,12 @@ func Route() *mux.Router {
projectAdminAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter() projectAdminAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
projectAdminAPI.Use(projects.ProjectMiddleware, projects.MustBeAdmin) projectAdminAPI.Use(projects.ProjectMiddleware, projects.MustBeAdmin)
projectAdminAPI.Path("/").HandlerFunc(projects.UpdateProject).Methods("PUT") projectAdminAPI.Methods("PUT").HandlerFunc(projects.UpdateProject)
projectAdminAPI.Path("/").HandlerFunc(projects.DeleteProject).Methods("DELETE") projectAdminAPI.Methods("DELETE").HandlerFunc(projects.DeleteProject)
projectAdminAPI.Path("/users").HandlerFunc(projects.AddUser).Methods("POST")
projectAdminUsersAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
projectAdminUsersAPI.Use(projects.ProjectMiddleware, projects.MustBeAdmin)
projectAdminUsersAPI.Path("/users").HandlerFunc(projects.AddUser).Methods("POST")
projectUserManagement := projectAdminAPI.PathPrefix("/users").Subrouter() projectUserManagement := projectAdminAPI.PathPrefix("/users").Subrouter()
projectUserManagement.Use(projects.UserMiddleware) projectUserManagement.Use(projects.UserMiddleware)
@ -232,31 +241,33 @@ func debugPrintRoutes(r *mux.Router) {
//nolint: gocyclo //nolint: gocyclo
func servePublic(w http.ResponseWriter, r *http.Request) { func servePublic(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
htmlPrefix := ""
if util.Config.OldFrontend {
htmlPrefix = "/html"
}
publicAssetsPrefix := ""
if util.Config.OldFrontend {
publicAssetsPrefix = "public"
}
webPath := "/" webPath := "/"
if util.WebHostURL != nil { if util.WebHostURL != nil {
webPath = util.WebHostURL.RequestURI() webPath = util.WebHostURL.RequestURI()
} }
path := r.URL.Path
if path == webPath + "api" || strings.HasPrefix(path, webPath + "api/") {
w.WriteHeader(http.StatusNotFound)
return
}
htmlPrefix := ""
publicAssetsPrefix := ""
if util.Config.OldFrontend {
htmlPrefix = "/html"
publicAssetsPrefix = "public"
}
if publicAssetsPrefix != "" && !strings.HasPrefix(path, webPath+publicAssetsPrefix) { if publicAssetsPrefix != "" && !strings.HasPrefix(path, webPath+publicAssetsPrefix) {
if len(strings.Split(path, ".")) > 1 { if strings.Contains(path, ".") {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
return return
} }
path = htmlPrefix+"/index.html" path = htmlPrefix+"/index.html"
} else if len(strings.Split(path, ".")) == 1 { } else if !strings.Contains(path, ".") {
path = htmlPrefix+"/index.html" path = htmlPrefix+"/index.html"
} }

View File

@ -21,6 +21,15 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
) )
func cropTrailingSlashMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
r.URL.Path = strings.TrimSuffix(r.URL.Path, "/")
}
next.ServeHTTP(w, r)
})
}
func main() { func main() {
util.ConfigInit() util.ConfigInit()
if util.InteractiveSetup { if util.InteractiveSetup {
@ -69,7 +78,7 @@ func main() {
fmt.Println("Server is running") fmt.Println("Server is running")
err := http.ListenAndServe(util.Config.Interface+util.Config.Port, nil) err := http.ListenAndServe(util.Config.Interface+util.Config.Port, cropTrailingSlashMiddleware(router))
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }

View File

@ -734,7 +734,7 @@ export default {
} }
this.user = (await axios({ this.user = (await axios({
method: 'get', method: 'get',
url: '/api/user/', url: '/api/user',
responseType: 'json', responseType: 'json',
})).data; })).data;
}, },

View File

@ -13,7 +13,7 @@
<v-text-field <v-text-field
v-model="item.name" v-model="item.name"
label="Playbook Alias" label="Project Name"
:rules="[v => !!v || 'Project name is required']" :rules="[v => !!v || 'Project name is required']"
required required
:disabled="formSaving" :disabled="formSaving"

View File

@ -15,6 +15,7 @@ export default class Socket extends Listenable {
if (!this.isRunning()) { if (!this.isRunning()) {
return; return;
} }
this.ws = null;
setTimeout(() => { setTimeout(() => {
this.start(); this.start();
}, 2000); }, 2000);