Merge pull request #20 from hunterlong/dev

user update - travis test on dev
pull/26/head v0.29.8
Hunter Long 2018-07-08 23:18:31 -07:00 committed by GitHub
commit 7079a41c6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 108 additions and 49 deletions

View File

@ -18,7 +18,7 @@ services:
env: env:
global: global:
- VERSION=0.29.7 - VERSION=0.29.8
- DB_HOST=localhost - DB_HOST=localhost
- DB_USER=travis - DB_USER=travis
- DB_PASS= - DB_PASS=
@ -65,8 +65,9 @@ before_script:
- go get - go get
script: script:
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then /bin/bash -c .travis/compile.sh; fi - /bin/bash -c .travis/compile.sh
- go test -v -covermode=count -coverprofile=coverage.out && $GOPATH/bin/goveralls -coverprofile=coverage.out -service=travis -repotoken $COVERALLS - go test -v -covermode=count -coverprofile=coverage.out
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then $GOPATH/bin/goveralls -coverprofile=coverage.out -service=travis -repotoken $COVERALLS; fi
after_success: after_success:
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then travis_wait 30 docker pull karalabe/xgo-latest; fi - if [[ "$TRAVIS_BRANCH" == "master" ]]; then travis_wait 30 docker pull karalabe/xgo-latest; fi

View File

@ -1,6 +1,6 @@
FROM alpine:latest FROM alpine:latest
ENV VERSION=v0.29.7 ENV VERSION=v0.29.8
RUN apk --no-cache add libstdc++ ca-certificates RUN apk --no-cache add libstdc++ ca-certificates
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \ RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \

View File

@ -32,6 +32,13 @@ func (u *User) Delete() error {
return user.Delete() return user.Delete()
} }
func (u *User) Update() error {
u.CreatedAt = time.Now()
col := DbSession.Collection("users")
user := col.Find("id", u.Id)
return user.Update(u)
}
func (u *User) Create() (int64, error) { func (u *User) Create() (int64, error) {
u.CreatedAt = time.Now() u.CreatedAt = time.Now()
u.Password = utils.HashPassword(u.Password) u.Password = utils.HashPassword(u.Password)

View File

@ -29,9 +29,10 @@ func Router() *mux.Router {
r.Handle("/service/{id}/delete_failures", http.HandlerFunc(ServicesDeleteFailuresHandler)).Methods("GET") r.Handle("/service/{id}/delete_failures", http.HandlerFunc(ServicesDeleteFailuresHandler)).Methods("GET")
r.Handle("/service/{id}/checkin", http.HandlerFunc(CheckinCreateUpdateHandler)).Methods("POST") r.Handle("/service/{id}/checkin", http.HandlerFunc(CheckinCreateUpdateHandler)).Methods("POST")
r.Handle("/users", http.HandlerFunc(UsersHandler)).Methods("GET") r.Handle("/users", http.HandlerFunc(UsersHandler)).Methods("GET")
r.Handle("/user/{id}", http.HandlerFunc(UsersHandler)).Methods("GET")
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST") r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
r.Handle("/users/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET") r.Handle("/user/{id}", http.HandlerFunc(UsersEditHandler)).Methods("GET")
r.Handle("/user/{id}", http.HandlerFunc(UpdateUserHandler)).Methods("POST")
r.Handle("/user/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET")
r.Handle("/settings", http.HandlerFunc(PluginsHandler)).Methods("GET") r.Handle("/settings", http.HandlerFunc(PluginsHandler)).Methods("GET")
r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST") r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST")
r.Handle("/settings/css", http.HandlerFunc(SaveSASSHandler)).Methods("POST") r.Handle("/settings/css", http.HandlerFunc(SaveSASSHandler)).Methods("POST")

View File

@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/hunterlong/statup/core" "github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
@ -18,13 +19,16 @@ func SessionUser(r *http.Request) *types.User {
var user *types.User var user *types.User
col := core.DbSession.Collection("users") col := core.DbSession.Collection("users")
res := col.Find("id", uuid) res := col.Find("id", uuid)
res.One(&user) err := res.One(&user)
if err != nil {
utils.Log(3, fmt.Sprintf("cannot fetch user %v", uuid))
return nil
}
return user return user
} }
func UsersHandler(w http.ResponseWriter, r *http.Request) { func UsersHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r) if !IsAuthenticated(r) {
if !auth {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
return return
} }
@ -32,9 +36,41 @@ func UsersHandler(w http.ResponseWriter, r *http.Request) {
ExecuteResponse(w, r, "users.html", users) ExecuteResponse(w, r, "users.html", users)
} }
func UsersEditHandler(w http.ResponseWriter, r *http.Request) {
if !IsAuthenticated(r) {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
user, _ := core.SelectUser(int64(id))
ExecuteResponse(w, r, "user.html", user)
}
func UpdateUserHandler(w http.ResponseWriter, r *http.Request) {
if !IsAuthenticated(r) {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
r.ParseForm()
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
user, _ := core.SelectUser(int64(id))
user.Username = r.PostForm.Get("username")
user.Email = r.PostForm.Get("email")
user.Admin = (r.PostForm.Get("admin") == "on")
password := r.PostForm.Get("password")
if password != "##########" {
user.Password = utils.HashPassword(password)
}
user.Update()
users, _ := core.SelectAllUsers()
ExecuteResponse(w, r, "users.html", users)
}
func CreateUserHandler(w http.ResponseWriter, r *http.Request) { func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r) if !IsAuthenticated(r) {
if !auth {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
return return
} }
@ -59,8 +95,7 @@ func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
} }
func UsersDeleteHandler(w http.ResponseWriter, r *http.Request) { func UsersDeleteHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r) if !IsAuthenticated(r) {
if !auth {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
return return
} }

View File

@ -64,6 +64,9 @@ func TestRunAll(t *testing.T) {
t.Run(dbt+" Create Users", func(t *testing.T) { t.Run(dbt+" Create Users", func(t *testing.T) {
RunUser_Create(t) RunUser_Create(t)
}) })
t.Run(dbt+" Update User", func(t *testing.T) {
RunUser_Update(t)
})
t.Run(dbt+" Create Non Unique Users", func(t *testing.T) { t.Run(dbt+" Create Non Unique Users", func(t *testing.T) {
t.SkipNow() t.SkipNow()
RunUser_NonUniqueCreate(t) RunUser_NonUniqueCreate(t)
@ -134,6 +137,9 @@ func TestRunAll(t *testing.T) {
t.Run(dbt+" HTTP /users", func(t *testing.T) { t.Run(dbt+" HTTP /users", func(t *testing.T) {
RunUsersHandler(t) RunUsersHandler(t)
}) })
t.Run(dbt+" HTTP /user/1", func(t *testing.T) {
RunUserViewHandler(t)
})
t.Run(dbt+" HTTP /services", func(t *testing.T) { t.Run(dbt+" HTTP /services", func(t *testing.T) {
RunServicesHandler(t) RunServicesHandler(t)
}) })
@ -264,6 +270,7 @@ func RunUser_Create(t *testing.T) {
Username: "admin", Username: "admin",
Password: "admin", Password: "admin",
Email: "info@testuser.com", Email: "info@testuser.com",
Admin: true,
} }
id, err := user.Create() id, err := user.Create()
assert.Nil(t, err) assert.Nil(t, err)
@ -279,6 +286,17 @@ func RunUser_Create(t *testing.T) {
assert.Equal(t, int64(2), id) assert.Equal(t, int64(2), id)
} }
func RunUser_Update(t *testing.T) {
user, err := core.SelectUser(1)
user.Email = "info@updatedemail.com"
assert.Nil(t, err)
err = user.Update()
assert.Nil(t, err)
updatedUser, err := core.SelectUser(1)
assert.Nil(t, err)
assert.Equal(t, "info@updatedemail.com", updatedUser.Email)
}
func RunUser_NonUniqueCreate(t *testing.T) { func RunUser_NonUniqueCreate(t *testing.T) {
user := &core.User{ user := &core.User{
Username: "admin", Username: "admin",
@ -481,6 +499,15 @@ func RunUsersHandler(t *testing.T) {
assert.True(t, strings.Contains(rr.Body.String(), "footer")) assert.True(t, strings.Contains(rr.Body.String(), "footer"))
} }
func RunUserViewHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/user/1", nil)
assert.Nil(t, err)
rr := httptest.NewRecorder()
route.ServeHTTP(rr, req)
assert.True(t, strings.Contains(rr.Body.String(), "<title>Statup | Users</title>"))
assert.True(t, strings.Contains(rr.Body.String(), "footer"))
}
func RunServicesHandler(t *testing.T) { func RunServicesHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/services", nil) req, err := http.NewRequest("GET", "/services", nil)
assert.Nil(t, err) assert.Nil(t, err)

View File

@ -11,6 +11,15 @@ $('form').submit(function() {
}); });
$(".confirm-btn").on('click', function() {
var r = confirm("Are you sure you want to delete?");
if (r == true) {
return true;
} else {
return false;
}
});
var ranVar = false; var ranVar = false;
var ranTheme = false; var ranTheme = false;

View File

@ -22,64 +22,42 @@
<div class="col-12"> <div class="col-12">
<h3>Users</h3> <h3>User {{.Username}}</h3>
<form action="/user/{{.Id}}" method="POST">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Username</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{{range .}}
<tr>
<td>{{.Username}}</td>
<td class="text-right">
<div class="btn-group">
<a href="/users/{{.Id}}/delete" class="btn btn-danger">Delete</a>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
<h3>Create User</h3>
<form action="/users" method="POST">
<div class="form-group row"> <div class="form-group row">
<label for="username" class="col-sm-4 col-form-label">Username</label> <label for="username" class="col-sm-4 col-form-label">Username</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input type="text" name="username" class="form-control" id="username" placeholder="Username" required> <input type="text" name="username" class="form-control" value="{{.Username}}" id="username" placeholder="Username" required>
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<span class="switch"> <span class="switch">
<input type="checkbox" name="admin" class="switch" id="switch-normal"> <input type="checkbox" name="admin" class="switch" id="switch-normal"{{if .Admin}} checked{{end}}>
<label for="switch-normal">Administrator</label> <label for="switch-normal">Administrator</label>
</span> </span>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="email" class="col-sm-4 col-form-label">Email Address</label> <label for="email" class="col-sm-4 col-form-label">Email Address</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="email" name="email" class="form-control" id="email" placeholder="user@domain.com" required> <input type="email" name="email" class="form-control" id="email" value="{{.Email}}" placeholder="user@domain.com" required>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="password" class="col-sm-4 col-form-label">Password</label> <label for="password" class="col-sm-4 col-form-label">Password</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="password" name="password" class="form-control" id="password" placeholder="Password" required> <input type="password" name="password" class="form-control" id="password" value="##########" placeholder="Password" required>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="password_confirm" class="col-sm-4 col-form-label">Confirm Password</label> <label for="password_confirm" class="col-sm-4 col-form-label">Confirm Password</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="password" name="password_confirm" class="form-control" id="password_confirm" placeholder="Confirm Password" required> <input type="password" name="password_confirm" class="form-control" id="password_confirm" value="##########" placeholder="Confirm Password" required>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col-sm-12"> <div class="col-sm-12">
<button type="submit" class="btn btn-primary btn-block">Create User</button> <input type="hidden" name="user_id" value="{{.Id}}">
<button type="submit" class="btn btn-primary btn-block">Update User</button>
</div> </div>
</div> </div>
</form> </form>

View File

@ -35,9 +35,10 @@
{{range .}} {{range .}}
<tr> <tr>
<td>{{.Username}}</td> <td>{{.Username}}</td>
<td class="text-right"> <td class="text-right" id="user_{{.Id}}">
<div class="btn-group"> <div class="btn-group">
<a href="/users/{{.Id}}/delete" class="btn btn-danger">Delete</a> <a href="/user/{{.Id}}" class="btn btn-primary">Edit</a>
<a href="/user/{{.Id}}/delete" class="btn btn-danger confirm-btn" data-id="user_{{.Id}}">Delete</a>
</div> </div>
</td> </td>
</tr> </tr>