Merge branch 'master' into 고객_검색

This commit is contained in:
백창훈
2022-03-04 12:02:09 +09:00
committed by GitHub
23 changed files with 544 additions and 41 deletions

3
customer-vue/.env Normal file
View 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

View File

@@ -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"

View 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");
}
}

View File

@@ -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'
})
},
}

View 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;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View 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

View 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;
}
}

View File

@@ -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);
}
);

View File

@@ -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({

View 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>

View 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>

View File

@@ -1,3 +1,6 @@
module.exports = {
transpileDependencies: true
transpileDependencies: true,
devServer:{
allowedHosts: 'all',
}
}