<commit_before>/**
 * Created by zhangjinjie on 2017/5/17.
 */
import store from './store';
import * as types from './store/types';
import consts from './consts';

export default class Auth {
  constructor(Vue, options = {}) {
    // check Axios, Vue-router, Vuex used
    if (!Vue.$http && !Vue.prototype.$http) {
      console.error('Axios must be set first');
      return;
    }

    if (!Vue.router) {
      console.error('Vue router 2.x must be set first');
      return;
    }

    if (!Vue.store) {
      console.error('Vuex must be used first');
      return;
    }

    // set variables;
    this._Vue = Vue;
    const instance = (Vue.$http || Vue.prototype.$http).create({
      headers: {
        'content-type': options.contentType || 'application/json;charset=utf-8',
      },
    });

    this._http = instance;
    this._store = Vue.store;
    this._router = Vue.router;

    // fetch用户信息后，变为true，开始刷新；退出时，置为false；
    this._loaded = false;

    // options
    const defaultOptions = {
      appKey: '',
      authType: 'Bearer',
      hamletPrefix: '/api/auth',
      authRedirect: '/login',

      // mins
      refreshInterval: 10,
      forbiddenRedirect: '/403',
      notFoundRedirect: '/404',
      allowThirdpartyLogin: false,
    };
    this.options = Object.assign({}, defaultOptions, options);
    this.options.fetchUser = this.options.fetchUser || this.url('me');

    // add consts
    this.consts = consts;

    // register store
    Vue.store.registerModule('auth', store);

    // app key
    if (!this.options.appKey) {
      console.error('must set app key first');
      return;
    }

    // 必须在register store后面执行
    this._store.commit(types.SET_APPKEY, options.appKey);

    // register http interceptors
    this._http.interceptors.request.use((request) => {
      // console.log('request', request);
      // 判断是否是hamlet请求，如果是添加app_key
      if (request.url.indexOf(this.options.hamletPrefix) !== -1) {
        const appKey = this._store.state.auth.appKey;
        const method = request.method && request.method.toUpperCase();
        if (['POST', 'PUT'].indexOf(method) !== -1) {
          request.data = Object.assign(
            request.data || {},
            { app_key: appKey },
          );
        } else if (['GET'].indexOf(method) !== -1) {
          request.params = Object.assign(
            request.params || {},
            { app_key: appKey },
          );
        }
      }

      // 判断是否包含token，如果有，则加到header里面
      const token = this._store.state.auth.token;

      if (token) {
        request.headers.Authorization = `${this.options.authType} ${token}`;
        // this._http.defaults.headers.common['Authorization'] = `${this.options.authType} ${token}`;
      }

      return request;
    });

    this._http.interceptors.response.use((res) => {
      // console.log('response', res);
      // 当发现返回码为401时，跳转到登陆页面
      if (res.status === 401 && (!res.config || !res.config.headers || !res.config.headers._noauth)) {
        console.debug('unauthorized, jump to login page');
        this._router.push(this.options.authRedirect);
      }

      return Promise.resolve(res);
    }, (error) => {
      console.error('error', error);
      return Promise.reject(error);
    });

    // 注册路由，实现当跳转到需要验证的url时，自动检查认证状态，如果失败，跳转到登录页面
    Vue.router.beforeEach((to, from, next) => {
      // not matched route redirect to notFound route
      console.log('>>>> to: ', to);
      if (to.matched.length === 0) {
        next(this.options.notFoundRedirect);
      }

      let auth = false;
      const query = to.query;
      const authRoutes = to.matched.filter(route => 'auth' in route.meta);

      if (authRoutes.length) {
        auth = authRoutes[authRoutes.length - 1].meta.auth;
      }

      // 当用户通过第三方登录时,拿到URL中的token和access_token, 存入localstorage, 重定向至去除token信息的URL
      if (this.options.allowThirdpartyLogin &&
        query.thirdparty_connect_access_token &&
        query.thirdparty_connect_refresh_token) {
        this._store.commit(types.SET_TOKEN, query.thirdparty_connect_access_token);
        this._store.commit(types.SET_REFRESH_TOKEN, query.thirdparty_connect_refresh_token);
        next({ path: to.path });
      }

      // 当用户绑定第三方账号时, 重定向至去除绑定成功信息的URL
      if (this.options.allowThirdpartyLogin && query.thirdparty_connect_ok) {
        next({ path: to.path });
      }

      const token = this._store.state.auth.token;

      // 需要认证时，检查权限是否满足
      if (auth) {
        // 未获得token
        if (!token) {
          // 当路由重定向到登陆页面，附带重定向的页面值
          let fullPath = to.path;
          if (query && fullPath) {
            const keys = Object.keys(query);
            for (let i = 0, len = keys.length; i < len; i += 1) {
              fullPath = `${fullPath}${i === 0 ? '?' : '&'}${keys[i]}=${query[keys[i]]}`;
            }
          }
          next({ path: this.options.authRedirect, query: { redirectedFrom: to.redirectedFrom || fullPath } });
        } else {
          this.fetch().then(() => {
            const user = this.user();
            if (typeof auth === 'object' && auth.constructor === Array) {
              if ((!user.role && !auth.length) || auth.indexOf(user.role) !== -1) {
                next();
              } else {
                next(this.options.forbiddenRedirect);
              }
            } else if (auth === user.role || auth === true) {
              next();
            } else {
              next(this.options.authRedirect);
            }
          }, () => {
            // 获取用户信息失败，跳转到登录页面
            next(this.options.authRedirect);
          });
        }
      } else if (token) {
        // 不需要认证时，如果存在token，则更新一次用户信息
        this.fetch().then(() => {
          // token有效，跳转
          next();
        }).catch(() => {
          // token失效，清除token
          this._store.commit(types.CLEAR_TOKENS);
          next();
        });
      } else {
        next();
      }
    });

    // refresh
    setInterval(() => {
      if (this._loaded) {
        this.refresh();
      }
    }, this.options.refreshInterval * 60 * 1000);
  }

  url(relative) {
    let prefix = this.options.hamletPrefix;

    if (prefix.charAt(prefix.length - 1) === '/') {
      prefix = prefix.slice(0, -1);
    }

    if (relative.charAt(0) === '/') {
      relative = relative.slice(1);
    }<commit_msg>mod: merge miniprogram auth logic<commit_after>/**
 * Created by zhangjinjie on 2017/5/17.
 */
import store from './store';
import * as types from './store/types';
import consts from './consts';

export default class Auth {
  constructor(Vue, options = {}) {
    // check Axios, Vue-router, Vuex used
    if (!Vue.$http && !Vue.prototype.$http) {
      console.error('Axios must be set first');
      return;
    }

    if (!Vue.router) {
      console.error('Vue router 2.x must be set first');
      return;
    }

    if (!Vue.store) {
      console.error('Vuex must be used first');
      return;
    }

    // set variables;
    this._Vue = Vue;
    const instance = (Vue.$http || Vue.prototype.$http).create({
      headers: {
        'content-type': options.contentType || 'application/json;charset=utf-8',
      },
    });

    this._http = instance;
    this._store = Vue.store;
    this._router = Vue.router;

    // fetch用户信息后，变为true，开始刷新；退出时，置为false；
    this._loaded = false;

    // 下面用到此变量的地方均是对小程序和移动端的特殊处理
    this.isMobileOrMiniprogram = !!navigator.userAgent.match(/mobile|miniProgram|android/i);

    // options
    const defaultOptions = {
      appKey: '',
      authType: 'Bearer',
      hamletPrefix: '/api/auth',
      authRedirect: '/login',

      // mins
      refreshInterval: 10,
      forbiddenRedirect: '/403',
      notFoundRedirect: '/404',
      allowThirdpartyLogin: false,
    };
    this.options = Object.assign({}, defaultOptions, options);
    this.options.fetchUser = this.options.fetchUser || this.url('me');

    // add consts
    this.consts = consts;

    // register store
    Vue.store.registerModule('auth', store);

    // app key
    if (!this.options.appKey) {
      console.error('must set app key first');
      return;
    }

    // 必须在register store后面执行
    this._store.commit(types.SET_APPKEY, options.appKey);

    // register http interceptors
    this._http.interceptors.request.use((request) => {
      // console.log('request', request);
      // 判断是否是hamlet请求，如果是添加app_key
      if (request.url.indexOf(this.options.hamletPrefix) !== -1) {
        const appKey = this._store.state.auth.appKey;
        const method = request.method && request.method.toUpperCase();
        if (['POST', 'PUT'].indexOf(method) !== -1) {
          request.data = Object.assign(
            request.data || {},
            { app_key: appKey },
          );
        } else if (['GET'].indexOf(method) !== -1) {
          request.params = Object.assign(
            request.params || {},
            { app_key: appKey },
          );
        }
      }

      // 判断是否包含token，如果有，则加到header里面
      const token = this._store.state.auth.token;

      if (token) {
        request.headers.Authorization = `${this.options.authType} ${token}`;
        // this._http.defaults.headers.common['Authorization'] = `${this.options.authType} ${token}`;
      }

      return request;
    });

    this._http.interceptors.response.use((res) => {
      // console.log('response', res);
      // 当发现返回码为401时，跳转到登陆页面
      if (res.status === 401 && (!res.config || !res.config.headers || !res.config.headers._noauth)) {
        console.debug('unauthorized, jump to login page');
        this._router.push(this.options.authRedirect);
      }

      return Promise.resolve(res);
    }, (error) => {
      console.error('error', error);
      return Promise.reject(error);
    });

    // 注册路由，实现当跳转到需要验证的url时，自动检查认证状态，如果失败，跳转到登录页面
    Vue.router.beforeEach((to, from, next) => {
      const query = to.query;
      if (query && query.tk) {
        const token = query.tk;
        localStorage.setItem('token', token);
        this._store.commit(types.SET_TOKEN, token);
      }

      if (query && query.rtk) {
        const refreshToken = query.rtk;
        localStorage.setItem('refresh_token', refreshToken);
        this._store.commit(types.SET_REFRESH_TOKEN, refreshToken);
      }

      // not matched route redirect to notFound route
      console.log('>>>> to: ', to);
      if (to.matched.length === 0) {
        next(this.options.notFoundRedirect);
      }

      let auth = false;
      const authRoutes = to.matched.filter(route => 'auth' in route.meta);
      if (authRoutes.length) {
        auth = authRoutes[authRoutes.length - 1].meta.auth;
      }

      // 当用户通过第三方登录时,拿到URL中的token和access_token, 存入localstorage, 重定向至去除token信息的URL
      if (this.options.allowThirdpartyLogin &&
        query.thirdparty_connect_access_token &&
        query.thirdparty_connect_refresh_token) {
        this._store.commit(types.SET_TOKEN, query.thirdparty_connect_access_token);
        this._store.commit(types.SET_REFRESH_TOKEN, query.thirdparty_connect_refresh_token);
        next({ path: to.path });
      }

      // 当用户绑定第三方账号时, 重定向至去除绑定成功信息的URL
      if (this.options.allowThirdpartyLogin && query.thirdparty_connect_ok) {
        next({ path: to.path });
      }

      const token = this._store.state.auth.token;

      // 需要认证时，检查权限是否满足
      if (auth) {
        // 未获得token
        if (!token) {
          // 当路由重定向到登陆页面，附带重定向的页面值
          let fullPath = to.path;
          if (query && fullPath) {
            const keys = Object.keys(query);
            for (let i = 0, len = keys.length; i < len; i += 1) {
              fullPath = `${fullPath}${i === 0 ? '?' : '&'}${keys[i]}=${query[keys[i]]}`;
            }
          }
          next({ path: this.options.authRedirect, query: { redirectedFrom: to.redirectedFrom || fullPath } });
        } else {
          this.fetch().then(() => {
            const user = this.user();
            if (typeof auth === 'object' && auth.constructor === Array) {
              if ((!user.role && !auth.length) || auth.indexOf(user.role) !== -1) {
                next();
              } else {
                next(this.options.forbiddenRedirect);
              }
            } else if (this.isMobileOrMiniprogram || auth === user.role || auth === true) {
              next();
            } else {
              next(this.options.authRedirect);
            }
          }, () => {
            // 获取用户信息失败，跳转到登录页面
            if (this.isMobileOrMiniprogram) {
              next();
            } else {
              next(this.options.authRedirect);
            }
          });
        }
      } else if (token) {
        // 不需要认证时，如果存在token，则更新一次用户信息
        this.fetch().then(() => {
          // token有效，跳转
          next();
        }).catch(() => {
          // token失效，清除token
          // hack: 小程序、移动端这里不需要清空 token
          if (!this.isMobileOrMiniprogram) {
            this._store.commit(types.CLEAR_TOKENS);
          }
          next();
        });
      } else {
        next();
      }
    });

    // refresh
    setInterval(() => {
      if (this._loaded) {
        this.refresh();
      }
    }, this.options.refreshInterval * 60 * 1000);
  }

  url(relative) {
    let prefix = this.options.hamletPrefix;

    if (prefix.charAt(prefix.length - 1) === '/') {
      prefix = prefix.slice(0, -1);
    }

    if (relative.charAt(0) === '/') {
      relative = relative.slice(1);
    }
{'fraction_non_alphanumeric': 0.1528286376205067, 'fraction_numerical': 0.0064270233913758315, 'mean_word_length': 2.0194043321299637, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '559131', 'n_tokens_mistral': 4518, 'n_tokens_neox': 4549, 'n_words': 757}