mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2025-02-18 17:39:46 +03:00
notification mark read
This commit is contained in:
parent
102ef5b4a1
commit
6f690734c5
14 changed files with 89 additions and 41 deletions
|
@ -177,6 +177,7 @@ impl Data {
|
||||||
mailer: Self::get_mailer(),
|
mailer: Self::get_mailer(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
init.join().unwrap();
|
init.join().unwrap();
|
||||||
|
|
||||||
Arc::new(data)
|
Arc::new(data)
|
||||||
|
|
|
@ -17,48 +17,28 @@
|
||||||
|
|
||||||
const ROUTES = {
|
const ROUTES = {
|
||||||
registerUser: '/api/v1/signup',
|
registerUser: '/api/v1/signup',
|
||||||
|
|
||||||
loginUser: '/api/v1/signin',
|
loginUser: '/api/v1/signin',
|
||||||
|
|
||||||
signoutUser: '/api/v1/signout',
|
signoutUser: '/api/v1/signout',
|
||||||
|
|
||||||
deleteAccount: '/api/v1/account/delete',
|
deleteAccount: '/api/v1/account/delete',
|
||||||
|
|
||||||
usernameExists: '/api/v1/account/username/exists',
|
usernameExists: '/api/v1/account/username/exists',
|
||||||
|
|
||||||
emailExists: '/api/v1/account/email/exists',
|
emailExists: '/api/v1/account/email/exists',
|
||||||
|
|
||||||
healthCheck: '/api/v1/meta/health',
|
healthCheck: '/api/v1/meta/health',
|
||||||
|
|
||||||
buildDetails: '/api/v1/meta/build',
|
buildDetails: '/api/v1/meta/build',
|
||||||
|
|
||||||
addDomain: '/api/v1/mcaptcha/domain/add',
|
addDomain: '/api/v1/mcaptcha/domain/add',
|
||||||
|
|
||||||
challengeDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/get',
|
challengeDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/get',
|
||||||
|
|
||||||
proveDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/prove',
|
proveDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/prove',
|
||||||
|
|
||||||
deleteDomain: '/api/v1/mcaptcha/domain/delete',
|
deleteDomain: '/api/v1/mcaptcha/domain/delete',
|
||||||
|
|
||||||
addToken: '/api/v1/mcaptcha/domain/token/add',
|
addToken: '/api/v1/mcaptcha/domain/token/add',
|
||||||
|
|
||||||
updateTokenKey: '/api/v1/mcaptcha/domain/token/update',
|
updateTokenKey: '/api/v1/mcaptcha/domain/token/update',
|
||||||
|
|
||||||
getTokenKey: '/api/v1/mcaptcha/domain/token/get',
|
getTokenKey: '/api/v1/mcaptcha/domain/token/get',
|
||||||
|
|
||||||
deleteToken: '/api/v1/mcaptcha/domain/token/delete',
|
deleteToken: '/api/v1/mcaptcha/domain/token/delete',
|
||||||
|
|
||||||
addTokenLevels: '/api/v1/mcaptcha/domain/token/levels/add',
|
addTokenLevels: '/api/v1/mcaptcha/domain/token/levels/add',
|
||||||
|
|
||||||
updateTokenLevels: '/api/v1/mcaptcha/domain/token/levels/update',
|
updateTokenLevels: '/api/v1/mcaptcha/domain/token/levels/update',
|
||||||
|
|
||||||
deleteTokenLevels: '/api/v1/mcaptcha/domain/token/levels/delete',
|
deleteTokenLevels: '/api/v1/mcaptcha/domain/token/levels/delete',
|
||||||
|
|
||||||
getTokenLevels: '/api/v1/mcaptcha/domain/token/levels/get',
|
getTokenLevels: '/api/v1/mcaptcha/domain/token/levels/get',
|
||||||
|
|
||||||
getTokenDuration: '/api/v1/mcaptcha/domain/token/token/get',
|
getTokenDuration: '/api/v1/mcaptcha/domain/token/token/get',
|
||||||
|
|
||||||
updateTokenDuration: '/api/v1/mcaptcha/domain/token/token/update',
|
updateTokenDuration: '/api/v1/mcaptcha/domain/token/token/update',
|
||||||
|
markNotificationRead: '/api/v1/notifications/read',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ROUTES;
|
export default ROUTES;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import * as panel from './panel/ts/index';
|
||||||
import * as addSiteKey from './panel/sitekey/add/ts';
|
import * as addSiteKey from './panel/sitekey/add/ts';
|
||||||
import * as editSitekey from './panel/sitekey/edit/';
|
import * as editSitekey from './panel/sitekey/edit/';
|
||||||
import * as listSitekeys from './panel/sitekey/list/ts';
|
import * as listSitekeys from './panel/sitekey/list/ts';
|
||||||
|
import * as notidications from './panel/notifications/ts';
|
||||||
import {MODE} from './logger';
|
import {MODE} from './logger';
|
||||||
import log from './logger';
|
import log from './logger';
|
||||||
|
|
||||||
|
@ -50,9 +51,10 @@ const router = new Router();
|
||||||
router.register(VIEWS.panelHome, panel.index);
|
router.register(VIEWS.panelHome, panel.index);
|
||||||
router.register(VIEWS.registerUser, register.index);
|
router.register(VIEWS.registerUser, register.index);
|
||||||
router.register(VIEWS.loginUser, login.index);
|
router.register(VIEWS.loginUser, login.index);
|
||||||
|
router.register(VIEWS.notifications, notidications.index);
|
||||||
router.register(VIEWS.listSitekey, listSitekeys.index);
|
router.register(VIEWS.listSitekey, listSitekeys.index);
|
||||||
router.register(VIEWS.addSiteKey, addSiteKey.index);
|
router.register(VIEWS.addSiteKey, addSiteKey.index);
|
||||||
router.register(VIEWS.editSitekey("[A-Z,a-z,0-9]+"), editSitekey.index);
|
router.register(VIEWS.editSitekey('[A-Z),a-z,0-9]+'), editSitekey.index);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
router.route();
|
router.route();
|
||||||
|
|
|
@ -53,6 +53,7 @@ include!("./navbar/index.html"); .>
|
||||||
</td>
|
</td>
|
||||||
<td class="sitekey-list__key">
|
<td class="sitekey-list__key">
|
||||||
<div class="sitekey-list__edit">
|
<div class="sitekey-list__edit">
|
||||||
|
<. let key = format!("/sitekey/{}", &sitekey.key); .>
|
||||||
<. include!("./sitekey/view/__edit-sitekey-icon.html"); .>
|
<. include!("./sitekey/view/__edit-sitekey-icon.html"); .>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -14,7 +14,7 @@ include!("../navbar/index.html"); .>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="notification__body">
|
<tbody class="notification__body">
|
||||||
<. for notification in n.iter() { .>
|
<. for notification in n.iter() { .>
|
||||||
<tr class="notification__item">
|
<tr class="notification__item" id="notification__item-<.= notification.id .>">
|
||||||
<td>
|
<td>
|
||||||
<h3 class="notification__item-heading">
|
<h3 class="notification__item-heading">
|
||||||
<.= notification.heading .>
|
<.= notification.heading .>
|
||||||
|
@ -34,6 +34,7 @@ include!("../navbar/index.html"); .>
|
||||||
src="<.= crate::FILES
|
src="<.= crate::FILES
|
||||||
.get("./static/cache/img/svg/check.svg")
|
.get("./static/cache/img/svg/check.svg")
|
||||||
.unwrap() .>"
|
.unwrap() .>"
|
||||||
|
data-id="<.= notification.id .>"
|
||||||
alt="Mark Read"
|
alt="Mark Read"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification__mark-read-btn:hover {
|
.notification__mark-read-btn:hover {
|
||||||
cursor: grab;
|
cursor: pointer;
|
||||||
background-color: $light-grey;
|
background-color: $light-grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
55
templates/panel/notifications/ts/index.ts
Normal file
55
templates/panel/notifications/ts/index.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import genJsonPayload from '../../../utils/genJsonPayload';
|
||||||
|
import createError from '../../../components/error';
|
||||||
|
|
||||||
|
import ROUTES from '../../../api/v1/routes';
|
||||||
|
|
||||||
|
const BTN = document.querySelectorAll('.notification__mark-read-btn');
|
||||||
|
const TABLE_BODY = document.querySelector('.notification__body');
|
||||||
|
|
||||||
|
const notification_record = (id: number) =>
|
||||||
|
<HTMLElement>TABLE_BODY.querySelector(`#notification__item-${id}`);
|
||||||
|
|
||||||
|
const markRead = async (e: Event) => {
|
||||||
|
const element = <HTMLElement>e.target;
|
||||||
|
|
||||||
|
const id = Number.parseInt(element.dataset.id);
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await fetch(ROUTES.markNotificationRead, genJsonPayload(payload));
|
||||||
|
if (res.ok) {
|
||||||
|
notification_record(id).remove();
|
||||||
|
} else {
|
||||||
|
const err = await res.json();
|
||||||
|
createError(err.error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addMarkReadEventListenet = () => {
|
||||||
|
BTN.forEach(btn => {
|
||||||
|
btn.addEventListener('click', markRead, true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const index = () => {
|
||||||
|
addMarkReadEventListenet();
|
||||||
|
};
|
|
@ -30,8 +30,7 @@ import createError from '../../../components/error';
|
||||||
|
|
||||||
import VIEWS from '../../../views/v1/routes';
|
import VIEWS from '../../../views/v1/routes';
|
||||||
|
|
||||||
const BTN = <HTMLElement>document.querySelector('.sitekey-form__submit');
|
const BTN_CLASS = document.querySelector('sitekey-form__submit');
|
||||||
const key = BTN.dataset.sitekey;
|
|
||||||
|
|
||||||
const submit = async (e: Event) => {
|
const submit = async (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -44,6 +43,9 @@ const submit = async (e: Event) => {
|
||||||
const levels = LEVELS.getLevels();
|
const levels = LEVELS.getLevels();
|
||||||
console.debug(`[form submition]: levels: ${levels}`);
|
console.debug(`[form submition]: levels: ${levels}`);
|
||||||
|
|
||||||
|
const btn = <HTMLElement>document.querySelector(`${BTN_CLASS}`);
|
||||||
|
const key = btn.dataset.sitekey;
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
levels,
|
levels,
|
||||||
duration,
|
duration,
|
||||||
|
|
|
@ -44,6 +44,7 @@ include!("../../navbar/index.html"); .>
|
||||||
</td>
|
</td>
|
||||||
<td class="sitekey-list__key">
|
<td class="sitekey-list__key">
|
||||||
<div class="sitekey-list__edit">
|
<div class="sitekey-list__edit">
|
||||||
|
<. let key = format!("/sitekey/{}", &sitekey.key); .>
|
||||||
<. include!("../view/__edit-sitekey-icon.html"); .>
|
<. include!("../view/__edit-sitekey-icon.html"); .>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a href="./edit/">
|
<a href="<.= key .>/edit/">
|
||||||
<img class="sitekey-form__edit" src="<.=
|
<img class="sitekey-form__edit" src="<.=
|
||||||
crate::FILES.get("./static/cache/img/svg/edit.svg").unwrap() .>" alt="Edit
|
crate::FILES.get("./static/cache/img/svg/edit.svg").unwrap() .>" alt="Edit
|
||||||
sitekey" />
|
sitekey" />
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<. if READONLY { .>
|
<. if READONLY { .>
|
||||||
|
<. let key = "."; .>
|
||||||
<. include!("./__edit-sitekey-icon.html"); .>
|
<. include!("./__edit-sitekey-icon.html"); .>
|
||||||
<. } .>
|
<. } .>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
|
@ -27,7 +27,7 @@ const panelResult = 'hello from panel';
|
||||||
const panelRoute = '/panel';
|
const panelRoute = '/panel';
|
||||||
const panel = () => (result.result = panelResult);
|
const panel = () => (result.result = panelResult);
|
||||||
|
|
||||||
const settingsRoute = '/settings/';
|
const settingsRoute = '/sitekey/';
|
||||||
const settingsResult = 'hello from settings';
|
const settingsResult = 'hello from settings';
|
||||||
const settings = () => (result.result = settingsResult);
|
const settings = () => (result.result = settingsResult);
|
||||||
|
|
||||||
|
@ -41,9 +41,9 @@ const emptyUriErr = 'uri is empty';
|
||||||
const unregisteredRouteErr = "Route isn't registered";
|
const unregisteredRouteErr = "Route isn't registered";
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
router.register(patternRoute, pattern);
|
||||||
router.register(panelRoute, panel);
|
router.register(panelRoute, panel);
|
||||||
router.register(settingsRoute, settings);
|
router.register(settingsRoute, settings);
|
||||||
router.register(patternRoute, pattern);
|
|
||||||
|
|
||||||
it('checks if Router works', () => {
|
it('checks if Router works', () => {
|
||||||
window.history.pushState({}, '', examplePatternRoute);
|
window.history.pushState({}, '', examplePatternRoute);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Removes trailing slashed from URI */
|
/** Removes trailing slash from URI */
|
||||||
const normalizeUri = (uri: string) => {
|
const normalizeUri = (uri: string) => {
|
||||||
uri = uri.trim();
|
uri = uri.trim();
|
||||||
if (uri.length == 0) {
|
if (uri.length == 0) {
|
||||||
|
@ -54,7 +54,7 @@ export class Router {
|
||||||
register(uri: string, fn: () => void) {
|
register(uri: string, fn: () => void) {
|
||||||
uri = normalizeUri(uri);
|
uri = normalizeUri(uri);
|
||||||
|
|
||||||
let pattern = new RegExp(`^${uri}(.*)`);
|
let pattern = new RegExp(`^${uri}$`);
|
||||||
|
|
||||||
let patterString = pattern.toString();
|
let patterString = pattern.toString();
|
||||||
if (
|
if (
|
||||||
|
@ -83,18 +83,21 @@ export class Router {
|
||||||
route() {
|
route() {
|
||||||
const path = normalizeUri(window.location.pathname);
|
const path = normalizeUri(window.location.pathname);
|
||||||
|
|
||||||
let fn: () => void | undefined;
|
let fn: undefined | (() => void);
|
||||||
|
|
||||||
this.routes.forEach(route => {
|
if (
|
||||||
if (path.match(route.pattern)) {
|
this.routes.find(route => {
|
||||||
fn = route.fn;
|
if (path.match(route.pattern)) {
|
||||||
|
fn = route.fn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
if (fn === undefined) {
|
||||||
|
throw new Error("Route isn't registered");
|
||||||
|
} else {
|
||||||
|
return fn();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (fn === undefined) {
|
|
||||||
throw new Error("Route isn't registered");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ const ROUTES = {
|
||||||
signoutUser: '/api/v1/signout',
|
signoutUser: '/api/v1/signout',
|
||||||
panelHome: '/',
|
panelHome: '/',
|
||||||
docsHome: '/docs/',
|
docsHome: '/docs/',
|
||||||
|
notifications: '/notifications',
|
||||||
listSitekey: '/sitekeys/',
|
listSitekey: '/sitekeys/',
|
||||||
viewSitekey: (key: string) => `/sitekey/${key}/`,
|
viewSitekey: (key: string) => `/sitekey/${key}/`,
|
||||||
editSitekey: (key: string) => `/sitekey/${key}/edit/`,
|
editSitekey: (key: string) => `/sitekey/${key}/edit/`,
|
||||||
|
|
Loading…
Add table
Reference in a new issue