Merge branch 'master' into 고객_검색
This commit is contained in:
3
customer-vue/.env
Normal file
3
customer-vue/.env
Normal file
@@ -0,0 +1,3 @@
|
||||
VUE_APP_BASEURL=http://just-pickup.com:8080
|
||||
VUE_APP_OWNER_SERVICE_BASEURL=http://just-pickup.com:8001
|
||||
VUE_APP_CUSTOMER_SERVICE_BASEURL=http://just-pickup.com:8000
|
||||
@@ -20,6 +20,8 @@
|
||||
"@vue/cli-plugin-babel": "^5.0.0",
|
||||
"@vue/cli-plugin-eslint": "^5.0.0",
|
||||
"@vue/cli-service": "^5.0.0",
|
||||
"axios": "^0.26.0",
|
||||
"moment": "^2.29.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
|
||||
26
customer-vue/src/api/auth.js
Normal file
26
customer-vue/src/api/auth.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import axios from "axios";
|
||||
import jwt from "@/common/jwt";
|
||||
|
||||
export default {
|
||||
async requestReissue() {
|
||||
const config = {
|
||||
headers: {
|
||||
"X-AUTH-TOKEN": jwt.getToken()
|
||||
}
|
||||
}
|
||||
|
||||
const res = await axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+"/user-service/auth/reissue", config);
|
||||
|
||||
const accessToken = res.data.data.accessToken;
|
||||
jwt.saveToken(accessToken);
|
||||
jwt.saveExpiredTime(res.data.data.expiredTime);
|
||||
axios.defaults.headers.common['Authorization'] = "Bearer " + accessToken;
|
||||
|
||||
return accessToken;
|
||||
},
|
||||
requestCheckAccessToken() {
|
||||
axios.defaults.headers.common['Authorization'] = "Bearer " + jwt.getToken();
|
||||
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+"/user-service/auth/check/access-token");
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,45 @@
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
requestSearchStore(latitude, longitude, storeName, page) {
|
||||
const options = {
|
||||
params: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
storeName: storeName,
|
||||
page: page
|
||||
requestSearchStore(latitude, longitude, storeName, page) {
|
||||
const options = {
|
||||
params: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
storeName: storeName,
|
||||
page: page
|
||||
}
|
||||
}
|
||||
}
|
||||
return axios.get("http://localhost:8000/store-service/search-store", options);
|
||||
}
|
||||
return axios.get("http://localhost:8000/store-service/search-store", options);
|
||||
},
|
||||
getCategoryList(){
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/category/');
|
||||
},
|
||||
putCategoryList(data){
|
||||
return axios({
|
||||
method:'put',
|
||||
url:process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/category',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
},
|
||||
data: data,
|
||||
responseType:'json'
|
||||
})
|
||||
},
|
||||
getItemById(itemId){
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/item/'+itemId)
|
||||
},
|
||||
saveItem(method, itemData){
|
||||
return axios({
|
||||
method:method,
|
||||
url: process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/item',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json;charset=UTF-8'
|
||||
},
|
||||
data: itemData,
|
||||
responseType:'json'
|
||||
})
|
||||
},
|
||||
}
|
||||
32
customer-vue/src/api/user.js
Normal file
32
customer-vue/src/api/user.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import axios from "axios";
|
||||
import jwt from '../common/jwt.js';
|
||||
|
||||
export default {
|
||||
requestRegisterUser(user) {
|
||||
return axios.post(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+"/user-service/store-owner", user);
|
||||
},
|
||||
|
||||
async requestLoginUser(email, password) {
|
||||
const user = {
|
||||
email: email,
|
||||
password: password
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+"/user-service/login", user);
|
||||
const data = response.data.data;
|
||||
|
||||
jwt.saveToken(data.accessToken);
|
||||
jwt.saveExpiredTime(data.expiredTime);
|
||||
|
||||
axios.defaults.headers.common['Authorization'] = "Bearer " + data.accessToken;
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log("Error = ", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
BIN
customer-vue/src/assets/justLogo.png
Normal file
BIN
customer-vue/src/assets/justLogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
customer-vue/src/assets/logo_google.png
Normal file
BIN
customer-vue/src/assets/logo_google.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
19
customer-vue/src/assets/logo_naver.svg
Normal file
19
customer-vue/src/assets/logo_naver.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="53" height="52" viewBox="0 0 53 52">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<g transform="translate(-794.000000, -2855.000000) translate(166.000000, 2282.000000) translate(0.500000, 381.500000) translate(486.500000, 191.500000) translate(73.500000, 0.000000) translate(68.000000, 0.000000)">
|
||||
<circle cx="26" cy="26" r="26" fill="#1EC800"/>
|
||||
<path fill="#FFF" fill-opacity="0" d="M12.9 13.265H38.9V39.265H12.9z"/>
|
||||
<path fill="#FFF" d="M28.997 17.524L28.997 26.343 22.823 17.524 16.15 17.524 16.15 35.006 22.801 35.006 22.801 26.186 28.976 35.006 35.65 35.006 35.65 17.524z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1000 B |
38
customer-vue/src/common/jwt.js
Normal file
38
customer-vue/src/common/jwt.js
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
const moment = require('moment');
|
||||
const ACCESS_TOKEN_NAME = "accessToken";
|
||||
const EXPIRED_TIME_NAME = "expiredTime";
|
||||
|
||||
const tag = "[jwt]";
|
||||
|
||||
export default {
|
||||
getToken() {
|
||||
return localStorage.getItem(ACCESS_TOKEN_NAME);
|
||||
},
|
||||
saveToken(token) {
|
||||
localStorage.setItem(ACCESS_TOKEN_NAME, token);
|
||||
},
|
||||
getExpiredTime() {
|
||||
return localStorage.getItem(EXPIRED_TIME_NAME);
|
||||
},
|
||||
saveExpiredTime(expiredTime) {
|
||||
localStorage.setItem(EXPIRED_TIME_NAME, expiredTime);
|
||||
},
|
||||
destroyAll() {
|
||||
localStorage.removeItem(ACCESS_TOKEN_NAME);
|
||||
localStorage.removeItem(EXPIRED_TIME_NAME);
|
||||
},
|
||||
isExpired() {
|
||||
const expiredTime = this.getExpiredTime();
|
||||
|
||||
const expiredMoment = moment(expiredTime);
|
||||
let currentMoment = moment();
|
||||
|
||||
const difference = moment.duration(expiredMoment.diff(currentMoment)).asSeconds();
|
||||
|
||||
console.log(tag, "expireMoment = ", expiredMoment, "currentMoment = ", currentMoment, "diff = ", difference);
|
||||
|
||||
// 만료 30초 전일 경우 만료로 판단
|
||||
return difference <= 30;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@ import Vue from 'vue';
|
||||
import App from './App.vue';
|
||||
import vuetify from "@/plugins/vuetify";
|
||||
import router from "./router/router.js";
|
||||
import axios from "axios";
|
||||
import auth from "@/api/auth";
|
||||
|
||||
axios.defaults.withCredentials = true;
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
@@ -10,3 +13,29 @@ new Vue({
|
||||
router,
|
||||
render: h => h(App),
|
||||
}).$mount('#app')
|
||||
|
||||
axios.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
if (error.response.status === 401) {
|
||||
let code = error.response.data.code;
|
||||
if (code === "EXPIRED") {
|
||||
console.log("## expired");
|
||||
try {
|
||||
const accessToken = await auth.requestReissue();
|
||||
originalRequest.headers.Authorization = "Bearer " + accessToken;
|
||||
return axios(originalRequest);
|
||||
} catch (reissueError) {
|
||||
window.location.href = process.env.VUE_APP_BASEURL+"/login";
|
||||
alert("권한이 없습니다. 다시 로그인 해주세요");
|
||||
}
|
||||
}
|
||||
window.location.href = process.env.VUE_APP_BASEURL+"/login";
|
||||
alert("권한이 없습니다. 다시 로그인해주세요.");
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
@@ -2,9 +2,17 @@ import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
|
||||
import HomeLayout from '../views/Layout/HomeLayout.vue';
|
||||
const ACCESS_TOKEN_NAME = "accessToken";
|
||||
const EXPIRED_TIME_NAME = "expiredTime";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const auth = async function (to, from, next) {
|
||||
localStorage.setItem(ACCESS_TOKEN_NAME, to.query.accessToken);
|
||||
localStorage.setItem(EXPIRED_TIME_NAME, to.query.expiredTime)
|
||||
next();
|
||||
};
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
@@ -22,7 +30,26 @@ const routes = [
|
||||
component: () => import('../views/SearchStore')
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
redirect: 'login',
|
||||
component: HomeLayout,
|
||||
children: [
|
||||
{
|
||||
path: "/login",
|
||||
name: 'login',
|
||||
component: () => import('../views/LoginPage')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
name: 'auth',
|
||||
beforeEnter: auth,
|
||||
component: () => import('../views/Layout/AuthSuccess.vue')
|
||||
|
||||
},
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
|
||||
20
customer-vue/src/views/Layout/AuthSuccess.vue
Normal file
20
customer-vue/src/views/Layout/AuthSuccess.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<div>인증에 성공하였습니다.</div>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "AuthSuccess",
|
||||
components: {
|
||||
},mounted() {
|
||||
opener.document.location.href=process.env.VUE_APP_BASEURL;
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
120
customer-vue/src/views/LoginPage.vue
Normal file
120
customer-vue/src/views/LoginPage.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<v-container
|
||||
fill-height
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div align="center" ><v-img
|
||||
max-height="150"
|
||||
max-width="250"
|
||||
:src="logo"></v-img></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row
|
||||
justify="center"
|
||||
>
|
||||
<v-col class="align-content-center">
|
||||
|
||||
|
||||
<v-form ref="form" lazy-validation>
|
||||
<v-text-field
|
||||
:rules="[v => /.+@.+\..+/.test(v) || 'E-mail must be valid', v => !!v || '이메일은 필수 값입니다']"
|
||||
label="이메일"
|
||||
prepend-icon="mdi-account-circle"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
:rules="[v => !!v || '비밀번호는 필수 값입니다']"
|
||||
label="비밀번호"
|
||||
type="Password"
|
||||
prepend-icon="mdi-lock"
|
||||
append-icon="mdi-eye-off"
|
||||
></v-text-field>
|
||||
<v-btn
|
||||
block
|
||||
>
|
||||
Login
|
||||
</v-btn>
|
||||
<div class="d-block my-7" align="center">
|
||||
<v-subheader class="d-inline" >소셜 아이디로 로그인해보세요!</v-subheader>
|
||||
</div>
|
||||
<div class="d-block " align="center">
|
||||
<v-btn
|
||||
class="mx-2"
|
||||
fab
|
||||
small
|
||||
>
|
||||
<v-img
|
||||
class="d-inline-block align-lg-center mx-5"
|
||||
style=""
|
||||
max-width="38"
|
||||
max-height="38"
|
||||
:src="logo_naver"
|
||||
@click="login_auth('naver')"
|
||||
/>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="mx-2"
|
||||
fab
|
||||
small
|
||||
>
|
||||
<v-img
|
||||
class="d-inline-block mx-5"
|
||||
max-width="38"
|
||||
max-height="38"
|
||||
min-width="38"
|
||||
min-height="38"
|
||||
:src="logo_google"
|
||||
@click="login_auth('google')"
|
||||
/>
|
||||
</v-btn>
|
||||
|
||||
</div>
|
||||
|
||||
</v-form>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import logo from '@/assets/justLogo.png'
|
||||
import logo_naver from '@/assets/logo_naver.svg'
|
||||
import logo_google from '@/assets/logo_google.png'
|
||||
|
||||
export default {
|
||||
name: "LoginPage",
|
||||
data (){
|
||||
return {
|
||||
logo : logo,
|
||||
logo_naver: logo_naver,
|
||||
logo_google : logo_google,
|
||||
auth_popup: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
auth_popup : function () {
|
||||
this.auth_popup.addEventListener('beforeunload', function() {
|
||||
window.location.href=process.env.VUE_APP_BASEURL;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
login_auth: async function(target) {
|
||||
const _url = process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/user-service/oauth2/authorization/'+target
|
||||
this.auth_popup = window.open(
|
||||
_url,
|
||||
"",
|
||||
"width=600,height=400,left=200,top=200"
|
||||
);
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,3 +1,6 @@
|
||||
module.exports = {
|
||||
transpileDependencies: true
|
||||
transpileDependencies: true,
|
||||
devServer:{
|
||||
allowedHosts: 'all',
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user