Skip to content

Commit 02ffdd0

Browse files
author
Julien Neuhart
committed
wip, almost done
1 parent 608d3f3 commit 02ffdd0

31 files changed

+908
-82
lines changed

sources/app/.eslintrc.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,8 @@
1212
"extends": [
1313
"eslint:recommended",
1414
"plugin:vue/recommended"
15-
]
15+
],
16+
"rules": {
17+
"indent": ["error", 2]
18+
}
1619
}

sources/app/assets/vue/App.vue

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@
3939
>
4040
<a class="nav-link">Posts</a>
4141
</router-link>
42+
<li
43+
v-if="isAuthenticated"
44+
class="nav-item"
45+
>
46+
<a
47+
class="nav-link"
48+
href="/api/security/logout"
49+
>Logout</a>
50+
</li>
4251
</ul>
4352
</div>
4453
</nav>
@@ -48,7 +57,34 @@
4857
</template>
4958

5059
<script>
51-
export default {
52-
name: "App",
53-
}
60+
import axios from "axios";
61+
62+
export default {
63+
name: "App",
64+
computed: {
65+
isAuthenticated() {
66+
return this.$store.getters["security/isAuthenticated"]
67+
},
68+
},
69+
created() {
70+
let isAuthenticated = JSON.parse(this.$parent.$el.attributes["data-is-authenticated"].value),
71+
user = JSON.parse(this.$parent.$el.attributes["data-user"].value);
72+
73+
let payload = { isAuthenticated: isAuthenticated, user: user };
74+
this.$store.dispatch("security/onRefresh", payload);
75+
76+
axios.interceptors.response.use(undefined, (err) => {
77+
return new Promise(() => {
78+
if (err.response.status === 401) {
79+
this.$router.push({path: "/login"})
80+
} else if (err.response.status === 500) {
81+
document.open();
82+
document.write(err.response.data);
83+
document.close();
84+
}
85+
throw err;
86+
});
87+
});
88+
},
89+
}
5490
</script>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import axios from "axios";
2+
3+
export default {
4+
login(login, password) {
5+
return axios.post("/api/security/login", {
6+
username: login,
7+
password: password
8+
});
9+
}
10+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<template>
2+
<div
3+
class="alert alert-danger"
4+
role="alert"
5+
>
6+
{{ error.response.data.error }}
7+
</div>
8+
</template>
9+
10+
<script>
11+
export default {
12+
name: "ErrorMessage",
13+
props: {
14+
error: {
15+
type: Object,
16+
required: true
17+
}
18+
},
19+
}
20+
</script>

sources/app/assets/vue/components/Post.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default {
1212
props: {
1313
message: {
1414
type: String,
15-
required: true,
15+
required: true
1616
}
1717
}
1818
};
Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
11
import Vue from "vue";
22
import VueRouter from "vue-router";
3+
import store from "../store";
34
import Home from "../views/Home";
5+
import Login from "../views/Login";
46
import Posts from "../views/Posts";
57

68
Vue.use(VueRouter);
79

8-
export default new VueRouter({
10+
let router = new VueRouter({
911
mode: "history",
1012
routes: [
1113
{ path: "/home", component: Home },
12-
{ path: "/posts", component: Posts },
14+
{ path: "/login", component: Login },
15+
{ path: "/posts", component: Posts, meta: { requiresAuth: true } },
1316
{ path: "*", redirect: "/home" }
14-
]
17+
],
1518
});
19+
20+
router.beforeEach((to, from, next) => {
21+
if (to.matched.some(record => record.meta.requiresAuth)) {
22+
// this route requires auth, check if logged in
23+
// if not, redirect to login page.
24+
if (store.getters["security/isAuthenticated"]) {
25+
next();
26+
} else {
27+
next({
28+
path: "/login",
29+
query: { redirect: to.fullPath }
30+
});
31+
}
32+
} else {
33+
next(); // make sure to always call next()!
34+
}
35+
});
36+
37+
export default router;

sources/app/assets/vue/store/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import Vue from "vue";
22
import Vuex from "vuex";
3+
import SecurityModule from "./security";
34
import PostModule from "./post";
45

56
Vue.use(Vuex);
67

78
export default new Vuex.Store({
89
modules: {
10+
security: SecurityModule,
911
post: PostModule
1012
}
1113
});
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import SecurityAPI from "../api/security";
2+
3+
const AUTHENTICATING = "AUTHENTICATING",
4+
AUTHENTICATING_SUCCESS = "AUTHENTICATING_SUCCESS",
5+
AUTHENTICATING_ERROR = "AUTHENTICATING_ERROR",
6+
PROVIDING_DATA_ON_REFRESH_SUCCESS = "PROVIDING_DATA_ON_REFRESH_SUCCESS";
7+
8+
export default {
9+
namespaced: true,
10+
state: {
11+
isLoading: false,
12+
error: null,
13+
isAuthenticated: false,
14+
user: null
15+
},
16+
getters: {
17+
isLoading(state) {
18+
return state.isLoading;
19+
},
20+
hasError(state) {
21+
return state.error !== null;
22+
},
23+
error(state) {
24+
return state.error;
25+
},
26+
isAuthenticated(state) {
27+
return state.isAuthenticated;
28+
},
29+
hasRole(state) {
30+
return role => {
31+
return state.user.roles.indexOf(role) !== -1;
32+
}
33+
}
34+
},
35+
mutations: {
36+
[AUTHENTICATING](state) {
37+
state.isLoading = true;
38+
state.error = null;
39+
state.isAuthenticated = false;
40+
state.user = null;
41+
},
42+
[AUTHENTICATING_SUCCESS](state, user) {
43+
state.isLoading = false;
44+
state.error = null;
45+
state.isAuthenticated = true;
46+
state.user = user;
47+
},
48+
[AUTHENTICATING_ERROR](state, error) {
49+
state.isLoading = false;
50+
state.error = error;
51+
state.isAuthenticated = false;
52+
state.user = null;
53+
},
54+
[PROVIDING_DATA_ON_REFRESH_SUCCESS](state, payload) {
55+
state.isLoading = false;
56+
state.error = null;
57+
state.isAuthenticated = payload.isAuthenticated;
58+
state.user = payload.user;
59+
}
60+
},
61+
actions: {
62+
async login({commit}, payload) {
63+
commit(AUTHENTICATING);
64+
try {
65+
let response = await SecurityAPI.login(payload.login, payload.password);
66+
commit(AUTHENTICATING_SUCCESS, response.data);
67+
return response.data;
68+
} catch (error) {
69+
commit(AUTHENTICATING_ERROR, error);
70+
return null;
71+
}
72+
},
73+
onRefresh({commit}, payload) {
74+
commit(PROVIDING_DATA_ON_REFRESH_SUCCESS, payload);
75+
}
76+
}
77+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<template>
2+
<div>
3+
<div class="row col">
4+
<h1>Login</h1>
5+
</div>
6+
7+
<div class="row col">
8+
<form>
9+
<div class="form-row">
10+
<div class="col-4">
11+
<input
12+
v-model="login"
13+
type="text"
14+
class="form-control"
15+
>
16+
</div>
17+
<div class="col-4">
18+
<input
19+
v-model="password"
20+
type="password"
21+
class="form-control"
22+
>
23+
</div>
24+
<div class="col-4">
25+
<button
26+
:disabled="login.length === 0 || password.length === 0 || isLoading"
27+
type="button"
28+
class="btn btn-primary"
29+
@click="performLogin()"
30+
>
31+
Login
32+
</button>
33+
</div>
34+
</div>
35+
</form>
36+
</div>
37+
38+
<div
39+
v-if="isLoading"
40+
class="row col"
41+
>
42+
<p>Loading...</p>
43+
</div>
44+
45+
<div
46+
v-else-if="hasError"
47+
class="row col"
48+
>
49+
<error-message :error="error" />
50+
</div>
51+
</div>
52+
</template>
53+
54+
<script>
55+
import ErrorMessage from "../components/ErrorMessage";
56+
57+
export default {
58+
name: "Login",
59+
components: {
60+
ErrorMessage,
61+
},
62+
data() {
63+
return {
64+
login: "",
65+
password: ""
66+
};
67+
},
68+
computed: {
69+
isLoading() {
70+
return this.$store.getters["security/isLoading"];
71+
},
72+
hasError() {
73+
return this.$store.getters["security/hasError"];
74+
},
75+
error() {
76+
return this.$store.getters["security/error"];
77+
}
78+
},
79+
created() {
80+
let redirect = this.$route.query.redirect;
81+
82+
if (this.$store.getters["security/isAuthenticated"]) {
83+
if (typeof redirect !== "undefined") {
84+
this.$router.push({path: redirect});
85+
} else {
86+
this.$router.push({path: "/home"});
87+
}
88+
}
89+
},
90+
methods: {
91+
async performLogin() {
92+
let payload = {login: this.$data.login, password: this.$data.password},
93+
redirect = this.$route.query.redirect;
94+
95+
await this.$store.dispatch("security/login", payload);
96+
if (!this.$store.getters["security/hasError"]) {
97+
if (typeof redirect !== "undefined") {
98+
this.$router.push({path: redirect});
99+
} else {
100+
this.$router.push({path: "/home"});
101+
}
102+
}
103+
}
104+
}
105+
}
106+
</script>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy