|
- // authentication model
- const _make_auth = api_root => {
-
- // members
- let _login_name = '', _password = ''
- let _token_payload = {}
-
- // methods
- let _token = value => {
- if (value) localStorage.setItem('token', value)
- return localStorage.getItem('token')
- }
-
- let _load_token_payload = _ => {
- try {
- _token_payload = JSON.parse(atob(_token().split('.')[1]))
- } catch (e) {
- console.log('failed parsing token: ', e)
- }
- }
-
- let _login = _ => m.request({
- method: 'POST',
- url: `${api_root}/rpc/login`,
- body: {
- login_name: _login_name,
- password: _password
- }}).then(data => {
- _token(data)
- _load_token_payload()
-
- // TODO set to origin page
- m.route.set('/')
- }).catch(e => alert(e.response.message))
-
- // initialize
- _load_token_payload()
-
- // public methods
- return {
- login: _login,
- signed_in: _ => _token() ? true : false,
- payload: _ => _token_payload,
- token: _token,
- login_name: value => {
- if (value) _login_name = value
- return _login_name
- },
- password: value => {
- if (value) _password = value
- return _password
- }
- }
- }
-
- // main api
- const _make_api = (api_root, need_auth=true) => {
- // private state
- let _root = api_root
-
- // if auth is required
- let _auth = need_auth ? _make_auth(api_root) : null
-
- return {
- // utilities
- download: download,
-
- // expose authentication module
- auth: _auth,
- }
- }
-
- /* api wrapper */
- /*
- * api.request(options): all options will be forwarded to m.request, except a
- * few:
- *
- * 1. options.queries: an array of object '{label: <>, value: <>, op: <>}' to
- * build postgrest query easily
- */
-
- export default (api_root, auth={}) => {
- return {
- // wrap m.request with authentication validation if demanded
- request: (options = {}) => {
- /* normalize arguments */
- if (typeof options === 'string') {
- options = {url: options}
- }
-
- /* normalize url */
- if (options.url.startsWith('/'))
- options.url = api_root + options.url
-
- /* normalize headers */
- // 1. check if auth is required
- if (false/*need_auth*/) {
- // insert auth header if availible
- if (_auth.token()) {
- options.headers = {
- Authorization: 'Bearer ' + _auth.token(),
- ...options.headers
- }
- }
- // route to login
- else {
- m.route.set('/login')
- }
- }
-
- // 2. return values on Prefer
- // if (['POST', 'PATCH'].includes(options.method) && options.headers && !options.headers.Prefer)
- if (['POST', 'PATCH'].includes(options.method))
- options.config = xhr => {
- xhr.setRequestHeader('Prefer', 'return=representation')
- return xhr
- }
-
- // normalize params: since options.params is an object, it cannot support
- // duplicated query keys, while postgrest supports that.
- //
- // Simply build query string by hand.
- if (options.queries) {
- // build the query string
- let _query_string = options.queries
- .filter(query => typeof query != 'undefined')
- .map(query => {
- // prepare the query kv pair and optional operator
- let k = query.label
- let v = query.value
- let op = query.op
-
- // return early if v is empty
- if (typeof v == 'undefined' || (!v && v !== 0))
- return undefined
-
- // generate value if v is a function
- if (typeof v == 'function')
- v = v()
-
- // format v on k & op
- if (op == 'like')
- v = `*${v}*`
- else if (op == 'like.right') {
- v = `${v}*`
- op = 'like'
- } else if (op == 'in')
- v = `(${v.join(',')})`
- else if (k == 'and') {
- v = '(' + v.filter(sub_query => sub_query.value == 0 || sub_query.value).map(sub_query => `${sub_query.label}.${sub_query.op}.${typeof sub_query.value == 'function' ? sub_query.value() : sub_query.value}`).join(',') + ')'
- }
-
- return {k: k, v: op ? `${op}.${v}` : v}
- })
- .map(query => query.k + '=' + encodeURIComponent(query.v)).join('&')
-
- // append query string to url if necessary
- if (_query_string)
- options.url = options.url + '?' + _query_string
-
- delete options.queries
- }
-
- return m.request(options).catch(e => {
- if (e.code === 401) {
- localStorage.removeItem('token')
- m.route.set('/login')
- }
-
- throw e
- })
- }
- }
- }
|