diff --git a/config/dev.env.js b/config/dev.env.js index 13ea61f7..f2b2a130 100644 --- a/config/dev.env.js +++ b/config/dev.env.js @@ -1,5 +1,4 @@ module.exports = { NODE_ENV: '"development"', - ENV_CONFIG: '"dev"', - BASE_API: '"http://localhost:4000"' + ENV_CONFIG: '"dev"' } diff --git a/package.json b/package.json index 81dff90c..097c291f 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "eslint-loader": "2.0.0", "eslint-plugin-vue": "4.7.1", "file-loader": "1.1.11", + "flush-promises": "^1.0.2", "friendly-errors-webpack-plugin": "1.7.0", "hash-sum": "1.0.2", "html-webpack-plugin": "^3.2.0", diff --git a/src/api/__mocks__/login.js b/src/api/__mocks__/login.js new file mode 100644 index 00000000..9f1bfeb1 --- /dev/null +++ b/src/api/__mocks__/login.js @@ -0,0 +1,59 @@ +const users = [ + { username: 'bob', password: '123456' } +] + +export async function loginByUsername(username, password) { + const user = users.find(user => user.username === username) + const verifyPassword = user.password === password + const data = { + 'token_type': 'Bearer', + 'scope': 'read write follow', + 'refresh_token': 'foo123', + 'me': 'bob', + 'expires_in': 600, + 'access_token': 'bar123' + } + + return verifyPassword + ? Promise.resolve({ data }) + : Promise.reject({ message: 'Invalid credentials' }) +} + +export function getUserInfo(token, authHost) { + const userInfo = { + 'name_html': 'bob', + 'background_image': null, + 'friends_count': 0, + 'description_html': '', + 'followers_count': 0, + 'locked': false, + 'follows_you': true, + 'statusnet_blocking': false, + 'statusnet_profile_url': '', + 'following': true, + 'id': '10', + 'is_local': true, + 'profile_image_url': '', + 'role': 'admin', + 'profile_image_url_profile_size': '', + 'rights': { 'admin': true, 'delete_others_notice': true }, + 'token': 'foo123456', + 'no_rich_text': false, + 'statuses_count': 0, + 'cover_photo': '', + 'hide_follows': false, + 'pleroma': { 'confirmation_pending': false, 'deactivated': false, 'tags': ['force_nsfw'] }, + 'profile_image_url_original': '', + 'created_at': 'Fri Mar 01 15:15:19 +0000 2019', + 'fields': [], + 'name': 'bob', + 'description': '', + 'favourites_count': 0, + 'default_scope': 'public', + 'profile_image_url_https': '', + 'hide_followers': false, + 'show_role': true, + 'screen_name': 'bob' } + + return Promise.resolve({ data: userInfo }) +} diff --git a/src/api/__mocks__/users.js b/src/api/__mocks__/users.js index c8e6fe94..73a93174 100644 --- a/src/api/__mocks__/users.js +++ b/src/api/__mocks__/users.js @@ -4,7 +4,7 @@ const users = [ { deactivated: true, id: 'abc', nickname: 'john', local: true, roles: { admin: false, moderator: false }, tags: ['strip_media'] } ] -export async function fetchUsers(showLocalUsersOnly, token, page = 1) { +export async function fetchUsers(showLocalUsersOnly, authHost, token, page = 1) { const filteredUsers = showLocalUsersOnly ? users.filter(user => user.local) : users return Promise.resolve({ data: { users: filteredUsers, @@ -13,12 +13,12 @@ export async function fetchUsers(showLocalUsersOnly, token, page = 1) { }}) } -export async function toggleUserActivation(nickname, token) { +export async function toggleUserActivation(nickname, authHost, token) { const response = users.find(user => user.nickname === nickname) return Promise.resolve({ data: { ...response, deactivated: !response.deactivated }}) } -export async function searchUsers(query, showLocalUsersOnly, token, page = 1) { +export async function searchUsers(query, showLocalUsersOnly, authHost, token, page = 1) { const filteredUsers = showLocalUsersOnly ? users.filter(user => user.local) : users const response = filteredUsers.filter(user => user.nickname === query) return Promise.resolve({ data: { @@ -28,28 +28,28 @@ export async function searchUsers(query, showLocalUsersOnly, token, page = 1) { }}) } -export async function addRight(nickname, right, token) { +export async function addRight(nickname, right, authHost, token) { return Promise.resolve({ data: { [`is_${right}`]: true } }) } -export async function deleteRight(nickname, right, token) { +export async function deleteRight(nickname, right, authHost, token) { return Promise.resolve({ data: { [`is_${right}`]: false } }) } -export async function deleteUser(nickname, token) { +export async function deleteUser(nickname, authHost, token) { return Promise.resolve({ data: nickname }) } -export async function tagUser(nickname, tag, token) { +export async function tagUser(nickname, tag, authHost, token) { return Promise.resolve() } -export async function untagUser(nickname, tag, token) { +export async function untagUser(nickname, tag, authHost, token) { return Promise.resolve() } diff --git a/src/api/login.js b/src/api/login.js index e3d990d1..adaabd54 100644 --- a/src/api/login.js +++ b/src/api/login.js @@ -1,7 +1,9 @@ import request from '@/utils/request' +import { baseName } from './utils' -export async function loginByUsername(username, password) { +export async function loginByUsername(username, password, authHost) { const appsRequest = await request({ + baseURL: baseName(authHost), url: '/api/v1/apps', method: 'post', data: { @@ -14,6 +16,7 @@ export async function loginByUsername(username, password) { const app = appsRequest.data return request({ + baseURL: baseName(authHost), url: '/oauth/token', method: 'post', data: { @@ -26,8 +29,9 @@ export async function loginByUsername(username, password) { }) } -export function getUserInfo(token) { +export function getUserInfo(token, authHost) { return request({ + baseURL: baseName(authHost), url: '/api/account/verify_credentials', method: 'post', headers: token ? { 'Authorization': `Bearer ${token}` } : {} diff --git a/src/api/users.js b/src/api/users.js index 43ebe5e1..2ec6a2ab 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -1,56 +1,64 @@ import request from '@/utils/request' import { getToken } from '@/utils/auth' +import { baseName } from './utils' -export async function fetchUsers(showLocalUsersOnly, token, page = 1) { +export async function fetchUsers(showLocalUsersOnly, authHost, token, page = 1) { return await request({ + baseURL: baseName(authHost), url: `/api/pleroma/admin/users?page=${page}&local_only=${showLocalUsersOnly}`, method: 'get', headers: authHeaders(token) }) } -export async function toggleUserActivation(nickname, token) { +export async function toggleUserActivation(nickname, authHost, token) { return await request({ + baseURL: baseName(authHost), url: `/api/pleroma/admin/users/${nickname}/toggle_activation`, method: 'patch', headers: authHeaders(token) }) } -export async function searchUsers(query, showLocalUsersOnly, token, page = 1) { +export async function searchUsers(query, showLocalUsersOnly, authHost, token, page = 1) { return await request({ + baseURL: baseName(authHost), url: `/api/pleroma/admin/users?query=${query}&page=${page}&local_only=${showLocalUsersOnly}`, method: 'get', headers: authHeaders(token) }) } -export async function addRight(nickname, right, token) { +export async function addRight(nickname, right, authHost, token) { return await request({ + baseURL: baseName(authHost), url: `/api/pleroma/admin/permission_group/${nickname}/${right}`, method: 'post', headers: authHeaders(token) }) } -export async function deleteRight(nickname, right, token) { +export async function deleteRight(nickname, right, authHost, token) { return await request({ + baseURL: baseName(authHost), url: `/api/pleroma/admin/permission_group/${nickname}/${right}`, method: 'delete', headers: authHeaders(token) }) } -export async function deleteUser(nickname, token) { +export async function deleteUser(nickname, authHost, token) { return await request({ + baseURL: baseName(authHost), url: `/api/pleroma/admin/user.json?nickname=${nickname}`, method: 'delete', headers: authHeaders(token) }) } -export async function tagUser(nickname, tag, token) { +export async function tagUser(nickname, tag, authHost, token) { return await request({ + baseURL: baseName(authHost), url: '/api/pleroma/admin/users/tag', method: 'put', headers: authHeaders(token), @@ -58,8 +66,9 @@ export async function tagUser(nickname, tag, token) { }) } -export async function untagUser(nickname, tag, token) { +export async function untagUser(nickname, tag, authHost, token) { return await request({ + baseURL: baseName(authHost), url: '/api/pleroma/admin/users/tag', method: 'delete', headers: authHeaders(token), diff --git a/src/api/utils.js b/src/api/utils.js new file mode 100644 index 00000000..4943f650 --- /dev/null +++ b/src/api/utils.js @@ -0,0 +1,5 @@ +const isLocalhost = (instanceName) => + instanceName.startsWith('localhost:') || instanceName.startsWith('127.0.0.1:') + +export const baseName = (instanceName) => + isLocalhost(instanceName) ? `http://${instanceName}` : `https://${instanceName}` diff --git a/src/lang/en.js b/src/lang/en.js index 655fb0ff..1c4097e7 100644 --- a/src/lang/en.js +++ b/src/lang/en.js @@ -76,8 +76,9 @@ export default { login: { title: 'Login Form', logIn: 'Log in', - username: 'Username', + username: 'Username@Host', password: 'Password', + errorMessage: 'Username must contain username and host, e.g. john@pleroma.social', any: 'any', thirdparty: 'Or connect with', thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !' diff --git a/src/permission.js b/src/permission.js index acd01b74..1439b1b9 100644 --- a/src/permission.js +++ b/src/permission.js @@ -16,7 +16,7 @@ function hasPermission(roles, permissionRoles) { const whiteList = ['/login', '/auth-redirect']// no redirect whitelist -router.beforeEach((to, from, next) => { +export const beforeEachRoute = (to, from, next) => { NProgress.start() // start progress bar if (getToken()) { // determine if there has token /* has token*/ @@ -24,12 +24,12 @@ router.beforeEach((to, from, next) => { next({ path: '/' }) NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it } else { - if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 - store.dispatch('GetUserInfo').then(res => { // 拉取user_info + if (store.getters.roles.length === 0) { + store.dispatch('GetUserInfo').then(res => { const roles = res.data.rights.admin ? ['admin'] : [] - store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表 - router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 - next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record + store.dispatch('GenerateRoutes', { roles }).then(() => { + router.addRoutes(store.getters.addRouters) + next({ ...to, replace: true }) }) }).catch((err) => { store.dispatch('FedLogOut').then(() => { @@ -38,25 +38,24 @@ router.beforeEach((to, from, next) => { }) }) } else { - // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ if (hasPermission(store.getters.roles, to.meta.roles)) { next() } else { next({ path: '/401', replace: true, query: { noGoBack: true }}) } - // 可删 ↑ } } } else { /* has no token*/ - if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 + if (whiteList.indexOf(to.path) !== -1) { next() } else { - next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 + next(`/login?redirect=${to.path}`) NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it } } -}) +} +router.beforeEach(beforeEachRoute) router.afterEach(() => { NProgress.done() // finish progress bar diff --git a/src/store/getters.js b/src/store/getters.js index b20f2a1c..cec3bce1 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -15,6 +15,7 @@ const getters = { permission_routers: state => state.permission.routers, addRouters: state => state.permission.addRouters, errorLogs: state => state.errorLog.logs, - users: state => state.users.fetchedUsers + users: state => state.users.fetchedUsers, + authHost: state => state.user.authHost } export default getters diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 1da451b6..8c282e71 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -1,5 +1,5 @@ import { loginByUsername, getUserInfo } from '@/api/login' -import { getToken, setToken, removeToken } from '@/utils/auth' +import { getToken, setToken, removeToken, getAuthHost, setAuthHost, removeAuthHost } from '@/utils/auth' const user = { state: { @@ -8,6 +8,7 @@ const user = { status: '', code: '', token: getToken(), + authHost: getAuthHost(), name: '', avatar: '', introduction: '', @@ -44,19 +45,24 @@ const user = { }, SET_ID: (state, id) => { state.id = id + }, + SET_AUTH_HOST: (state, authHost) => { + state.authHost = authHost } }, actions: { - LoginByUsername({ commit }, userInfo) { - const username = userInfo.username.trim() + LoginByUsername({ commit, dispatch }, { username, authHost, password }) { return new Promise((resolve, reject) => { - loginByUsername(username, userInfo.password).then(response => { + loginByUsername(username, password, authHost).then(response => { const data = response.data commit('SET_TOKEN', data.access_token) + commit('SET_AUTH_HOST', authHost) setToken(data.access_token) + setAuthHost(authHost) resolve() }).catch(error => { + dispatch('addErrorLog', { message: error.message }) reject(error) }) }) @@ -64,7 +70,7 @@ const user = { GetUserInfo({ commit, state }) { return new Promise((resolve, reject) => { - getUserInfo(state.token).then(response => { + getUserInfo(state.token, state.authHost).then(response => { const data = response.data if (!data) { @@ -91,11 +97,13 @@ const user = { commit('SET_TOKEN', '') commit('SET_ROLES', []) removeToken() + removeAuthHost() }, FedLogOut({ commit }) { return new Promise(resolve => { commit('SET_TOKEN', '') removeToken() + removeAuthHost() resolve() }) } diff --git a/src/store/modules/users.js b/src/store/modules/users.js index e3bf6e63..09854c59 100644 --- a/src/store/modules/users.js +++ b/src/store/modules/users.js @@ -43,14 +43,14 @@ const users = { }, actions: { async FetchUsers({ commit, state, getters }, { page }) { - const response = await fetchUsers(state.showLocalUsersOnly, getters.token, page) + const response = await fetchUsers(state.showLocalUsersOnly, getters.authHost, getters.token, page) commit('SET_LOADING', true) loadUsers(commit, page, response.data) }, async ToggleUserActivation({ commit, getters }, nickname) { - const response = await toggleUserActivation(nickname, getters.token) + const response = await toggleUserActivation(nickname, getters.authHost, getters.token) commit('SWAP_USER', response.data) }, @@ -62,7 +62,7 @@ const users = { commit('SET_LOADING', true) commit('SET_SEARCH_QUERY', query) - const response = await searchUsers(query, state.showLocalUsersOnly, getters.token, page) + const response = await searchUsers(query, state.showLocalUsersOnly, getters.authHost, getters.token, page) loadUsers(commit, page, response.data) } @@ -73,24 +73,24 @@ const users = { }, async ToggleRight({ commit, getters }, { user, right }) { user.roles[right] - ? await deleteRight(user.nickname, right, getters.token) - : await addRight(user.nickname, right, getters.token) + ? await deleteRight(user.nickname, right, getters.authHost, getters.token) + : await addRight(user.nickname, right, getters.authHost, getters.token) const updatedUser = { ...user, roles: { ...user.roles, [right]: !user.roles[right] }} commit('SWAP_USER', updatedUser) }, async DeleteUser({ commit, getters }, user) { - await deleteUser(user.nickname, getters.token) + await deleteUser(user.nickname, getters.authHost, getters.token) const updatedUser = { ...user, deactivated: true } commit('SWAP_USER', updatedUser) }, async ToggleTag({ commit, getters }, { user, tag }) { if (user.tags.includes(tag)) { - await untagUser(user.nickname, tag, getters.token) + await untagUser(user.nickname, tag, getters.authHost, getters.token) const updatedUser = { ...user, tags: user.tags.filter(userTag => userTag !== tag) } commit('SWAP_USER', updatedUser) } else { - await tagUser(user.nickname, tag, getters.token) + await tagUser(user.nickname, tag, getters.authHost, getters.token) const updatedUser = { ...user, tags: [...user.tags, tag] } commit('SWAP_USER', updatedUser) } diff --git a/src/utils/auth.js b/src/utils/auth.js index 08a43d6e..f8939a9a 100644 --- a/src/utils/auth.js +++ b/src/utils/auth.js @@ -1,6 +1,7 @@ import Cookies from 'js-cookie' const TokenKey = 'Admin-Token' +const AuthHostKey = 'Auth-Host' export function getToken() { return Cookies.get(TokenKey) @@ -13,3 +14,15 @@ export function setToken(token) { export function removeToken() { return Cookies.remove(TokenKey) } + +export function getAuthHost() { + return Cookies.get(AuthHostKey) +} + +export function setAuthHost(token) { + return Cookies.set(AuthHostKey, token) +} + +export function removeAuthHost() { + return Cookies.remove(AuthHostKey) +} diff --git a/src/utils/request.js b/src/utils/request.js index 0bf34b09..5fdfef80 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -3,7 +3,6 @@ import { Message } from 'element-ui' // create an axios instance const service = axios.create({ - baseURL: process.env.BASE_API, // api 的 base_url timeout: 5000 // request timeout }) diff --git a/src/views/i18n-demo/index.vue b/src/views/i18n-demo/index.vue deleted file mode 100644 index 08861bca..00000000 --- a/src/views/i18n-demo/index.vue +++ /dev/null @@ -1,143 +0,0 @@ - - - - - diff --git a/src/views/i18n-demo/local.js b/src/views/i18n-demo/local.js deleted file mode 100644 index 9b43e605..00000000 --- a/src/views/i18n-demo/local.js +++ /dev/null @@ -1,63 +0,0 @@ - -export default { - zh: { - i18nView: { - title: '切换语言', - note: '本项目国际化基于 vue-i18n', - datePlaceholder: '请选择日期', - selectPlaceholder: '请选择', - tableDate: '日期', - tableName: '姓名', - tableAddress: '地址', - default: '默认按钮', - primary: '主要按钮', - success: '成功按钮', - info: '信息按钮', - warning: '警告按钮', - danger: '危险按钮', - one: '一', - two: '二', - three: '三' - } - }, - en: { - i18nView: { - title: 'Switch Language', - note: 'The internationalization of this project is based on vue-i18n', - datePlaceholder: 'Pick a day', - selectPlaceholder: 'Select', - tableDate: 'tableDate', - tableName: 'tableName', - tableAddress: 'tableAddress', - default: 'default:', - primary: 'primary', - success: 'success', - info: 'info', - warning: 'warning', - danger: 'danger', - one: 'One', - two: 'Two', - three: 'Three' - } - }, - es: { - i18nView: { - title: 'Switch Language', - note: 'The internationalization of this project is based on vue-i18n', - datePlaceholder: 'Pick a day', - selectPlaceholder: 'Select', - tableDate: 'tableDate', - tableName: 'tableName', - tableAddress: 'tableAddress', - default: 'default:', - primary: 'primary', - success: 'success', - info: 'info', - warning: 'warning', - danger: 'danger', - one: 'One', - two: 'Two', - three: 'Three' - } - } -} diff --git a/src/views/login/index.vue b/src/views/login/index.vue index c2a5d202..5ff5b857 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -45,8 +45,13 @@