mirror of
https://github.com/kremalicious/umami.git
synced 2024-12-24 18:26:20 +01:00
Merge branch 'dev' of https://github.com/umami-software/umami into dev
# Conflicts: # public/iso-3166-2.json
This commit is contained in:
commit
e286994397
@ -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' },
|
||||
|
@ -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, {
|
||||
|
@ -52,7 +52,7 @@ export default function UsersTable({ data = [], onDelete }) {
|
||||
</Icon>
|
||||
<Text>{formatMessage(labels.delete)}</Text>
|
||||
</Button>
|
||||
<Modal>
|
||||
<Modal title={formatMessage(labels.deleteUser)}>
|
||||
{close => (
|
||||
<UserDeleteForm
|
||||
userId={row.id}
|
||||
|
@ -13,7 +13,7 @@ import useMessages from 'hooks/useMessages';
|
||||
const CONFIRM_VALUE = 'DELETE';
|
||||
|
||||
export default function WebsiteDeleteForm({ websiteId, onSave, onClose }) {
|
||||
const { formatMessage, labels, messages } = useMessages();
|
||||
const { formatMessage, labels, messages, FormattedMessage } = useMessages();
|
||||
const { del, useMutation } = useApi();
|
||||
const { mutate, error } = useMutation(data => del(`/websites/${websiteId}`, data));
|
||||
|
||||
@ -28,7 +28,12 @@ export default function WebsiteDeleteForm({ websiteId, onSave, onClose }) {
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit} error={error}>
|
||||
<p>{formatMessage(messages.deleteWebsite, { confirmation: CONFIRM_VALUE })}</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
{...messages.deleteWebsite}
|
||||
values={{ confirmation: <b>{CONFIRM_VALUE}</b> }}
|
||||
/>
|
||||
</p>
|
||||
<FormRow label={formatMessage(labels.confirm)}>
|
||||
<FormInput name="confirmation" rules={{ validate: value => value === CONFIRM_VALUE }}>
|
||||
<TextField autoComplete="off" />
|
||||
|
@ -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}",
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ export interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
password?: string;
|
||||
role: string;
|
||||
createdAt?: Date;
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
|
@ -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);
|
||||
|
@ -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))) {
|
||||
|
@ -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;
|
||||
|
@ -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') {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -10,7 +10,7 @@ export default function LogoutPage({ disabled }) {
|
||||
|
||||
useEffect(() => {
|
||||
async function logout() {
|
||||
await post('/logout');
|
||||
await post('/auth/logout');
|
||||
}
|
||||
|
||||
if (!disabled) {
|
||||
|
@ -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": [
|
||||
|
@ -17,6 +17,7 @@ export async function getUser(
|
||||
username: true,
|
||||
password: includePassword,
|
||||
role: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user