diff --git a/components/messages.js b/components/messages.js index 6100afbf..9de5f52b 100644 --- a/components/messages.js +++ b/components/messages.js @@ -10,6 +10,7 @@ export const labels = defineMessages({ leave: { id: 'label.leave', defaultMessage: 'Leave' }, users: { id: 'label.users', defaultMessage: 'Users' }, createUser: { id: 'label.create-user', defaultMessage: 'Create user' }, + deleteUser: { id: 'label.delete-users', defaultMessage: 'Delete user' }, username: { id: 'label.username', defaultMessage: 'Username' }, password: { id: 'label.password', defaultMessage: 'Password' }, role: { id: 'label.role', defaultMessage: 'Role' }, diff --git a/components/pages/settings/users/UserEditForm.js b/components/pages/settings/users/UserEditForm.js index e746b18b..49df4b24 100644 --- a/components/pages/settings/users/UserEditForm.js +++ b/components/pages/settings/users/UserEditForm.js @@ -16,7 +16,9 @@ import useMessages from 'hooks/useMessages'; export default function UserEditForm({ userId, data, onSave }) { const { formatMessage, labels, messages } = useMessages(); const { post, useMutation } = useApi(); - const { mutate, error } = useMutation(({ username }) => post(`/users/${userId}`, { username })); + const { mutate, error } = useMutation(({ username, password, role }) => + post(`/users/${userId}`, { username, password, role }), + ); const handleSubmit = async data => { mutate(data, { diff --git a/components/pages/settings/users/UsersTable.js b/components/pages/settings/users/UsersTable.js index 5ea3806c..c60f11ff 100644 --- a/components/pages/settings/users/UsersTable.js +++ b/components/pages/settings/users/UsersTable.js @@ -52,7 +52,7 @@ export default function UsersTable({ data = [], onDelete }) { {formatMessage(labels.delete)} - + {close => ( del(`/websites/${websiteId}`, data)); @@ -28,7 +28,12 @@ export default function WebsiteDeleteForm({ websiteId, onSave, onClose }) { return (
-

{formatMessage(messages.deleteWebsite, { confirmation: CONFIRM_VALUE })}

+

+ {CONFIRM_VALUE} }} + /> +

value === CONFIRM_VALUE }}> diff --git a/lang/en-US.json b/lang/en-US.json index 44b4e475..a601bf60 100644 --- a/lang/en-US.json +++ b/lang/en-US.json @@ -116,7 +116,7 @@ "message.confirm-delete": "Are you sure you want to delete {target}?", "message.confirm-leave": "Are you sure you want to leave {target}?", "message.confirm-reset": "Are you sure you want to reset {target}'s statistics?", - "message.delete-website": "Delete website", + "message.delete-website": "To delete this website, type {confirmation} in the box below to confirm.", "message.delete-website-warning": "All associated data will be deleted as well.", "message.error": "Something went wrong.", "message.event-log": "{event} on {url}", diff --git a/lib/auth.ts b/lib/auth.ts index 93f24b2c..2195ad8f 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -95,17 +95,11 @@ export async function canViewWebsite({ user, shareToken }: Auth, websiteId: stri return false; } -export async function canCreateWebsite({ user }: Auth, teamId?: string) { +export async function canCreateWebsite({ user }: Auth) { if (user.isAdmin) { return true; } - if (teamId) { - const teamUser = await getTeamUser(teamId, user.id); - - return hasPermission(teamUser?.role, PERMISSIONS.websiteCreate); - } - return hasPermission(user.role, PERMISSIONS.websiteCreate); } diff --git a/lib/types.ts b/lib/types.ts index 034326f0..5db5a586 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -43,6 +43,7 @@ export interface User { id: string; username: string; password?: string; + role: string; createdAt?: Date; } diff --git a/pages/api/auth/login.ts b/pages/api/auth/login.ts index 97c43ca7..9bdfec22 100644 --- a/pages/api/auth/login.ts +++ b/pages/api/auth/login.ts @@ -45,7 +45,10 @@ export default async ( const token = createSecureToken({ userId: user.id }, secret()); - return ok(res, { token, user }); + return ok(res, { + token, + user: { id: user.id, username: user.username, createdAt: user.createdAt }, + }); } return unauthorized(res, 'message.incorrect-username-password'); diff --git a/pages/api/teams/[id]/websites/[websiteId].ts b/pages/api/teams/[id]/websites/[websiteId].ts index 5c0835d9..795295d3 100644 --- a/pages/api/teams/[id]/websites/[websiteId].ts +++ b/pages/api/teams/[id]/websites/[websiteId].ts @@ -23,9 +23,9 @@ export default async ( return unauthorized(res); } - const websites = await deleteTeamWebsite(teamId, websiteId); + await deleteTeamWebsite(teamId, websiteId); - return ok(res, websites); + return ok(res); } return methodNotAllowed(res); diff --git a/pages/api/teams/[id]/websites/index.ts b/pages/api/teams/[id]/websites/index.ts index 0db429d1..63be478b 100644 --- a/pages/api/teams/[id]/websites/index.ts +++ b/pages/api/teams/[id]/websites/index.ts @@ -10,7 +10,6 @@ export interface TeamWebsiteRequestQuery { } export interface TeamWebsiteRequestBody { - teamWebsiteId?: string; websiteIds?: string[]; } @@ -21,9 +20,6 @@ export default async ( await useAuth(req, res); const { id: teamId } = req.query; - const { - user: { id: userId }, - } = req.auth; if (req.method === 'GET') { if (!(await canViewTeam(req.auth, teamId))) { diff --git a/pages/api/users/[id]/index.ts b/pages/api/users/[id]/index.ts index c7106d95..8219c4a7 100644 --- a/pages/api/users/[id]/index.ts +++ b/pages/api/users/[id]/index.ts @@ -1,4 +1,4 @@ -import { NextApiRequestQueryBody, User } from 'lib/types'; +import { NextApiRequestQueryBody, Roles, User } from 'lib/types'; import { canDeleteUser, canUpdateUser, canViewUser } from 'lib/auth'; import { useAuth } from 'lib/middleware'; import { NextApiResponse } from 'next'; @@ -12,6 +12,7 @@ export interface UserRequestQuery { export interface UserRequestBody { username: string; password: string; + role: Roles; } export default async ( @@ -40,17 +41,20 @@ export default async ( return unauthorized(res); } - const { username, password } = req.body; + const { username, password, role } = req.body; const user = await getUser({ id }); const data: any = {}; - // Only admin can change these fields - if (password && isAdmin) { + if (password) { data.password = hashPassword(password); } + if (role && isAdmin) { + data.role = role; + } + // Only admin can change these fields if (username && isAdmin) { data.username = username; diff --git a/pages/api/websites/[id]/index.ts b/pages/api/websites/[id]/index.ts index 671f6274..c1907fce 100644 --- a/pages/api/websites/[id]/index.ts +++ b/pages/api/websites/[id]/index.ts @@ -41,15 +41,17 @@ export default async ( const { name, domain, shareId } = req.body; + let website; + try { - await updateWebsite(websiteId, { name, domain, shareId }); + website = await updateWebsite(websiteId, { name, domain, shareId }); } catch (e: any) { if (e.message.includes('Unique constraint') && e.message.includes('share_id')) { return serverError(res, 'That share ID is already taken.'); } } - return ok(res); + return ok(res, website); } if (req.method === 'DELETE') { diff --git a/pages/api/websites/index.ts b/pages/api/websites/index.ts index 52c3ba35..49797d08 100644 --- a/pages/api/websites/index.ts +++ b/pages/api/websites/index.ts @@ -10,7 +10,6 @@ export interface WebsitesRequestBody { name: string; domain: string; shareId: string; - teamId?: string; } export default async ( @@ -31,9 +30,9 @@ export default async ( } if (req.method === 'POST') { - const { name, domain, shareId, teamId } = req.body; + const { name, domain, shareId } = req.body; - if (!(await canCreateWebsite(req.auth, teamId))) { + if (!(await canCreateWebsite(req.auth))) { return unauthorized(res); } @@ -44,11 +43,7 @@ export default async ( shareId, }; - if (teamId) { - data.teamId = teamId; - } else { - data.userId = userId; - } + data.userId = userId; const website = await createWebsite(data); diff --git a/pages/logout.js b/pages/logout.js index 8f102f6a..6ffe23e1 100644 --- a/pages/logout.js +++ b/pages/logout.js @@ -10,7 +10,7 @@ export default function LogoutPage({ disabled }) { useEffect(() => { async function logout() { - await post('/logout'); + await post('/auth/logout'); } if (!disabled) { diff --git a/public/intl/messages/en-US.json b/public/intl/messages/en-US.json index d8ce582a..5dc57b9e 100644 --- a/public/intl/messages/en-US.json +++ b/public/intl/messages/en-US.json @@ -758,7 +758,15 @@ "message.delete-website": [ { "type": 0, - "value": "Delete website" + "value": "To delete this website, type " + }, + { + "type": 1, + "value": "confirmation" + }, + { + "type": 0, + "value": " in the box below to confirm." } ], "message.delete-website-warning": [ diff --git a/queries/admin/user.ts b/queries/admin/user.ts index 1f3a2bd0..412c7785 100644 --- a/queries/admin/user.ts +++ b/queries/admin/user.ts @@ -17,6 +17,7 @@ export async function getUser( username: true, password: includePassword, role: true, + createdAt: true, }, }); } diff --git a/queries/analytics/event/getEventMetrics.ts b/queries/analytics/event/getEventMetrics.ts index 662e072b..32deef5d 100644 --- a/queries/analytics/event/getEventMetrics.ts +++ b/queries/analytics/event/getEventMetrics.ts @@ -13,7 +13,6 @@ export async function getEventMetrics( endDate: Date; timezone: string; unit: string; - column: string; filters: { url: string; eventName: string; @@ -40,7 +39,6 @@ async function relationalQuery( endDate: Date; timezone: string; unit: string; - column: string; filters: { url: string; eventName: string;