Skip to content

Commit

Permalink
Merge pull request #632 from ansible-semaphore/fix-api-trailing-slash
Browse files Browse the repository at this point in the history
fix(api): remove redirect for calls without trailing slash
  • Loading branch information
fiftin committed Nov 26, 2020
2 parents 1b49bfb + 80616ee commit eee34fb
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 24 deletions.
53 changes: 32 additions & 21 deletions api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func notFoundHandler(w http.ResponseWriter, r *http.Request) {

// Route declares all routes
func Route() *mux.Router {
r := mux.NewRouter().StrictSlash(true)
r := mux.NewRouter()
r.NotFoundHandler = http.HandlerFunc(servePublic)

webPath := "/"
Expand Down Expand Up @@ -90,25 +90,31 @@ func Route() *mux.Router {
authenticatedAPI.Path("/users").HandlerFunc(getUsers).Methods("GET", "HEAD")
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(createAPIToken).Methods("POST")
tokenAPI.HandleFunc("/tokens/{token_id}", expireAPIToken).Methods("DELETE")

userAPI := authenticatedAPI.PathPrefix("/users/{user_id}").Subrouter()
userAPI.Use(getUserMiddleware)

userAPI.Path("/").HandlerFunc(getUser).Methods("GET", "HEAD")
userAPI.Path("/").HandlerFunc(updateUser).Methods("PUT")
userAPI.Path("/").HandlerFunc(deleteUser).Methods("DELETE")
userAPI.Path("/password").HandlerFunc(updateUserPassword).Methods("POST")
userAPI.Methods("GET", "HEAD").HandlerFunc(getUser)
userAPI.Methods("PUT").HandlerFunc(updateUser)
userAPI.Methods("DELETE").HandlerFunc(deleteUser)

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.Use(projects.ProjectMiddleware)

projectUserAPI.Path("/").HandlerFunc(projects.GetProject).Methods("GET", "HEAD")
projectUserAPI.Path("/events").HandlerFunc(getAllEvents).Methods("GET", "HEAD")
projectUserAPI.HandleFunc("/events/last", getLastEvents).Methods("GET", "HEAD")

Expand Down Expand Up @@ -136,9 +142,12 @@ func Route() *mux.Router {
projectAdminAPI := authenticatedAPI.PathPrefix("/project/{project_id}").Subrouter()
projectAdminAPI.Use(projects.ProjectMiddleware, projects.MustBeAdmin)

projectAdminAPI.Path("/").HandlerFunc(projects.UpdateProject).Methods("PUT")
projectAdminAPI.Path("/").HandlerFunc(projects.DeleteProject).Methods("DELETE")
projectAdminAPI.Path("/users").HandlerFunc(projects.AddUser).Methods("POST")
projectAdminAPI.Methods("PUT").HandlerFunc(projects.UpdateProject)
projectAdminAPI.Methods("DELETE").HandlerFunc(projects.DeleteProject)

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.Use(projects.UserMiddleware)
Expand Down Expand Up @@ -232,31 +241,33 @@ func debugPrintRoutes(r *mux.Router) {

//nolint: gocyclo
func servePublic(w http.ResponseWriter, r *http.Request) {
webPath := "/"
if util.WebHostURL != nil {
webPath = util.WebHostURL.RequestURI()
}

path := r.URL.Path

htmlPrefix := ""
if util.Config.OldFrontend {
htmlPrefix = "/html"
if path == webPath + "api" || strings.HasPrefix(path, webPath + "api/") {
w.WriteHeader(http.StatusNotFound)
return
}

htmlPrefix := ""
publicAssetsPrefix := ""
if util.Config.OldFrontend {
htmlPrefix = "/html"
publicAssetsPrefix = "public"
}

webPath := "/"
if util.WebHostURL != nil {
webPath = util.WebHostURL.RequestURI()
}

if publicAssetsPrefix != "" && !strings.HasPrefix(path, webPath+publicAssetsPrefix) {
if len(strings.Split(path, ".")) > 1 {
if strings.Contains(path, ".") {
w.WriteHeader(http.StatusNotFound)
return
}

path = htmlPrefix+"/index.html"
} else if len(strings.Split(path, ".")) == 1 {
} else if !strings.Contains(path, ".") {
path = htmlPrefix+"/index.html"
}

Expand Down
11 changes: 10 additions & 1 deletion cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ import (
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() {
util.ConfigInit()
if util.InteractiveSetup {
Expand Down Expand Up @@ -69,7 +78,7 @@ func main() {

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 {
log.Panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion web2/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ export default {
}
this.user = (await axios({
method: 'get',
url: '/api/user/',
url: '/api/user',
responseType: 'json',
})).data;
},
Expand Down
2 changes: 1 addition & 1 deletion web2/src/components/ProjectForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<v-text-field
v-model="item.name"
label="Playbook Alias"
label="Project Name"
:rules="[v => !!v || 'Project name is required']"
required
:disabled="formSaving"
Expand Down
1 change: 1 addition & 0 deletions web2/src/lib/Socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default class Socket extends Listenable {
if (!this.isRunning()) {
return;
}
this.ws = null;
setTimeout(() => {
this.start();
}, 2000);
Expand Down

0 comments on commit eee34fb

Please sign in to comment.