diff --git a/js/admin.js b/js/admin.js index dc61880..fe12a21 100644 --- a/js/admin.js +++ b/js/admin.js @@ -251,8 +251,8 @@ export const admin = (() => { comment.init(); try { - // window.atob can throw error - if (!session.isAdmin() || !session.getToken() || (JSON.parse(window.atob(session.getToken().split('.')[1]))?.exp ?? 0) < (Date.now() / 1000)) { + const jwt = session.decode(); + if (!jwt || (jwt.exp ?? 0) < (Date.now() / 1000)) { throw new Error('invalid token'); } diff --git a/js/guest.js b/js/guest.js index 45c9ef9..c8ce2f1 100644 --- a/js/guest.js +++ b/js/guest.js @@ -145,18 +145,23 @@ export const guest = (() => { return; } - comment.init(); progress.add(); + progress.add(); + comment.init(); + session.guest() .then((res) => { if (res.code !== 200) { + progress.invalid('request'); return res; } - return comment.comment() + progress.complete('request'); + comment.comment() .then(() => progress.complete('comment')) .catch(() => progress.invalid('comment')); - }); + }) + .catch(() => progress.invalid('request')); }; return { diff --git a/js/request.js b/js/request.js index 49e9e22..69de110 100644 --- a/js/request.js +++ b/js/request.js @@ -11,15 +11,17 @@ export const HTTP_DELETE = 'DELETE'; export const request = (method, path) => { const controller = new AbortController(); + const header = new Headers({ + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }); + offline.addAbort(() => controller.abort()); let url = document.body.getAttribute('data-url'); let req = { method: String(method).toUpperCase(), - headers: new Headers({ - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }), + headers: header, signal: controller.signal, }; @@ -60,6 +62,9 @@ export const request = (method, path) => { throw new Error(err); }); }, + /** + * @returns {Promise} + */ download() { return fetch(url + path, req) .then((res) => { @@ -101,6 +106,10 @@ export const request = (method, path) => { throw new Error(err); }); }, + /** + * @param {string} token + * @returns {this} + */ token(token) { if (session.isAdmin()) { req.headers.append('Authorization', 'Bearer ' + token); @@ -110,6 +119,10 @@ export const request = (method, path) => { req.headers.append('x-access-key', token); return this; }, + /** + * @param {object} body + * @returns {this} + */ body(body) { req.body = JSON.stringify(body); return this; diff --git a/js/session.js b/js/session.js index ca120bc..affcb50 100644 --- a/js/session.js +++ b/js/session.js @@ -1,59 +1,98 @@ import { dto } from './dto.js'; import { storage } from './storage.js'; -import { progress } from './progress.js'; import { request, HTTP_POST, HTTP_GET } from './request.js'; export const session = (() => { - let session = null; + /** + * @type {ReturnType} + */ + let ses = null; - const getToken = () => session.get('token'); + /** + * @returns {string} + */ + const getToken = () => ses.get('token'); + /** + * @param {object} body + * @returns {Promise} + */ const login = (body) => { return request(HTTP_POST, '/api/session') .body(body) .send(dto.tokenResponse) - .then((res) => { - if (res.code === 200) { - session.set('token', res.data.token); - } + .then( + (res) => { + if (res.code === 200) { + setToken(res.data.token); + } - return res; - }) - .then((res) => res.code === 200, () => false); + return res.code === 200; + }, + () => false + ); }; - const logout = () => session.unset('token'); + /** + * @returns {void} + */ + const logout = () => ses.unset('token'); - const isAdmin = () => String(getToken() ?? '.').split('.').length === 3; + /** + * @param {string} token + * @returns {void} + */ + const setToken = (token) => ses.set('token', token); + /** + * @returns {boolean} + */ + const isAdmin = () => getToken().split('.').length === 3; + + /** + * @returns {Promise>} + */ const guest = () => { - progress.add(); + const config = storage('config'); + return request(HTTP_GET, '/api/config') .token(document.body.getAttribute('data-key')) .send() .then((res) => { if (res.code !== 200) { - progress.invalid('request'); return res; } - const config = storage('config'); for (let [key, value] of Object.entries(res.data)) { config.set(key, value); } - session.set('token', document.body.getAttribute('data-key')); - progress.complete('request'); - + setToken(document.body.getAttribute('data-key')); return res; - }).catch(() => { - progress.invalid('request'); }); }; + /** + * @returns {object|null} + */ + const decode = () => { + if (!isAdmin()) { + return null; + } + + try { + return JSON.parse(window.atob(getToken().split('.')[1])); + } catch { + return null; + } + }; + + /** + * @returns {void} + */ const init = () => { - session = storage('session'); + ses = storage('session'); }; return { @@ -61,7 +100,9 @@ export const session = (() => { guest, login, logout, + decode, isAdmin, + setToken, getToken, }; })(); \ No newline at end of file diff --git a/js/storage.js b/js/storage.js index 553ca70..815059c 100644 --- a/js/storage.js +++ b/js/storage.js @@ -1,39 +1,59 @@ export const storage = (table) => { + /** + * @param {string=} key + * @returns {any} + */ const get = (key = null) => { const data = JSON.parse(localStorage.getItem(table)); return key ? data[String(key)] : data; }; + /** + * @param {string} key + * @param {any} value + * @returns {void} + */ const set = (key, value) => { - let storage = get(); - storage[String(key)] = value; - localStorage.setItem(table, JSON.stringify(storage)); + let data = get(); + data[String(key)] = value; + localStorage.setItem(table, JSON.stringify(data)); }; + /** + * @param {string} key + * @returns {boolean} + */ const has = (key) => Object.keys(get()).includes(String(key)); + /** + * @param {string} key + * @returns {void} + */ const unset = (key) => { if (!has(key)) { return; } - let storage = get(); - delete storage[String(key)]; - localStorage.setItem(table, JSON.stringify(storage)); + let data = get(); + delete data[String(key)]; + localStorage.setItem(table, JSON.stringify(data)); }; - const clear = () => localStorage.setItem(table, JSON.stringify({})); + /** + * @returns {void} + */ + const clear = () => localStorage.setItem(table, '{}'); if (!localStorage.getItem(table)) { clear(); } return { - get, set, - unset, + get, has, clear, + unset, }; }; \ No newline at end of file