Account editing and change password.
This commit is contained in:
1 parent
b5cf9f8719
commit
b392a51676
23 files changed
+230
-102
No files matched your search
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M435.848 83.466L172.804 346.51l-96.652-96.652c-4.686-4.686-12.284-4.686-16.971 0l-28.284 28.284c-4.686 4.686-4.686 12.284 0 16.971l133.421 133.421c4.686 4.686 12.284 4.686 16.971 0l299.813-299.813c4.686-4.686 4.686-12.284 0-16.971l-28.284-28.284c-4.686-4.686-12.284-4.686-16.97 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 360 B |
@@ -1,16 +1,19 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import PageHeader from './layout/PageHeader';
|
||||
import Button from './common/Button';
|
||||
import Table from './common/Table';
|
||||
import PageHeader from 'components/layout/PageHeader';
|
||||
import Button from 'components/common/Button';
|
||||
import Icon from 'components/common/Icon';
|
||||
import Table from 'components/common/Table';
|
||||
import Modal from 'components/common/Modal';
|
||||
import WebsiteEditForm from 'components/forms/WebsiteEditForm';
|
||||
import AccountEditForm from 'components/forms/AccountEditForm';
|
||||
import Pen from 'assets/pen.svg';
|
||||
import Plus from 'assets/plus.svg';
|
||||
import Trash from 'assets/trash.svg';
|
||||
import Check from 'assets/check.svg';
|
||||
import { get } from 'lib/web';
|
||||
import styles from './AccountSettings.module.css';
|
||||
import Modal from './common/Modal';
|
||||
import WebsiteEditForm from './forms/WebsiteEditForm';
|
||||
import AccountEditForm from './forms/AccountEditForm';
|
||||
import DeleteForm from './forms/DeleteForm';
|
||||
|
||||
export default function AccountSettings() {
|
||||
const user = useSelector(state => state.user);
|
||||
@@ -23,16 +26,23 @@ export default function AccountSettings() {
|
||||
const columns = [
|
||||
{ key: 'username', label: 'Username' },
|
||||
{
|
||||
render: row => (
|
||||
<>
|
||||
<Button icon={<Pen />} size="small" onClick={() => setEditAccount(row)}>
|
||||
<div>Edit</div>
|
||||
</Button>
|
||||
<Button icon={<Trash />} size="small" onClick={() => setDeleteAccount(row)}>
|
||||
<div>Delete</div>
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
key: 'is_admin',
|
||||
label: 'Administrator',
|
||||
render: ({ is_admin }) => (is_admin ? <Icon icon={<Check />} size="medium" /> : null),
|
||||
},
|
||||
{
|
||||
className: styles.buttons,
|
||||
render: row =>
|
||||
row.username !== 'admin' ? (
|
||||
<>
|
||||
<Button icon={<Pen />} size="small" onClick={() => setEditAccount(row)}>
|
||||
<div>Edit</div>
|
||||
</Button>
|
||||
<Button icon={<Trash />} size="small" onClick={() => setDeleteAccount(row)}>
|
||||
<div>Delete</div>
|
||||
</Button>
|
||||
</>
|
||||
) : null,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -70,7 +80,11 @@ export default function AccountSettings() {
|
||||
<Table columns={columns} rows={data} />
|
||||
{editAccount && (
|
||||
<Modal title="Edit account">
|
||||
<AccountEditForm values={editAccount} onSave={handleSave} onClose={handleClose} />
|
||||
<AccountEditForm
|
||||
values={{ ...editAccount, password: '' }}
|
||||
onSave={handleSave}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{addAccount && (
|
||||
@@ -78,6 +92,15 @@ export default function AccountSettings() {
|
||||
<AccountEditForm onSave={handleSave} onClose={handleClose} />
|
||||
</Modal>
|
||||
)}
|
||||
{deleteAccount && (
|
||||
<Modal title="Delete account">
|
||||
<DeleteForm
|
||||
values={{ type: 'account', id: deleteAccount.user_id, name: deleteAccount.username }}
|
||||
onSave={handleSave}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
.label {
|
||||
font-size: var(--font-size-normal);
|
||||
font-weight: 600;
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import Modal from './common/Modal';
|
||||
export default function ProfileSettings() {
|
||||
const user = useSelector(state => state.user);
|
||||
const [changePassword, setChangePassword] = useState(false);
|
||||
const { user_id } = user;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -17,11 +18,17 @@ export default function ProfileSettings() {
|
||||
Change password
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<dt>Username</dt>
|
||||
<dd>{user.username}</dd>
|
||||
<dl>
|
||||
<dt>Username</dt>
|
||||
<dd>{user.username}</dd>
|
||||
</dl>
|
||||
{changePassword && (
|
||||
<Modal title="Change password">
|
||||
<ChangePasswordForm values={user} onClose={() => setChangePassword(false)} />
|
||||
<ChangePasswordForm
|
||||
values={{ user_id }}
|
||||
onSave={() => setChangePassword(false)}
|
||||
onClose={() => setChangePassword(false)}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -4,10 +4,13 @@ import MenuLayout from 'components/layout/MenuLayout';
|
||||
import WebsiteSettings from './WebsiteSettings';
|
||||
import AccountSettings from './AccountSettings';
|
||||
import ProfileSettings from './ProfileSettings';
|
||||
|
||||
const menuOptions = ['Websites', 'Accounts', 'Profile'];
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
export default function Settings() {
|
||||
const user = useSelector(state => state.user);
|
||||
|
||||
const menuOptions = ['Websites', user.is_admin && 'Accounts', 'Profile'];
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<MenuLayout menu={menuOptions} selectedOption="Websites">
|
||||
|
||||
@@ -9,7 +9,7 @@ import Code from 'assets/code.svg';
|
||||
import { get } from 'lib/web';
|
||||
import Modal from './common/Modal';
|
||||
import WebsiteEditForm from './forms/WebsiteEditForm';
|
||||
import WebsiteDeleteForm from './forms/WebsiteDeleteForm';
|
||||
import DeleteForm from './forms/DeleteForm';
|
||||
import WebsiteCodeForm from './forms/WebsiteCodeForm';
|
||||
import styles from './WebsiteSettings.module.css';
|
||||
|
||||
@@ -88,7 +88,11 @@ export default function WebsiteSettings() {
|
||||
)}
|
||||
{deleteWebsite && (
|
||||
<Modal title="Delete website">
|
||||
<WebsiteDeleteForm values={deleteWebsite} onSave={handleSave} onClose={handleClose} />
|
||||
<DeleteForm
|
||||
values={{ type: 'website', id: deleteWebsite.website_id, name: deleteWebsite.name }}
|
||||
onSave={handleSave}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{showCode && (
|
||||
|
||||
@@ -14,13 +14,13 @@ const initialValues = {
|
||||
password: '',
|
||||
};
|
||||
|
||||
const validate = ({ username, password }) => {
|
||||
const validate = ({ user_id, username, password }) => {
|
||||
const errors = {};
|
||||
|
||||
if (!username) {
|
||||
errors.username = 'Required';
|
||||
}
|
||||
if (!password) {
|
||||
if (!user_id && !password) {
|
||||
errors.password = 'Required';
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ export default function AccountEditForm({ values, onSave, onClose }) {
|
||||
const handleSubmit = async values => {
|
||||
const response = await post(`/api/account`, values);
|
||||
|
||||
if (response) {
|
||||
if (typeof response !== 'string') {
|
||||
onSave();
|
||||
} else {
|
||||
setMessage('Something went wrong.');
|
||||
setMessage(response || 'Something went wrong');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function AccountEditForm({ values, onSave, onClose }) {
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<label htmlFor="password">Password</label>
|
||||
<Field name="password" type="text" />
|
||||
<Field name="password" type="password" />
|
||||
<FormError name="password" />
|
||||
</FormRow>
|
||||
<FormButtons>
|
||||
|
||||
@@ -10,24 +10,24 @@ import FormLayout, {
|
||||
} from 'components/layout/FormLayout';
|
||||
|
||||
const initialValues = {
|
||||
password: '',
|
||||
newPassword: '',
|
||||
defaultPassword: '',
|
||||
current_password: '',
|
||||
new_password: '',
|
||||
confirm_password: '',
|
||||
};
|
||||
|
||||
const validate = ({ password, newPassword, confirmPassword }) => {
|
||||
const validate = ({ current_password, new_password, confirm_password }) => {
|
||||
const errors = {};
|
||||
|
||||
if (!password) {
|
||||
errors.password = 'Required';
|
||||
if (!current_password) {
|
||||
errors.current_password = 'Required';
|
||||
}
|
||||
if (!newPassword) {
|
||||
errors.newPassword = 'Required';
|
||||
if (!new_password) {
|
||||
errors.new_password = 'Required';
|
||||
}
|
||||
if (!confirmPassword) {
|
||||
errors.confirmPassword = 'Required';
|
||||
} else if (newPassword !== confirmPassword) {
|
||||
errors.confirmPassword = `Passwords don't match`;
|
||||
if (!confirm_password) {
|
||||
errors.confirm_password = 'Required';
|
||||
} else if (new_password !== confirm_password) {
|
||||
errors.confirm_password = `Passwords don't match`;
|
||||
}
|
||||
|
||||
return errors;
|
||||
@@ -37,12 +37,12 @@ export default function ChangePasswordForm({ values, onSave, onClose }) {
|
||||
const [message, setMessage] = useState();
|
||||
|
||||
const handleSubmit = async values => {
|
||||
const response = await post(`/api/website`, values);
|
||||
const response = await post(`/api/account/password`, values);
|
||||
|
||||
if (response) {
|
||||
if (typeof response !== 'string') {
|
||||
onSave();
|
||||
} else {
|
||||
setMessage('Something went wrong.');
|
||||
setMessage(response || 'Something went wrong');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -56,19 +56,19 @@ export default function ChangePasswordForm({ values, onSave, onClose }) {
|
||||
{() => (
|
||||
<Form>
|
||||
<FormRow>
|
||||
<label htmlFor="password">Current password</label>
|
||||
<Field name="password" type="password" />
|
||||
<FormError name="password" />
|
||||
<label htmlFor="current_password">Current password</label>
|
||||
<Field name="current_password" type="password" />
|
||||
<FormError name="current_password" />
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<label htmlFor="newPassword">New password</label>
|
||||
<Field name="newPassword" type="password" />
|
||||
<FormError name="newPassword" />
|
||||
<label htmlFor="new_password">New password</label>
|
||||
<Field name="new_password" type="password" />
|
||||
<FormError name="new_password" />
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<label htmlFor="confirmPassword">Confirm password</label>
|
||||
<Field name="confirmPassword" type="password" />
|
||||
<FormError name="confirmPassword" />
|
||||
<label htmlFor="confirm_password">Confirm password</label>
|
||||
<Field name="confirm_password" type="password" />
|
||||
<FormError name="confirm_password" />
|
||||
</FormRow>
|
||||
<FormButtons>
|
||||
<Button type="submit" variant="action">
|
||||
|
||||
@@ -19,16 +19,16 @@ const validate = ({ confirmation }) => {
|
||||
return errors;
|
||||
};
|
||||
|
||||
export default function WebsiteDeleteForm({ values, onSave, onClose }) {
|
||||
export default function DeleteForm({ values, onSave, onClose }) {
|
||||
const [message, setMessage] = useState();
|
||||
|
||||
const handleSubmit = async ({ website_id }) => {
|
||||
const response = await del(`/api/website/${website_id}`);
|
||||
const handleSubmit = async ({ type, id }) => {
|
||||
const response = await del(`/api/${type}/${id}`);
|
||||
|
||||
if (response) {
|
||||
if (typeof response !== 'string') {
|
||||
onSave();
|
||||
} else {
|
||||
setMessage('Something went wrong.');
|
||||
setMessage('Something went wrong');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function LoginForm() {
|
||||
if (response?.token) {
|
||||
await Router.push('/');
|
||||
} else {
|
||||
setMessage('Incorrect username/password.');
|
||||
setMessage('Incorrect username/password');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@ export default function WebsiteEditForm({ values, onSave, onClose }) {
|
||||
const handleSubmit = async values => {
|
||||
const response = await post(`/api/website`, values);
|
||||
|
||||
if (response) {
|
||||
if (typeof response !== 'string') {
|
||||
onSave();
|
||||
} else {
|
||||
setMessage('Something went wrong.');
|
||||
setMessage('Something went wrong');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const ErrorTag = ({ msg }) => {
|
||||
|
||||
return (
|
||||
<animated.div className={styles.error} style={props}>
|
||||
{msg}
|
||||
<div className={styles.msg}>{msg}</div>
|
||||
</animated.div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--gray50);
|
||||
background: var(--red400);
|
||||
font-size: var(--font-size-small);
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -35,7 +32,13 @@
|
||||
left: 100%;
|
||||
bottom: 0;
|
||||
margin-left: 16px;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
.msg {
|
||||
color: var(--gray50);
|
||||
background: var(--red400);
|
||||
font-size: var(--font-size-small);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -8,14 +8,16 @@ export default function MenuLayout({ menu, selectedOption, onMenuSelect, childre
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.menu}>
|
||||
{menu.map(item => (
|
||||
<div
|
||||
className={classNames(styles.option, { [styles.active]: option === item })}
|
||||
onClick={() => setOption(item)}
|
||||
>
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
{menu.map(item =>
|
||||
item ? (
|
||||
<div
|
||||
className={classNames(styles.option, { [styles.active]: option === item })}
|
||||
onClick={() => setOption(item)}
|
||||
>
|
||||
{item}
|
||||
</div>
|
||||
) : null,
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
{typeof children === 'function' ? children(option) : children}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
|
||||
.option {
|
||||
padding: 8px 20px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
min-width: 140px;
|
||||
margin-right: 30px;
|
||||
|
||||
+2
-2
@@ -24,11 +24,11 @@ export function isValidId(s) {
|
||||
return validate(s);
|
||||
}
|
||||
|
||||
export function hashPassword(password) {
|
||||
export async function hashPassword(password) {
|
||||
return bcrypt.hash(password, SALT_ROUNDS);
|
||||
}
|
||||
|
||||
export function checkPassword(password, hash) {
|
||||
export async function checkPassword(password, hash) {
|
||||
return bcrypt.compare(password, hash);
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -8,18 +8,18 @@ export function redirect(res, url) {
|
||||
return res.status(303).end();
|
||||
}
|
||||
|
||||
export function badRequest(res) {
|
||||
return res.status(400).end();
|
||||
export function badRequest(res, msg) {
|
||||
return res.status(400).end(msg);
|
||||
}
|
||||
|
||||
export function unauthorized(res) {
|
||||
return res.status(401).end();
|
||||
export function unauthorized(res, msg) {
|
||||
return res.status(401).end(msg);
|
||||
}
|
||||
|
||||
export function forbidden(res) {
|
||||
return res.status(403).end();
|
||||
export function forbidden(res, msg) {
|
||||
return res.status(403).end(msg);
|
||||
}
|
||||
|
||||
export function methodNotAllowed(res) {
|
||||
res.status(405).end();
|
||||
export function methodNotAllowed(res, msg) {
|
||||
res.status(405).end(msg);
|
||||
}
|
||||
+6
-1
@@ -11,7 +11,12 @@ export const apiRequest = (method, url, body) =>
|
||||
if (res.ok) {
|
||||
return res.json();
|
||||
}
|
||||
return res.text();
|
||||
|
||||
if (['post', 'put', 'delete'].includes(method)) {
|
||||
return res.text();
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
function parseQuery(url, params = {}) {
|
||||
|
||||
+30
-11
@@ -1,15 +1,15 @@
|
||||
import { getAccounts, getAccount, updateAccount, createAccount } from 'lib/db';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { hashPassword, uuid } from 'lib/crypto';
|
||||
import { ok, unauthorized, methodNotAllowed } from 'lib/response';
|
||||
import { ok, unauthorized, methodNotAllowed, badRequest } from 'lib/response';
|
||||
|
||||
export default async (req, res) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
const { user_id: current_user_id, is_admin } = req.auth;
|
||||
const { user_id: current_user_id, is_admin: current_user_is_admin } = req.auth;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (is_admin) {
|
||||
if (current_user_is_admin) {
|
||||
const accounts = await getAccounts();
|
||||
|
||||
return ok(res, accounts);
|
||||
@@ -19,28 +19,47 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const { user_id, username, password } = req.body;
|
||||
const { user_id, username, password, is_admin } = req.body;
|
||||
|
||||
if (user_id) {
|
||||
const account = getAccount({ user_id });
|
||||
const account = await getAccount({ user_id });
|
||||
|
||||
if (account.user_id === current_user_id || is_admin) {
|
||||
if (account.user_id === current_user_id || current_user_is_admin) {
|
||||
const data = { password: password ? await hashPassword(password) : undefined };
|
||||
|
||||
if (is_admin) {
|
||||
data.username = username;
|
||||
// Only admin can change these fields
|
||||
if (current_user_is_admin) {
|
||||
// Cannot change username of admin
|
||||
if (username !== 'admin') {
|
||||
data.username = username;
|
||||
}
|
||||
data.is_admin = is_admin;
|
||||
}
|
||||
|
||||
const updated = await updateAccount(user_id, { username, password });
|
||||
if (data.username && account.username !== data.username) {
|
||||
const accountByUsername = await getAccount({ username });
|
||||
|
||||
if (accountByUsername) {
|
||||
return badRequest(res, 'Account already exists');
|
||||
}
|
||||
}
|
||||
|
||||
const updated = await updateAccount(user_id, data);
|
||||
|
||||
return ok(res, updated);
|
||||
}
|
||||
|
||||
return unauthorized(res);
|
||||
} else {
|
||||
const account = await createAccount({ username, password: await hashPassword(password) });
|
||||
const accountByUsername = await getAccount({ username });
|
||||
|
||||
return ok(res, account);
|
||||
if (accountByUsername) {
|
||||
return badRequest(res, 'Account already exists');
|
||||
}
|
||||
|
||||
const created = await createAccount({ username, password: await hashPassword(password) });
|
||||
|
||||
return ok(res, created);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { getAccount, deleteAccount } from 'lib/db';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { methodNotAllowed, ok, unauthorized } from 'lib/response';
|
||||
|
||||
export default async (req, res) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
const { is_admin } = req.auth;
|
||||
const { id } = req.query;
|
||||
const user_id = +id;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (is_admin) {
|
||||
const account = await getAccount({ user_id });
|
||||
|
||||
return ok(res, account);
|
||||
}
|
||||
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
if (is_admin) {
|
||||
await deleteAccount(user_id);
|
||||
|
||||
return ok(res);
|
||||
}
|
||||
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
return methodNotAllowed(res);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { getAccount, updateAccount } from 'lib/db';
|
||||
import { useAuth } from 'lib/middleware';
|
||||
import { badRequest, methodNotAllowed, ok } from 'lib/response';
|
||||
import { checkPassword, hashPassword } from 'lib/crypto';
|
||||
|
||||
export default async (req, res) => {
|
||||
await useAuth(req, res);
|
||||
|
||||
const { user_id } = req.auth;
|
||||
const { current_password, new_password } = req.body;
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const account = await getAccount({ user_id });
|
||||
const valid = await checkPassword(current_password, account.password);
|
||||
|
||||
if (!valid) {
|
||||
return badRequest(res, 'Current password is incorrect');
|
||||
}
|
||||
|
||||
const password = await hashPassword(new_password);
|
||||
|
||||
const updated = await updateAccount(user_id, { password });
|
||||
|
||||
return ok(res, updated);
|
||||
}
|
||||
|
||||
return methodNotAllowed(res);
|
||||
};
|
||||
@@ -7,7 +7,7 @@ import { ok, unauthorized } from 'lib/response';
|
||||
export default async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
const account = await getAccount(username);
|
||||
const account = await getAccount({ username });
|
||||
|
||||
if (account && (await checkPassword(password, account.password))) {
|
||||
const { user_id, username, is_admin } = account;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
!function(){"use strict";function e(e){var t=this.constructor;return this.then((function(n){return t.resolve(e()).then((function(){return n}))}),(function(n){return t.resolve(e()).then((function(){return t.reject(n)}))}))}var t=setTimeout;function n(e){return Boolean(e&&void 0!==e.length)}function r(){}function o(e){if(!(this instanceof o))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],f(e,this)}function i(e,t){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,o._immediateFn((function(){var n=1===e._state?t.onFulfilled:t.onRejected;if(null!==n){var r;try{r=n(e._value)}catch(e){return void s(t.promise,e)}u(t.promise,r)}else(1===e._state?u:s)(t.promise,e._value)}))):e._deferreds.push(t)}function u(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof o)return e._state=3,e._value=t,void a(e);if("function"==typeof n)return void f((r=n,i=t,function(){r.apply(i,arguments)}),e)}e._state=1,e._value=t,a(e)}catch(t){s(e,t)}var r,i}function s(e,t){e._state=2,e._value=t,a(e)}function a(e){2===e._state&&0===e._deferreds.length&&o._immediateFn((function(){e._handled||o._unhandledRejectionFn(e._value)}));for(var t=0,n=e._deferreds.length;t<n;t++)i(e,e._deferreds[t]);e._deferreds=null}function c(e,t,n){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.promise=n}function f(e,t){var n=!1;try{e((function(e){n||(n=!0,u(t,e))}),(function(e){n||(n=!0,s(t,e))}))}catch(e){if(n)return;n=!0,s(t,e)}}o.prototype["catch"]=function(e){return this.then(null,e)},o.prototype.then=function(e,t){var n=new this.constructor(r);return i(this,new c(e,t,n)),n},o.prototype["finally"]=e,o.all=function(e){return new o((function(t,r){if(!n(e))return r(new TypeError("Promise.all accepts an array"));var o=Array.prototype.slice.call(e);if(0===o.length)return t([]);var i=o.length;function u(e,n){try{if(n&&("object"==typeof n||"function"==typeof n)){var s=n.then;if("function"==typeof s)return void s.call(n,(function(t){u(e,t)}),r)}o[e]=n,0==--i&&t(o)}catch(e){r(e)}}for(var s=0;s<o.length;s++)u(s,o[s])}))},o.resolve=function(e){return e&&"object"==typeof e&&e.constructor===o?e:new o((function(t){t(e)}))},o.reject=function(e){return new o((function(t,n){n(e)}))},o.race=function(e){return new o((function(t,r){if(!n(e))return r(new TypeError("Promise.race accepts an array"));for(var i=0,u=e.length;i<u;i++)o.resolve(e[i]).then(t,r)}))},o._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){t(e,0)},o._unhandledRejectionFn=function(e){"undefined"!=typeof console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var l=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if("undefined"!=typeof global)return global;throw new Error("unable to locate global object")}();"Promise"in l?l.Promise.prototype["finally"]||(l.Promise.prototype["finally"]=e):l["Promise"]=o,self.fetch||(self.fetch=function(e,t){return t=t||{},new Promise((function(n,r){var o=new XMLHttpRequest,i=[],u=[],s={},a=function(){return{ok:2==(o.status/100|0),statusText:o.statusText,status:o.status,url:o.responseURL,text:function(){return Promise.resolve(o.responseText)},json:function(){return Promise.resolve(JSON.parse(o.responseText))},blob:function(){return Promise.resolve(new Blob([o.response]))},clone:a,headers:{keys:function(){return i},entries:function(){return u},get:function(e){return s[e.toLowerCase()]},has:function(e){return e.toLowerCase()in s}}}};for(var c in o.open(t.method||"get",e,!0),o.onload=function(){o.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,(function(e,t,n){i.push(t=t.toLowerCase()),u.push([t,n]),s[t]=s[t]?s[t]+","+n:n})),n(a())},o.onerror=r,o.withCredentials="include"==t.credentials,t.headers)o.setRequestHeader(c,t.headers[c]);o.send(t.body||null)}))});var h=function(e,t){return function(e,t,n){return fetch(t,{method:e,cache:"no-cache",headers:{Accept:"application/json","Content-Type":"application/json"},body:n}).then((function(e){return e.ok?e.json():null}))}("post",e,JSON.stringify(t))},p=function(e,t,n){var r=e[t];return function(){for(var t=[],o=arguments.length;o--;)t[o]=arguments[o];return n.apply(null,t),r.apply(e,t)}};!function(e,t){var n=e.screen,r=n.width,o=n.height,i=e.navigator.language,u=e.location,s=u.hostname,a=u.pathname,c=u.search,f=e.localStorage,l=e.document,d=e.history,v=l.querySelector("script[data-website-id]"),y=v&&v.getAttribute("data-website-id"),m=new URL(v.src).origin,w=r+"x"+o,_=[],g=""+a+c,b=l.referrer,j=function(e,n){var r={session:f.getItem(t),url:g,referrer:b,website:y,hostname:s,screen:w,language:i};return n&&Object.keys(n).forEach((function(e){r[e]=n[e]})),h(m+"/api/collect",{type:e,payload:r}).then((function(e){var n=e.session;returnLine truncated
|
||||
!function(){"use strict";function e(e){var t=this.constructor;return this.then((function(n){return t.resolve(e()).then((function(){return n}))}),(function(n){return t.resolve(e()).then((function(){return t.reject(n)}))}))}var t=setTimeout;function n(e){return Boolean(e&&void 0!==e.length)}function r(){}function o(e){if(!(this instanceof o))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],f(e,this)}function i(e,t){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,o._immediateFn((function(){var n=1===e._state?t.onFulfilled:t.onRejected;if(null!==n){var r;try{r=n(e._value)}catch(e){return void s(t.promise,e)}u(t.promise,r)}else(1===e._state?u:s)(t.promise,e._value)}))):e._deferreds.push(t)}function u(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof o)return e._state=3,e._value=t,void a(e);if("function"==typeof n)return void f((r=n,i=t,function(){r.apply(i,arguments)}),e)}e._state=1,e._value=t,a(e)}catch(t){s(e,t)}var r,i}function s(e,t){e._state=2,e._value=t,a(e)}function a(e){2===e._state&&0===e._deferreds.length&&o._immediateFn((function(){e._handled||o._unhandledRejectionFn(e._value)}));for(var t=0,n=e._deferreds.length;t<n;t++)i(e,e._deferreds[t]);e._deferreds=null}function c(e,t,n){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.promise=n}function f(e,t){var n=!1;try{e((function(e){n||(n=!0,u(t,e))}),(function(e){n||(n=!0,s(t,e))}))}catch(e){if(n)return;n=!0,s(t,e)}}o.prototype["catch"]=function(e){return this.then(null,e)},o.prototype.then=function(e,t){var n=new this.constructor(r);return i(this,new c(e,t,n)),n},o.prototype["finally"]=e,o.all=function(e){return new o((function(t,r){if(!n(e))return r(new TypeError("Promise.all accepts an array"));var o=Array.prototype.slice.call(e);if(0===o.length)return t([]);var i=o.length;function u(e,n){try{if(n&&("object"==typeof n||"function"==typeof n)){var s=n.then;if("function"==typeof s)return void s.call(n,(function(t){u(e,t)}),r)}o[e]=n,0==--i&&t(o)}catch(e){r(e)}}for(var s=0;s<o.length;s++)u(s,o[s])}))},o.resolve=function(e){return e&&"object"==typeof e&&e.constructor===o?e:new o((function(t){t(e)}))},o.reject=function(e){return new o((function(t,n){n(e)}))},o.race=function(e){return new o((function(t,r){if(!n(e))return r(new TypeError("Promise.race accepts an array"));for(var i=0,u=e.length;i<u;i++)o.resolve(e[i]).then(t,r)}))},o._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){t(e,0)},o._unhandledRejectionFn=function(e){"undefined"!=typeof console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var l=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if("undefined"!=typeof global)return global;throw new Error("unable to locate global object")}();"Promise"in l?l.Promise.prototype["finally"]||(l.Promise.prototype["finally"]=e):l["Promise"]=o,self.fetch||(self.fetch=function(e,t){return t=t||{},new Promise((function(n,r){var o=new XMLHttpRequest,i=[],u=[],s={},a=function(){return{ok:2==(o.status/100|0),statusText:o.statusText,status:o.status,url:o.responseURL,text:function(){return Promise.resolve(o.responseText)},json:function(){return Promise.resolve(JSON.parse(o.responseText))},blob:function(){return Promise.resolve(new Blob([o.response]))},clone:a,headers:{keys:function(){return i},entries:function(){return u},get:function(e){return s[e.toLowerCase()]},has:function(e){return e.toLowerCase()in s}}}};for(var c in o.open(t.method||"get",e,!0),o.onload=function(){o.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,(function(e,t,n){i.push(t=t.toLowerCase()),u.push([t,n]),s[t]=s[t]?s[t]+","+n:n})),n(a())},o.onerror=r,o.withCredentials="include"==t.credentials,t.headers)o.setRequestHeader(c,t.headers[c]);o.send(t.body||null)}))});var h=function(e,t){return function(e,t,n){return fetch(t,{method:e,cache:"no-cache",headers:{Accept:"application/json","Content-Type":"application/json"},body:n}).then((function(t){return t.ok?t.json():["post","put","delete"].includes(e)?t.text().then((function(e){return{ok:t.ok,status:t.status,message:e}})):null}))}("post",e,JSON.stringify(t))},p=function(e,t,n){var r=e[t];return function(){for(var t=[],o=arguments.length;o--;)t[o]=arguments[o];return n.apply(null,t),r.apply(e,t)}};!function(e,t){var n=e.screen,r=n.width,o=n.height,i=e.navigator.language,u=e.location,s=u.hostname,a=u.pathname,c=u.search,f=e.localStorage,l=e.document,d=e.history,v=l.querySelector("script[data-website-id]"),y=v&&v.getAttribute("data-website-id"),m=new URL(v.src).origin,w=r+"x"+o,_=[],g=""+a+c,b=l.referrer,j=function(e,n){var r={session:f.getItem(t),url:g,referrer:b,website:y,hostname:s,screen:w,language:i};return n&&Object.keys(n).forEaLine truncated
|
||||
Reference in new issue
Block a user