From f0b588308461985ed8fdf75653164414bc848b49 Mon Sep 17 00:00:00 2001 From: AlexIves16 Date: Sun, 3 May 2026 21:12:55 +0500 Subject: [PATCH] fix: username sanitization, canvas creation modal fixes, and auth headers for tasks --- app/pages/admin/groups/[id].vue | 2 +- app/pages/canvases.vue | 28 ++++++++++++------- app/pages/tasks.vue | 27 +++++++++--------- app/server/api/auth/google.post.ts | 3 +- app/server/api/groups/[id]/add-member.post.ts | 26 ++++++++--------- 5 files changed, 47 insertions(+), 39 deletions(-) diff --git a/app/pages/admin/groups/[id].vue b/app/pages/admin/groups/[id].vue index 1be038e..43a9cfe 100644 --- a/app/pages/admin/groups/[id].vue +++ b/app/pages/admin/groups/[id].vue @@ -159,7 +159,7 @@ const fetchGroup = async () => { headers: { Authorization: `Bearer ${auth.token.value}` } }) group.value = data.group - members.value = data.members || [] + members.value = data.group.members || [] } catch (e) { console.error('Failed to fetch group', e) navigateTo('/admin/groups') diff --git a/app/pages/canvases.vue b/app/pages/canvases.vue index aa88b49..881ecca 100644 --- a/app/pages/canvases.vue +++ b/app/pages/canvases.vue @@ -9,13 +9,13 @@

Мои Карты

-
+
-
@@ -223,11 +223,19 @@ const showCreateModal = ref(false) const importInput = ref(null) const activeTab = ref('personal') - const newCanvas = reactive({ - name: '', - visibility: 'private', - groupId: '' - }) +const newCanvas = reactive({ + name: '', + visibility: 'private', + groupId: '' +}) + +const openCreateModal = () => { + newCanvas.name = '' + newCanvas.visibility = 'private' + newCanvas.groupId = '' + showSettingsModal.value = false + showCreateModal.value = true +} const showSettingsModal = ref(false) const saving = ref(false) @@ -293,9 +301,9 @@ const filteredCanvases = computed(() => { switch (activeTab.value) { case 'personal': - return canvases.value.filter(c => c.ownerId === userId && !c.groupId) + return canvases.value.filter(c => c.ownerId === userId && (!c.groupId || c.groupId === '')) case 'team': - return canvases.value.filter(c => !!c.groupId) + return canvases.value.filter(c => c.groupId && c.groupId !== '') case 'public': return canvases.value.filter(c => c.isPublic) default: diff --git a/app/pages/tasks.vue b/app/pages/tasks.vue index 887736d..0c83a9a 100644 --- a/app/pages/tasks.vue +++ b/app/pages/tasks.vue @@ -163,21 +163,18 @@ const columns = [ // Fetch all tasks for current user const fetchTasks = async () => { try { - const res = await fetch('/api/tasks', { + const data = await $fetch('/api/tasks', { headers: { Authorization: `Bearer ${auth.token.value}` } }) - if (res.ok) { - const data = await res.json() - tasks.value = data.tasks || [] - - // Split tasks into columns - columns.forEach(col => { - columnsData[col.id] = tasks.value.filter(t => { - const taskState = t.state || t.data?.state || 'todo' - return taskState === col.id - }) + tasks.value = data.tasks || [] + + // Split tasks into columns + columns.forEach(col => { + columnsData[col.id] = tasks.value.filter(t => { + const taskState = t.state || t.data?.state || 'todo' + return taskState === col.id }) - } + }) } catch (e) { console.error('Failed to load tasks', e) } finally { @@ -187,8 +184,10 @@ const fetchTasks = async () => { const fetchCanvases = async () => { try { - const res = await $fetch('/api/canvas') - availableCanvases.value = res.canvases || [] + const data = await $fetch('/api/canvas', { + headers: { Authorization: `Bearer ${auth.token.value}` } + }) + availableCanvases.value = data.canvases || [] if (availableCanvases.value.length > 0) { newTask.canvasId = availableCanvases.value[0].id } diff --git a/app/server/api/auth/google.post.ts b/app/server/api/auth/google.post.ts index e490661..35b6e01 100644 --- a/app/server/api/auth/google.post.ts +++ b/app/server/api/auth/google.post.ts @@ -34,7 +34,8 @@ export default defineEventHandler(async (event) => { const fbUser: FirebaseUser = verifyRes.users[0] const email = fbUser.email - const username = email.split('@')[0] + // Очищаем username: заменяем пробелы на дефисы и убираем спецсимволы + const username = email.split('@')[0].toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-_]/g, '') const userId = fbUser.localId const user = await prisma.user.upsert({ diff --git a/app/server/api/groups/[id]/add-member.post.ts b/app/server/api/groups/[id]/add-member.post.ts index 386b51f..8c32855 100644 --- a/app/server/api/groups/[id]/add-member.post.ts +++ b/app/server/api/groups/[id]/add-member.post.ts @@ -5,28 +5,28 @@ export default defineEventHandler(async (event) => { const adminUser = requireAuth(event) const groupId = getRouterParam(event, 'id') const body = await readBody(event) - const { email, role = 'member' } = body + const { email, userId, role = 'member' } = body - if (!groupId || !email) { - throw createError({ statusCode: 400, message: 'Group ID and Email are required' }) + if (!groupId || (!email && !userId)) { + throw createError({ statusCode: 400, message: 'Group ID and either Email or User ID are required' }) } - // 1. Проверяем, является ли текущий пользователь админом группы или глобальным админом - const group = await prisma.group.findUnique({ - where: { id: groupId } - }) - + // 1. Проверяем права + const group = await prisma.group.findUnique({ where: { id: groupId } }) if (!group || (group.ownerId !== adminUser.id && adminUser.role !== 'admin')) { throw createError({ statusCode: 403, message: 'Only group owners or admins can add members' }) } - // 2. Ищем пользователя по email - const userToAdd = await prisma.user.findUnique({ - where: { email } - }) + // 2. Находим пользователя + let userToAdd = null + if (userId) { + userToAdd = await prisma.user.findUnique({ where: { id: userId } }) + } else if (email) { + userToAdd = await prisma.user.findUnique({ where: { email } }) + } if (!userToAdd) { - throw createError({ statusCode: 404, message: 'User with this email not found in system' }) + throw createError({ statusCode: 404, message: 'User not found' }) } // 3. Добавляем в группу