implemented the user view type and website sharing
parent
7a3443cd06
commit
66e67c7a3b
|
@ -1,18 +1,23 @@
|
|||
import React, { useRef } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Icon from 'components/common/Icon';
|
||||
import Check from 'assets/check.svg';
|
||||
import styles from './Checkbox.module.css';
|
||||
|
||||
function Checkbox({ name, value, label, onChange }) {
|
||||
function Checkbox({ name, value, label, onChange, valueArray }) {
|
||||
const ref = useRef();
|
||||
const [isChecked, setIsChecked] = useState();
|
||||
|
||||
const onClick = () => ref.current.click();
|
||||
|
||||
useEffect(() => {
|
||||
setIsChecked((valueArray && valueArray.includes(value)) || (!valueArray && value));
|
||||
}, [valueArray, value]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.checkbox} onClick={onClick}>
|
||||
{value && <Icon icon={<Check />} size="small" />}
|
||||
{isChecked && <Icon icon={<Check />} size="small" />}
|
||||
</div>
|
||||
<label className={styles.label} htmlFor={name} onClick={onClick}>
|
||||
{label}
|
||||
|
@ -22,7 +27,8 @@ function Checkbox({ name, value, label, onChange }) {
|
|||
className={styles.input}
|
||||
type="checkbox"
|
||||
name={name}
|
||||
defaultChecked={value}
|
||||
value={valueArray ? value : isChecked}
|
||||
defaultChecked={isChecked}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Formik, Form, Field } from 'formik';
|
||||
import { Formik, Form, Field, useFormikContext, useField } from 'formik';
|
||||
import Button from 'components/common/Button';
|
||||
import Checkbox from 'components/common/Checkbox';
|
||||
import FormLayout, {
|
||||
FormButtons,
|
||||
FormError,
|
||||
|
@ -9,10 +10,13 @@ import FormLayout, {
|
|||
FormRow,
|
||||
} from 'components/layout/FormLayout';
|
||||
import useApi from 'hooks/useApi';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
|
||||
const initialValues = {
|
||||
username: '',
|
||||
password: '',
|
||||
isViewer: false,
|
||||
websiteIds: [],
|
||||
};
|
||||
|
||||
const validate = ({ id, username, password }) => {
|
||||
|
@ -28,6 +32,44 @@ const validate = ({ id, username, password }) => {
|
|||
return errors;
|
||||
};
|
||||
|
||||
const WebsiteSelect = props => {
|
||||
const { data } = useFetch(`/websites`);
|
||||
const [field, meta] = useField(props);
|
||||
const {
|
||||
values: { websiteIds },
|
||||
} = useFormikContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
{data && data.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
maxHeight: '20vh',
|
||||
overflowY: 'auto',
|
||||
padding: '0 1rem',
|
||||
margin: '0 20px',
|
||||
background: 'var(--gray100)',
|
||||
border: '1px solid var(--gray500)',
|
||||
borderRadius: '5px',
|
||||
}}
|
||||
>
|
||||
{data.map(item => (
|
||||
<div key={`websiteIds-${item.id}`}>
|
||||
<Checkbox
|
||||
{...field}
|
||||
value={item.id.toString()}
|
||||
label={item.name}
|
||||
valueArray={websiteIds}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{!!meta.touched && !!meta.error && <div>{meta.error}</div>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default function AccountEditForm({ values, onSave, onClose }) {
|
||||
const { post } = useApi();
|
||||
const [message, setMessage] = useState();
|
||||
|
@ -52,7 +94,7 @@ export default function AccountEditForm({ values, onSave, onClose }) {
|
|||
validate={validate}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{() => (
|
||||
{values => (
|
||||
<Form>
|
||||
<FormRow>
|
||||
<label htmlFor="username">
|
||||
|
@ -72,6 +114,20 @@ export default function AccountEditForm({ values, onSave, onClose }) {
|
|||
<FormError name="password" />
|
||||
</div>
|
||||
</FormRow>
|
||||
{!values.values.isAdmin && (
|
||||
<FormRow>
|
||||
<label />
|
||||
<Field name="isViewer">
|
||||
{({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
label={<FormattedMessage id="label.is-viewer" defaultMessage="Viewer" />}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</FormRow>
|
||||
)}
|
||||
{values.values.isViewer && <WebsiteSelect name="websiteIds" />}
|
||||
<FormButtons>
|
||||
<Button type="submit" variant="action">
|
||||
<FormattedMessage id="label.save" defaultMessage="Save" />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Link from 'next/link';
|
||||
import classNames from 'classnames';
|
||||
|
@ -26,6 +26,14 @@ export default function AccountSettings() {
|
|||
const [saved, setSaved] = useState(0);
|
||||
const [message, setMessage] = useState();
|
||||
const { data } = useFetch(`/accounts`, {}, [saved]);
|
||||
const [formattedData, setFormattedData] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data)
|
||||
setFormattedData(
|
||||
data.map(d => ({ ...d, websiteIds: d.viewwebsites.map(vw => vw.websiteId.toString()) })),
|
||||
);
|
||||
}, [data]);
|
||||
|
||||
const Checkmark = ({ isAdmin }) => (isAdmin ? <Icon icon={<Check />} size="medium" /> : null);
|
||||
|
||||
|
@ -103,7 +111,7 @@ export default function AccountSettings() {
|
|||
<FormattedMessage id="label.add-account" defaultMessage="Add account" />
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<Table columns={columns} rows={data} />
|
||||
<Table columns={columns} rows={formattedData} />
|
||||
{editAccount && (
|
||||
<Modal title={<FormattedMessage id="label.edit-account" defaultMessage="Edit account" />}>
|
||||
<AccountEditForm
|
||||
|
|
|
@ -50,36 +50,44 @@ export default function WebsiteSettings() {
|
|||
onClick={() => setShowUrl(row)}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
icon={<Code />}
|
||||
size="small"
|
||||
tooltip={
|
||||
<FormattedMessage id="message.get-tracking-code" defaultMessage="Get tracking code" />
|
||||
}
|
||||
tooltipId={`button-code-${row.websiteUuid}`}
|
||||
onClick={() => setShowCode(row)}
|
||||
/>
|
||||
<Button
|
||||
icon={<Pen />}
|
||||
size="small"
|
||||
tooltip={<FormattedMessage id="label.edit" defaultMessage="Edit" />}
|
||||
tooltipId={`button-edit-${row.websiteUuid}`}
|
||||
onClick={() => setEditWebsite(row)}
|
||||
/>
|
||||
<Button
|
||||
icon={<Reset />}
|
||||
size="small"
|
||||
tooltip={<FormattedMessage id="label.reset" defaultMessage="Reset" />}
|
||||
tooltipId={`button-reset-${row.websiteUuid}`}
|
||||
onClick={() => setResetWebsite(row)}
|
||||
/>
|
||||
<Button
|
||||
icon={<Trash />}
|
||||
size="small"
|
||||
tooltip={<FormattedMessage id="label.delete" defaultMessage="Delete" />}
|
||||
tooltipId={`button-delete-${row.websiteUuid}`}
|
||||
onClick={() => setDeleteWebsite(row)}
|
||||
/>
|
||||
{!user.isViewer && (
|
||||
<Button
|
||||
icon={<Code />}
|
||||
size="small"
|
||||
tooltip={
|
||||
<FormattedMessage id="message.get-tracking-code" defaultMessage="Get tracking code" />
|
||||
}
|
||||
tooltipId={`button-code-${row.websiteUuid}`}
|
||||
onClick={() => setShowCode(row)}
|
||||
/>
|
||||
)}
|
||||
{!user.isViewer && (
|
||||
<Button
|
||||
icon={<Pen />}
|
||||
size="small"
|
||||
tooltip={<FormattedMessage id="label.edit" defaultMessage="Edit" />}
|
||||
tooltipId={`button-edit-${row.websiteUuid}`}
|
||||
onClick={() => setEditWebsite(row)}
|
||||
/>
|
||||
)}
|
||||
{!user.isViewer && (
|
||||
<Button
|
||||
icon={<Reset />}
|
||||
size="small"
|
||||
tooltip={<FormattedMessage id="label.reset" defaultMessage="Reset" />}
|
||||
tooltipId={`button-reset-${row.websiteUuid}`}
|
||||
onClick={() => setResetWebsite(row)}
|
||||
/>
|
||||
)}
|
||||
{!user.isViewer && (
|
||||
<Button
|
||||
icon={<Trash />}
|
||||
size="small"
|
||||
tooltip={<FormattedMessage id="label.delete" defaultMessage="Delete" />}
|
||||
tooltipId={`button-delete-${row.websiteUuid}`}
|
||||
onClick={() => setDeleteWebsite(row)}
|
||||
/>
|
||||
)}
|
||||
</ButtonLayout>
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE `account` ADD COLUMN `is_viewer` BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE `event_data` DROP PRIMARY KEY,
|
||||
MODIFY `event_data_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
ADD PRIMARY KEY (`event_data_id`);
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `_event_old`;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `viewerforwebsite` (
|
||||
`user_id` INTEGER UNSIGNED NOT NULL,
|
||||
`website_id` INTEGER UNSIGNED NOT NULL,
|
||||
|
||||
PRIMARY KEY (`user_id`, `website_id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `viewerforwebsite` ADD CONSTRAINT `viewerforwebsite_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `account`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `viewerforwebsite` ADD CONSTRAINT `viewerforwebsite_website_id_fkey` FOREIGN KEY (`website_id`) REFERENCES `website`(`website_id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -8,14 +8,16 @@ datasource db {
|
|||
}
|
||||
|
||||
model account {
|
||||
id Int @id @default(autoincrement()) @map("user_id") @db.UnsignedInt
|
||||
username String @unique() @db.VarChar(255)
|
||||
password String @db.VarChar(60)
|
||||
isAdmin Boolean @default(false) @map("is_admin")
|
||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
||||
updatedAt DateTime? @default(now()) @map("updated_at") @db.Timestamp(0)
|
||||
accountUuid String @unique() @map("account_uuid") @db.VarChar(36)
|
||||
website website[]
|
||||
id Int @id @default(autoincrement()) @map("user_id") @db.UnsignedInt
|
||||
username String @unique() @db.VarChar(255)
|
||||
password String @db.VarChar(60)
|
||||
isAdmin Boolean @default(false) @map("is_admin")
|
||||
isViewer Boolean @default(false) @map("is_viewer")
|
||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
||||
updatedAt DateTime? @default(now()) @map("updated_at") @db.Timestamp(0)
|
||||
accountUuid String @unique() @map("account_uuid") @db.VarChar(36)
|
||||
website website[]
|
||||
viewwebsites viewerforwebsite[]
|
||||
|
||||
@@index([accountUuid])
|
||||
}
|
||||
|
@ -86,18 +88,29 @@ model session {
|
|||
}
|
||||
|
||||
model website {
|
||||
id Int @id @default(autoincrement()) @map("website_id") @db.UnsignedInt
|
||||
websiteUuid String @unique() @map("website_uuid") @db.VarChar(36)
|
||||
userId Int @map("user_id") @db.UnsignedInt
|
||||
name String @db.VarChar(100)
|
||||
domain String? @db.VarChar(500)
|
||||
shareId String? @unique() @map("share_id") @db.VarChar(64)
|
||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
||||
account account @relation(fields: [userId], references: [id])
|
||||
id Int @id @default(autoincrement()) @map("website_id") @db.UnsignedInt
|
||||
websiteUuid String @unique() @map("website_uuid") @db.VarChar(36)
|
||||
userId Int @map("user_id") @db.UnsignedInt
|
||||
name String @db.VarChar(100)
|
||||
domain String? @db.VarChar(500)
|
||||
shareId String? @unique() @map("share_id") @db.VarChar(64)
|
||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
|
||||
account account @relation(fields: [userId], references: [id])
|
||||
event event[]
|
||||
pageview pageview[]
|
||||
session session[]
|
||||
viewers viewerforwebsite[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([websiteUuid])
|
||||
}
|
||||
|
||||
model viewerforwebsite {
|
||||
userId Int @map("user_id") @db.UnsignedInt
|
||||
websiteId Int @map("website_id") @db.UnsignedInt
|
||||
|
||||
account account @relation(fields: [userId], references: [id])
|
||||
website website @relation(fields: [websiteId], references: [id])
|
||||
|
||||
@@id([userId, websiteId])
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "account" ADD COLUMN "is_viewer" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "_event_old";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "viewerforwebsite" (
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"website_id" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "viewerforwebsite_pkey" PRIMARY KEY ("user_id","website_id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "viewerforwebsite" ADD CONSTRAINT "viewerforwebsite_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "account"("user_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "viewerforwebsite" ADD CONSTRAINT "viewerforwebsite_website_id_fkey" FOREIGN KEY ("website_id") REFERENCES "website"("website_id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -12,10 +12,12 @@ model account {
|
|||
username String @unique @db.VarChar(255)
|
||||
password String @db.VarChar(60)
|
||||
isAdmin Boolean @default(false) @map("is_admin")
|
||||
isViewer Boolean @default(false) @map("is_viewer")
|
||||
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
|
||||
updatedAt DateTime? @default(now()) @map("updated_at") @db.Timestamptz(6)
|
||||
accountUuid String @unique @map("account_uuid") @db.Uuid
|
||||
website website[]
|
||||
viewwebsites viewerforwebsite[]
|
||||
|
||||
@@index([accountUuid])
|
||||
}
|
||||
|
@ -97,7 +99,18 @@ model website {
|
|||
event event[]
|
||||
pageview pageview[]
|
||||
session session[]
|
||||
viewers viewerforwebsite[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([websiteUuid])
|
||||
}
|
||||
|
||||
model viewerforwebsite {
|
||||
userId Int @map("user_id")
|
||||
websiteId Int @map("website_id")
|
||||
|
||||
account account @relation(fields: [userId], references: [id])
|
||||
website website @relation(fields: [websiteId], references: [id])
|
||||
|
||||
@@id([userId, websiteId])
|
||||
}
|
||||
|
|
|
@ -52,7 +52,10 @@ export async function allowQuery(req, type, allowShareToken = true) {
|
|||
if (type === TYPE_WEBSITE) {
|
||||
const website = await getWebsite({ websiteUuid: id });
|
||||
|
||||
return website && website.userId === userId;
|
||||
return (
|
||||
website &&
|
||||
(website.userId === userId || website.viewers.map(v => v.userId).includes(userId))
|
||||
);
|
||||
} else if (type === TYPE_ACCOUNT) {
|
||||
const account = await getAccount({ accountUuid: id });
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ export default async (req, res) => {
|
|||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const { username, password } = req.body;
|
||||
const { username, password, isViewer, websiteIds } = req.body;
|
||||
|
||||
if (id !== userId && !isAdmin) {
|
||||
return unauthorized(res);
|
||||
|
@ -29,6 +29,8 @@ export default async (req, res) => {
|
|||
|
||||
const data = {};
|
||||
|
||||
data.isViewer = isViewer;
|
||||
|
||||
if (password) {
|
||||
data.password = hashPassword(password);
|
||||
}
|
||||
|
@ -36,6 +38,24 @@ export default async (req, res) => {
|
|||
// Only admin can change these fields
|
||||
if (isAdmin) {
|
||||
data.username = username;
|
||||
|
||||
const existingWebsiteIds = websiteIds.map(id => parseInt(id));
|
||||
|
||||
const websitesToDisconnect = account.viewwebsites
|
||||
.map(vw => vw.websiteId)
|
||||
.filter(id => !existingWebsiteIds.includes(id));
|
||||
|
||||
const websitesToConnect = existingWebsiteIds.filter(
|
||||
id => !account.viewwebsites.map(vw => vw.websiteId).includes(id),
|
||||
);
|
||||
|
||||
data.viewwebsites = {
|
||||
create: websitesToConnect.map(id => ({ website: { connect: { id } } })),
|
||||
deleteMany: websitesToDisconnect.map(id => ({
|
||||
websiteId: id,
|
||||
userId: account.id,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// Check when username changes
|
||||
|
|
|
@ -19,7 +19,7 @@ export default async (req, res) => {
|
|||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
const { username, password, account_uuid } = req.body;
|
||||
const { username, password, account_uuid, websiteIds, isViewer } = req.body;
|
||||
|
||||
const account = await getAccount({ username });
|
||||
|
||||
|
@ -31,6 +31,14 @@ export default async (req, res) => {
|
|||
username,
|
||||
password: hashPassword(password),
|
||||
accountUuid: account_uuid || uuid(),
|
||||
isViewer,
|
||||
viewwebsites: {
|
||||
create: websiteIds
|
||||
.map(id => parseInt(id))
|
||||
.map(id => ({
|
||||
website: { connect: { id } },
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
return ok(res, created);
|
||||
|
|
|
@ -12,8 +12,8 @@ export default async (req, res) => {
|
|||
const account = await getAccount({ username });
|
||||
|
||||
if (account && checkPassword(password, account.password)) {
|
||||
const { id, username, isAdmin, accountUuid } = account;
|
||||
const user = { userId: id, username, isAdmin, accountUuid };
|
||||
const { id, username, isAdmin, isViewer, accountUuid } = account;
|
||||
const user = { userId: id, username, isAdmin, isViewer, accountUuid };
|
||||
const token = createSecureToken(user, secret());
|
||||
|
||||
return ok(res, { token, user });
|
||||
|
|
|
@ -14,7 +14,9 @@ export default async (req, res) => {
|
|||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const websites = await getUserWebsites({ userId });
|
||||
const websites = await getUserWebsites({
|
||||
OR: [{ userId }, { viewers: { some: { userId } } }],
|
||||
});
|
||||
const ids = websites.map(({ websiteUuid }) => websiteUuid);
|
||||
const token = createToken({ websites: ids }, secret());
|
||||
const data = await getRealtimeData(ids, subMinutes(new Date(), 30));
|
||||
|
|
|
@ -9,6 +9,7 @@ export default async (req, res) => {
|
|||
await useAuth(req, res);
|
||||
|
||||
const { id: websiteUuid } = req.query;
|
||||
const { accountUuid, isViewer } = req.auth;
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (!(await allowQuery(req, TYPE_WEBSITE))) {
|
||||
|
@ -21,12 +22,11 @@ export default async (req, res) => {
|
|||
}
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (!(await allowQuery(req, TYPE_WEBSITE, false))) {
|
||||
if (!(await allowQuery(req, TYPE_WEBSITE, false)) || isViewer) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
const { name, domain, owner, enableShareUrl, shareId } = req.body;
|
||||
const { accountUuid } = req.auth;
|
||||
|
||||
let account;
|
||||
|
||||
|
@ -62,7 +62,7 @@ export default async (req, res) => {
|
|||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
if (!(await allowQuery(req, TYPE_WEBSITE, false))) {
|
||||
if (!(await allowQuery(req, TYPE_WEBSITE, false)) || isViewer) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@ export default async (req, res) => {
|
|||
await useAuth(req, res);
|
||||
|
||||
const { id: websiteId } = req.query;
|
||||
const { isViewer } = req.auth;
|
||||
|
||||
if (req.method === 'POST') {
|
||||
if (!(await allowQuery(req, TYPE_WEBSITE, false))) {
|
||||
if (!(await allowQuery(req, TYPE_WEBSITE, false)) || isViewer) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ export default async (req, res) => {
|
|||
|
||||
const { user_id, include_all } = req.query;
|
||||
|
||||
const { userId: currentUserId, isAdmin } = req.auth;
|
||||
const { userId: currentUserId, isAdmin, isViewer } = req.auth;
|
||||
|
||||
const accountUuid = user_id || req.auth.accountUuid;
|
||||
let account;
|
||||
|
||||
|
@ -26,7 +27,9 @@ export default async (req, res) => {
|
|||
const websites =
|
||||
isAdmin && include_all
|
||||
? await getAllWebsites()
|
||||
: await getUserWebsites({ userId: account?.id });
|
||||
: await getUserWebsites({
|
||||
OR: [{ userId: account?.id }, { viewers: { some: { userId: account?.id } } }],
|
||||
});
|
||||
|
||||
return ok(res, websites);
|
||||
}
|
||||
|
@ -36,7 +39,7 @@ export default async (req, res) => {
|
|||
|
||||
const website_owner = account ? account.id : +owner;
|
||||
|
||||
if (website_owner !== currentUserId && !isAdmin) {
|
||||
if ((website_owner !== currentUserId && !isAdmin) || isViewer) {
|
||||
return unauthorized(res);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,5 +3,8 @@ import prisma from 'lib/prisma';
|
|||
export async function getAccount(where) {
|
||||
return prisma.client.account.findUnique({
|
||||
where,
|
||||
include: {
|
||||
viewwebsites: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,9 +12,11 @@ export async function getAccounts() {
|
|||
id: true,
|
||||
username: true,
|
||||
isAdmin: true,
|
||||
isViewer: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
accountUuid: true,
|
||||
viewwebsites: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@ export async function getWebsite(where) {
|
|||
return prisma.client.website
|
||||
.findUnique({
|
||||
where,
|
||||
include: {
|
||||
viewers: true,
|
||||
},
|
||||
})
|
||||
.then(async data => {
|
||||
if (redis.enabled && data) {
|
||||
|
|
Loading…
Reference in New Issue