fix: username sanitization, canvas creation modal fixes, and auth headers for tasks

This commit is contained in:
2026-05-03 21:12:55 +05:00
parent 4a912d972e
commit f0b5883084
5 changed files with 47 additions and 39 deletions
+1 -1
View File
@@ -159,7 +159,7 @@ const fetchGroup = async () => {
headers: { Authorization: `Bearer ${auth.token.value}` } headers: { Authorization: `Bearer ${auth.token.value}` }
}) })
group.value = data.group group.value = data.group
members.value = data.members || [] members.value = data.group.members || []
} catch (e) { } catch (e) {
console.error('Failed to fetch group', e) console.error('Failed to fetch group', e)
navigateTo('/admin/groups') navigateTo('/admin/groups')
+15 -7
View File
@@ -9,13 +9,13 @@
<div class="flex items-center justify-between mb-8"> <div class="flex items-center justify-between mb-8">
<h1 class="text-2xl font-bold text-slate-100">Мои Карты</h1> <h1 class="text-2xl font-bold text-slate-100">Мои Карты</h1>
<div class="flex gap-2"> <div class="flex items-center gap-3">
<button @click="triggerImport" class="px-4 py-2 bg-slate-700 hover:bg-slate-600 text-white rounded-lg font-medium transition"> <button @click="triggerImport" class="px-4 py-2 bg-slate-700 hover:bg-slate-600 text-white rounded-lg font-medium transition">
Импорт .canvas Импорт .canvas
</button> </button>
<input ref="importInput" type="file" accept=".canvas,.json" class="hidden" @change="handleImport" /> <input ref="importInput" type="file" accept=".canvas,.json" class="hidden" @change="handleImport" />
<button @click="showCreateModal = true" class="px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg font-medium transition"> <button @click="openCreateModal" class="px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg font-bold shadow-lg shadow-blue-900/20 transition flex items-center gap-2">
+ Новая Карта <span>➕</span> Создать
</button> </button>
</div> </div>
</div> </div>
@@ -223,11 +223,19 @@ const showCreateModal = ref(false)
const importInput = ref(null) const importInput = ref(null)
const activeTab = ref('personal') const activeTab = ref('personal')
const newCanvas = reactive({ const newCanvas = reactive({
name: '', name: '',
visibility: 'private', visibility: 'private',
groupId: '' groupId: ''
}) })
const openCreateModal = () => {
newCanvas.name = ''
newCanvas.visibility = 'private'
newCanvas.groupId = ''
showSettingsModal.value = false
showCreateModal.value = true
}
const showSettingsModal = ref(false) const showSettingsModal = ref(false)
const saving = ref(false) const saving = ref(false)
@@ -293,9 +301,9 @@ const filteredCanvases = computed(() => {
switch (activeTab.value) { switch (activeTab.value) {
case 'personal': 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': case 'team':
return canvases.value.filter(c => !!c.groupId) return canvases.value.filter(c => c.groupId && c.groupId !== '')
case 'public': case 'public':
return canvases.value.filter(c => c.isPublic) return canvases.value.filter(c => c.isPublic)
default: default:
+5 -6
View File
@@ -163,11 +163,9 @@ const columns = [
// Fetch all tasks for current user // Fetch all tasks for current user
const fetchTasks = async () => { const fetchTasks = async () => {
try { try {
const res = await fetch('/api/tasks', { const data = await $fetch('/api/tasks', {
headers: { Authorization: `Bearer ${auth.token.value}` } headers: { Authorization: `Bearer ${auth.token.value}` }
}) })
if (res.ok) {
const data = await res.json()
tasks.value = data.tasks || [] tasks.value = data.tasks || []
// Split tasks into columns // Split tasks into columns
@@ -177,7 +175,6 @@ const fetchTasks = async () => {
return taskState === col.id return taskState === col.id
}) })
}) })
}
} catch (e) { } catch (e) {
console.error('Failed to load tasks', e) console.error('Failed to load tasks', e)
} finally { } finally {
@@ -187,8 +184,10 @@ const fetchTasks = async () => {
const fetchCanvases = async () => { const fetchCanvases = async () => {
try { try {
const res = await $fetch('/api/canvas') const data = await $fetch('/api/canvas', {
availableCanvases.value = res.canvases || [] headers: { Authorization: `Bearer ${auth.token.value}` }
})
availableCanvases.value = data.canvases || []
if (availableCanvases.value.length > 0) { if (availableCanvases.value.length > 0) {
newTask.canvasId = availableCanvases.value[0].id newTask.canvasId = availableCanvases.value[0].id
} }
+2 -1
View File
@@ -34,7 +34,8 @@ export default defineEventHandler(async (event) => {
const fbUser: FirebaseUser = verifyRes.users[0] const fbUser: FirebaseUser = verifyRes.users[0]
const email = fbUser.email 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 userId = fbUser.localId
const user = await prisma.user.upsert({ const user = await prisma.user.upsert({
+13 -13
View File
@@ -5,28 +5,28 @@ export default defineEventHandler(async (event) => {
const adminUser = requireAuth(event) const adminUser = requireAuth(event)
const groupId = getRouterParam(event, 'id') const groupId = getRouterParam(event, 'id')
const body = await readBody(event) const body = await readBody(event)
const { email, role = 'member' } = body const { email, userId, role = 'member' } = body
if (!groupId || !email) { if (!groupId || (!email && !userId)) {
throw createError({ statusCode: 400, message: 'Group ID and Email are required' }) throw createError({ statusCode: 400, message: 'Group ID and either Email or User ID are required' })
} }
// 1. Проверяем, является ли текущий пользователь админом группы или глобальным админом // 1. Проверяем права
const group = await prisma.group.findUnique({ const group = await prisma.group.findUnique({ where: { id: groupId } })
where: { id: groupId }
})
if (!group || (group.ownerId !== adminUser.id && adminUser.role !== 'admin')) { if (!group || (group.ownerId !== adminUser.id && adminUser.role !== 'admin')) {
throw createError({ statusCode: 403, message: 'Only group owners or admins can add members' }) throw createError({ statusCode: 403, message: 'Only group owners or admins can add members' })
} }
// 2. Ищем пользователя по email // 2. Находим пользователя
const userToAdd = await prisma.user.findUnique({ let userToAdd = null
where: { email } if (userId) {
}) userToAdd = await prisma.user.findUnique({ where: { id: userId } })
} else if (email) {
userToAdd = await prisma.user.findUnique({ where: { email } })
}
if (!userToAdd) { if (!userToAdd) {
throw createError({ statusCode: 404, message: 'User with this email not found in system' }) throw createError({ statusCode: 404, message: 'User not found' })
} }
// 3. Добавляем в группу // 3. Добавляем в группу