Browse Source

Add logging & authentication middleware

feature/add-admin-posts-frontend
Tovi Jaeschke-Rogers 3 years ago
parent
commit
b137b50766
9 changed files with 139 additions and 107 deletions
  1. +0
    -37
      Api/PostsAdmin.go
  2. +66
    -22
      Api/Routes.go
  3. +0
    -30
      Api/Users.go
  4. +56
    -4
      Frontend/vue/src/components/admin/components/navbar/AdminNavbar.vue
  5. +2
    -2
      Frontend/vue/src/components/admin/views/posts/AdminPostsForm.vue
  6. +10
    -5
      Frontend/vue/src/components/admin/views/posts/AdminPostsList.vue
  7. +1
    -6
      Frontend/vue/src/main.js
  8. +1
    -1
      Frontend/vue/src/router/index.js
  9. +3
    -0
      Models/Posts.go

+ 0
- 37
Api/PostsAdmin.go View File

@ -9,7 +9,6 @@ import (
"strconv" "strconv"
"time" "time"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/Auth"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/JsonSerialization" "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/JsonSerialization"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database" "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models" "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models"
@ -26,12 +25,6 @@ func getPosts(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
values = r.URL.Query() values = r.URL.Query()
page, err = strconv.Atoi(values.Get("page")) page, err = strconv.Atoi(values.Get("page"))
@ -80,12 +73,6 @@ func getPost(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
postData, err = Util.GetPostById(w, r) postData, err = Util.GetPostById(w, r)
if err != nil { if err != nil {
return return
@ -110,12 +97,6 @@ func createPost(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
requestBody, err = ioutil.ReadAll(r.Body) requestBody, err = ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
log.Printf("Error encountered reading POST body: %s\n", err.Error()) log.Printf("Error encountered reading POST body: %s\n", err.Error())
@ -162,12 +143,6 @@ func updatePost(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
id, err = Util.GetPostId(r) id, err = Util.GetPostId(r)
if err != nil { if err != nil {
log.Printf("Error encountered getting id\n") log.Printf("Error encountered getting id\n")
@ -216,12 +191,6 @@ func publishPost(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
postData, err = Util.GetPostById(w, r) postData, err = Util.GetPostById(w, r)
if err != nil { if err != nil {
log.Printf("Error encountered getting id\n") log.Printf("Error encountered getting id\n")
@ -260,12 +229,6 @@ func deletePost(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
postData, err = Util.GetPostById(w, r) postData, err = Util.GetPostById(w, r)
if err != nil { if err != nil {
return return


+ 66
- 22
Api/Routes.go View File

@ -2,49 +2,93 @@ package Api
import ( import (
"log" "log"
"net/http"
"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/Auth" "git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Api/Auth"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf(
"%s %s %s, Content Length: %d",
r.RemoteAddr,
r.Method,
r.RequestURI,
r.ContentLength,
)
next.ServeHTTP(w, r)
})
}
func authenticationMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var (
userSession Auth.Session
err error
)
userSession, err = Auth.CheckCookie(r)
if err != nil {
http.Error(w, "Forbidden", http.StatusUnauthorized)
return
}
log.Printf(
"Authenticated user: %s (%s)",
userSession.Email,
userSession.UserID,
)
next.ServeHTTP(w, r)
})
}
func InitApiEndpoints(router *mux.Router) { func InitApiEndpoints(router *mux.Router) {
var ( var (
api *mux.Router
api *mux.Router
adminApi *mux.Router
) )
log.Println("Initializing API routes...") log.Println("Initializing API routes...")
api = router.PathPrefix("/api/v1/").Subrouter() api = router.PathPrefix("/api/v1/").Subrouter()
api.Use(loggingMiddleware)
api.HandleFunc("/posts/front-page", getFrontPagePosts).Methods("GET") api.HandleFunc("/posts/front-page", getFrontPagePosts).Methods("GET")
api.HandleFunc("/post", getPostsPublic).Methods("GET") api.HandleFunc("/post", getPostsPublic).Methods("GET")
api.HandleFunc("/post/{postID}", getPostPublic).Methods("GET") api.HandleFunc("/post/{postID}", getPostPublic).Methods("GET")
// Define routes for posts api
api.HandleFunc("/admin/post", getPosts).Methods("GET")
api.HandleFunc("/admin/post/{postID}", getPost).Methods("GET")
api.HandleFunc("/admin/post", createPost).Methods("POST")
api.HandleFunc("/admin/post/{postID}", updatePost).Methods("PUT")
api.HandleFunc("/admin/post/{postID}", deletePost).Methods("DELETE")
api.HandleFunc("/admin/post/{postID}/publish", publishPost).Methods("GET")
api.HandleFunc("/admin/post/{postID}/image", createPostImage).Methods("POST")
api.HandleFunc("/admin/post/{postID}/image/{imageID}", deletePostImage).Methods("DELETE")
// Define routes for users api
api.HandleFunc("/admin/user", getUsers).Methods("GET")
api.HandleFunc("/admin/user", createUser).Methods("POST")
api.HandleFunc("/admin/user/{userID}", getUser).Methods("GET")
api.HandleFunc("/admin/user/{userID}", updateUser).Methods("PUT")
api.HandleFunc("/admin/user/{userID}", deletePost).Methods("DELETE")
api.HandleFunc("/admin/user/{userID}/update-password", Auth.UpdatePassword).Methods("PUT")
// Define routes for authentication // Define routes for authentication
api.HandleFunc("/admin/login", Auth.Login).Methods("POST") api.HandleFunc("/admin/login", Auth.Login).Methods("POST")
api.HandleFunc("/admin/logout", Auth.Logout).Methods("GET") api.HandleFunc("/admin/logout", Auth.Logout).Methods("GET")
api.HandleFunc("/admin/me", Auth.Me).Methods("GET") api.HandleFunc("/admin/me", Auth.Me).Methods("GET")
// router.PathPrefix("/").Handler(http.StripPrefix("/images/", http.FileServer(http.Dir("./uploads"))))
adminApi = api.PathPrefix("/admin/").Subrouter()
adminApi.Use(authenticationMiddleware)
// Define routes for posts api
adminApi.HandleFunc("/post", getPosts).Methods("GET")
adminApi.HandleFunc("/post/{postID}", getPost).Methods("GET")
adminApi.HandleFunc("/post", createPost).Methods("POST")
adminApi.HandleFunc("/post/{postID}", updatePost).Methods("PUT")
adminApi.HandleFunc("/post/{postID}", deletePost).Methods("DELETE")
adminApi.HandleFunc("/post/{postID}/publish", publishPost).Methods("GET")
adminApi.HandleFunc("/post/{postID}/image", createPostImage).Methods("POST")
adminApi.HandleFunc("/post/{postID}/image/{imageID}", deletePostImage).Methods("DELETE")
// Define routes for users api
adminApi.HandleFunc("/user", getUsers).Methods("GET")
adminApi.HandleFunc("/user", createUser).Methods("POST")
adminApi.HandleFunc("/user/{userID}", getUser).Methods("GET")
adminApi.HandleFunc("/user/{userID}", updateUser).Methods("PUT")
adminApi.HandleFunc("/user/{userID}", deletePost).Methods("DELETE")
adminApi.HandleFunc("/user/{userID}/update-password", Auth.UpdatePassword).Methods("PUT")
} }

+ 0
- 30
Api/Users.go View File

@ -25,12 +25,6 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
values = r.URL.Query() values = r.URL.Query()
page, err = strconv.Atoi(values.Get("page")) page, err = strconv.Atoi(values.Get("page"))
@ -79,12 +73,6 @@ func getUser(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
userData, err = Util.GetUserById(w, r) userData, err = Util.GetUserById(w, r)
if err != nil { if err != nil {
return return
@ -109,12 +97,6 @@ func createUser(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
requestBody, err = ioutil.ReadAll(r.Body) requestBody, err = ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
log.Printf("Error encountered reading POST body: %s\n", err.Error()) log.Printf("Error encountered reading POST body: %s\n", err.Error())
@ -184,12 +166,6 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
id, err = Util.GetUserId(r) id, err = Util.GetUserId(r)
if err != nil { if err != nil {
log.Printf("Error encountered reading POST body: %s\n", err.Error()) log.Printf("Error encountered reading POST body: %s\n", err.Error())
@ -236,12 +212,6 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
err error err error
) )
_, err = Auth.CheckCookie(r)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
userData, err = Util.GetUserById(w, r) userData, err = Util.GetUserById(w, r)
if err != nil { if err != nil {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)


+ 56
- 4
Frontend/vue/src/components/admin/components/navbar/AdminNavbar.vue View File

@ -39,7 +39,49 @@
</router-link> </router-link>
</li> </li>
<li class="nav-item dropdown rounded">
<li class="nav-item rounded d-md-none">
<a
href="#"
class="nav-link"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarDropdown"
aria-controls="navbarNav"
aria-expanded="false"
aria-label="Toggle navigation"
>
<i class="fa-solid fa-user"></i>
Profile
</a>
<!--
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" data-bs-target="navbarDropdown" aria-expanded="false">
<i class="fa-solid fa-user"></i>
Profile
</a>
-->
<div class="collapse d-md-none" id="navbarDropdown">
<ul class="navbar-nav ms-md-auto gap-2">
<li class="nav-item rounded nav-item-indented">
<router-link
v-if="$store.getters.getUser"
:to="{ name: 'AdminUsersForm', params: { id: $store.getters.getUser.id } }"
class="nav-link nav-link-indented"
>
Account
</router-link>
</li>
<li class="nav-item rounded nav-item-indented">
<a href="#" v-on:click="logout" class="nav-link nav-link-indented">Logout</a>
</li>
</ul>
</div>
</li>
<li class="nav-item dropdown rounded d-none d-md-block">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa-solid fa-user"></i> <i class="fa-solid fa-user"></i>
Profile Profile
@ -47,8 +89,8 @@
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<li> <li>
<router-link
:to="{ name: 'AdminUsersForm', params: { id: $store.getters.getUser.id } }"
<router-link
:to="{ name: 'AdminUsersForm', params: { id: $store.getters.getUser.id } }"
class="dropdown-item" class="dropdown-item"
> >
Account Account
@ -61,7 +103,6 @@
<div v-on:click="logout" class="dropdown-item">Logout</div> <div v-on:click="logout" class="dropdown-item">Logout</div>
</li> </li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -93,6 +134,17 @@ export default {
body { body {
font-family: Montserrat, sans-serif; font-family: Montserrat, sans-serif;
} }
.nav-item {
padding-left: 1rem;
padding-right: 1rem;
}
.nav-item-indented {
padding-left: 2.5rem;
padding-right: 2.5rem;
}
.navbar-nav .nav-item:hover { .navbar-nav .nav-item:hover {
background-color: rgba(180, 190, 203, 0.4); background-color: rgba(180, 190, 203, 0.4);
} }


+ 2
- 2
Frontend/vue/src/components/admin/views/posts/AdminPostsForm.vue View File

@ -33,7 +33,7 @@
<div class="row"> <div class="row">
<div class="col-md-8 mb-4">
<div class="col-md-7 mb-4">
<div class="form-outline"> <div class="form-outline">
<Field <Field
v-model="post.title" v-model="post.title"
@ -62,7 +62,7 @@
</div> </div>
</div> </div>
<div class="col-md-1 mb-4" v-if="post.front_page">
<div class="col-md-2 mb-4" v-if="post.front_page">
<div class="form-outline"> <div class="form-outline">
<Field <Field
v-model="post.order" v-model="post.order"


+ 10
- 5
Frontend/vue/src/components/admin/views/posts/AdminPostsList.vue View File

@ -68,12 +68,17 @@ export default {
AdminListHeader, AdminListHeader,
}, },
beforeMount () {
this.getInitialPosts()
},
async created () {
try {
const response = await this.axios.get('/admin/me')
if (response.status !== 200) {
return
}
} catch (e) {
return
}
mounted () {
this.axios.get('/admin/me')
this.getInitialPosts()
this.getNextPosts() this.getNextPosts()
}, },


+ 1
- 6
Frontend/vue/src/main.js View File

@ -6,7 +6,6 @@ import { defineRule } from 'vee-validate';
import AllRules from '@vee-validate/rules'; import AllRules from '@vee-validate/rules';
import Toaster from "@meforma/vue-toaster"; import Toaster from "@meforma/vue-toaster";
import Datepicker from '@vuepic/vue-datepicker'; import Datepicker from '@vuepic/vue-datepicker';
import BootstrapVue from 'bootstrap-vue-3'
import { library } from "@fortawesome/fontawesome-svg-core"; import { library } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
@ -25,10 +24,7 @@ import router from './router'
import admin from './store/admin/index.js' import admin from './store/admin/index.js'
import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.bundle.js'
import "bootstrap-vue-3/dist/bootstrap-vue-3.css"
import "bootstrap-vue-3/dist/bootstrap-vue-3.es.js"
import "bootstrap-vue-3/dist/bootstrap-vue-3.umd.js"
import 'bootstrap/dist/js/bootstrap.bundle.min.js'
import '@vuepic/vue-datepicker/dist/main.css' import '@vuepic/vue-datepicker/dist/main.css'
import './assets/css/admin.css' import './assets/css/admin.css'
@ -41,7 +37,6 @@ app.use(VueAxios, axios)
app.use(VueCookies) app.use(VueCookies)
app.use(admin) app.use(admin)
app.use(Toaster, { position: 'top-right' }) app.use(Toaster, { position: 'top-right' })
app.use(BootstrapVue)
Object.keys(AllRules).forEach(rule => { Object.keys(AllRules).forEach(rule => {
defineRule(rule, AllRules[rule]); defineRule(rule, AllRules[rule]);


+ 1
- 1
Frontend/vue/src/router/index.js View File

@ -77,7 +77,7 @@ const router = createRouter({
}); });
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
const user = admin.getUser;
const user = admin.getters.getUser ?? null;
if ((to.name == 'AdminLogin' || to.name == 'AdminSignup') && user !== null && !to.params.unauthorized) { if ((to.name == 'AdminLogin' || to.name == 'AdminSignup') && user !== null && !to.params.unauthorized) {
next({ name: 'AdminUsersList' }); next({ name: 'AdminUsersList' });


+ 3
- 0
Models/Posts.go View File

@ -45,6 +45,7 @@ type PostImage struct {
PublicFilepath string `gorm:"not null" json:"filepath"` PublicFilepath string `gorm:"not null" json:"filepath"`
Mimetype string `gorm:"not null" json:"mimetype"` Mimetype string `gorm:"not null" json:"mimetype"`
Size int64 `gorm:"not null"` Size int64 `gorm:"not null"`
Order int `gorm:"not null" json:"order"`
} }
type PostVideo struct { type PostVideo struct {
@ -54,6 +55,7 @@ type PostVideo struct {
Mimetype string `json:"mimetype"` Mimetype string `json:"mimetype"`
Size int64 `json:"size"` Size int64 `json:"size"`
Url string `json:"url"` Url string `json:"url"`
Order int `gorm:"not null" json:"order"`
} }
type PostAudio struct { type PostAudio struct {
@ -63,4 +65,5 @@ type PostAudio struct {
Mimetype string `json:"mimetype"` Mimetype string `json:"mimetype"`
Size int64 `json:"size"` Size int64 `json:"size"`
Url string `json:"url"` Url string `json:"url"`
Order int `gorm:"not null" json:"order"`
} }

Loading…
Cancel
Save