chore: fix lint issues, remove unused hasAvatar
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import Fastify from 'fastify'
|
||||
import jwt from '@fastify/jwt'
|
||||
import Fastify from 'fastify'
|
||||
import { afterAll, beforeEach, beforeAll, describe, expect, it } from 'vitest'
|
||||
import { prisma } from '../../lib/prisma.js'
|
||||
import { registerAuthRoutes } from '../auth.js'
|
||||
@@ -10,7 +10,9 @@ async function buildApp() {
|
||||
const app = Fastify({ logger: false })
|
||||
await app.register(jwt, { secret: JWT_SECRET })
|
||||
app.decorate('authenticate', async function (request, reply) {
|
||||
try { await request.jwtVerify() } catch {
|
||||
try {
|
||||
await request.jwtVerify()
|
||||
} catch {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
})
|
||||
@@ -36,7 +38,9 @@ describe('GET /api/me/auth-methods', () => {
|
||||
let app, user, token
|
||||
const email = `test-methods-${Date.now()}@example.com`
|
||||
|
||||
beforeAll(async () => { app = await buildApp() })
|
||||
beforeAll(async () => {
|
||||
app = await buildApp()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await prisma.notificationPreference.deleteMany({ where: { userId: user?.id } })
|
||||
await prisma.user.deleteMany({ where: { email } })
|
||||
@@ -53,7 +57,8 @@ describe('GET /api/me/auth-methods', () => {
|
||||
|
||||
it('returns methods for user without any method', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'GET', url: '/api/me/auth-methods',
|
||||
method: 'GET',
|
||||
url: '/api/me/auth-methods',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
expect(res.statusCode).toBe(200)
|
||||
@@ -66,7 +71,8 @@ describe('GET /api/me/auth-methods', () => {
|
||||
it('returns password as active after setting it', async () => {
|
||||
await prisma.user.update({ where: { id: user.id }, data: { passwordHash: 'hashed' } })
|
||||
const res = await app.inject({
|
||||
method: 'GET', url: '/api/me/auth-methods',
|
||||
method: 'GET',
|
||||
url: '/api/me/auth-methods',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
expect(JSON.parse(res.body).methods.find((m) => m.type === 'password').active).toBe(true)
|
||||
@@ -77,7 +83,9 @@ describe('POST /api/me/password', () => {
|
||||
let app, user, token
|
||||
const email = `test-set-pw-${Date.now()}@example.com`
|
||||
|
||||
beforeAll(async () => { app = await buildApp() })
|
||||
beforeAll(async () => {
|
||||
app = await buildApp()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await prisma.notificationPreference.deleteMany({ where: { userId: user?.id } })
|
||||
await prisma.user.deleteMany({ where: { email } })
|
||||
@@ -93,7 +101,8 @@ describe('POST /api/me/password', () => {
|
||||
|
||||
it('sets password', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/me/password',
|
||||
method: 'POST',
|
||||
url: '/api/me/password',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: { password: 'Test123!@' },
|
||||
})
|
||||
@@ -106,7 +115,8 @@ describe('POST /api/me/password', () => {
|
||||
it('rejects if password already set', async () => {
|
||||
await prisma.user.update({ where: { id: user.id }, data: { passwordHash: 'existing' } })
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/me/password',
|
||||
method: 'POST',
|
||||
url: '/api/me/password',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: { password: 'Test123!@' },
|
||||
})
|
||||
@@ -118,7 +128,9 @@ describe('DELETE /api/me/oauth/:provider', () => {
|
||||
let app, user, token
|
||||
const email = `test-unlink-${Date.now()}@example.com`
|
||||
|
||||
beforeAll(async () => { app = await buildApp() })
|
||||
beforeAll(async () => {
|
||||
app = await buildApp()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await prisma.oAuthAccount.deleteMany({ where: { user: { email } } })
|
||||
await prisma.notificationPreference.deleteMany({ where: { user: { email } } })
|
||||
@@ -135,7 +147,8 @@ describe('DELETE /api/me/oauth/:provider', () => {
|
||||
|
||||
it('returns 404 for non-linked provider', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'DELETE', url: '/api/me/oauth/vk',
|
||||
method: 'DELETE',
|
||||
url: '/api/me/oauth/vk',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
expect(res.statusCode).toBe(404)
|
||||
@@ -147,7 +160,8 @@ describe('DELETE /api/me/oauth/:provider', () => {
|
||||
data: { provider: 'vk', providerUserId: '123', userId: user.id },
|
||||
})
|
||||
const res = await app.inject({
|
||||
method: 'DELETE', url: '/api/me/oauth/vk',
|
||||
method: 'DELETE',
|
||||
url: '/api/me/oauth/vk',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
expect(res.statusCode).toBe(200)
|
||||
@@ -161,7 +175,8 @@ describe('DELETE /api/me/oauth/:provider', () => {
|
||||
data: { provider: 'vk', providerUserId: '123', userId: user.id },
|
||||
})
|
||||
const res = await app.inject({
|
||||
method: 'DELETE', url: '/api/me/oauth/vk',
|
||||
method: 'DELETE',
|
||||
url: '/api/me/oauth/vk',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
expect(res.statusCode).toBe(400)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Fastify from 'fastify'
|
||||
import jwt from '@fastify/jwt'
|
||||
import Fastify from 'fastify'
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'
|
||||
import { prisma } from '../../lib/prisma.js'
|
||||
import { registerAuthRoutes } from '../auth.js'
|
||||
@@ -26,8 +26,12 @@ async function buildApp() {
|
||||
|
||||
describe('POST /api/auth/register', () => {
|
||||
let app
|
||||
beforeAll(async () => { app = await buildApp() })
|
||||
afterAll(async () => { await app.close() })
|
||||
beforeAll(async () => {
|
||||
app = await buildApp()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await app.close()
|
||||
})
|
||||
afterEach(async () => {
|
||||
await prisma.authCode.deleteMany({ where: { email: TEST_EMAIL } })
|
||||
await prisma.notificationPreference.deleteMany({ where: { user: { email: TEST_EMAIL } } })
|
||||
@@ -48,11 +52,13 @@ describe('POST /api/auth/register', () => {
|
||||
|
||||
it('rejects duplicate email', async () => {
|
||||
await app.inject({
|
||||
method: 'POST', url: '/api/auth/register',
|
||||
method: 'POST',
|
||||
url: '/api/auth/register',
|
||||
payload: { email: TEST_EMAIL, password: 'Test123!@' },
|
||||
})
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/auth/register',
|
||||
method: 'POST',
|
||||
url: '/api/auth/register',
|
||||
payload: { email: TEST_EMAIL, password: 'Test123!@' },
|
||||
})
|
||||
expect(res.statusCode).toBe(409)
|
||||
@@ -60,7 +66,8 @@ describe('POST /api/auth/register', () => {
|
||||
|
||||
it('rejects weak password — too short', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/auth/register',
|
||||
method: 'POST',
|
||||
url: '/api/auth/register',
|
||||
payload: { email: TEST_EMAIL, password: 'Ab1!' },
|
||||
})
|
||||
expect(res.statusCode).toBe(400)
|
||||
@@ -70,7 +77,8 @@ describe('POST /api/auth/register', () => {
|
||||
|
||||
it('rejects weak password — no digit', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/auth/register',
|
||||
method: 'POST',
|
||||
url: '/api/auth/register',
|
||||
payload: { email: TEST_EMAIL, password: 'Abcdefgh!' },
|
||||
})
|
||||
expect(res.statusCode).toBe(400)
|
||||
@@ -79,7 +87,8 @@ describe('POST /api/auth/register', () => {
|
||||
|
||||
it('rejects weak password — no special char', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/auth/register',
|
||||
method: 'POST',
|
||||
url: '/api/auth/register',
|
||||
payload: { email: TEST_EMAIL, password: 'Abcdefg1' },
|
||||
})
|
||||
expect(res.statusCode).toBe(400)
|
||||
@@ -92,7 +101,8 @@ describe('POST /api/auth/login', () => {
|
||||
beforeAll(async () => {
|
||||
app = await buildApp()
|
||||
await app.inject({
|
||||
method: 'POST', url: '/api/auth/register',
|
||||
method: 'POST',
|
||||
url: '/api/auth/register',
|
||||
payload: { email: LOGIN_EMAIL, password: 'Test123!@' },
|
||||
})
|
||||
})
|
||||
@@ -106,7 +116,8 @@ describe('POST /api/auth/login', () => {
|
||||
|
||||
it('logs in with correct password', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/auth/login',
|
||||
method: 'POST',
|
||||
url: '/api/auth/login',
|
||||
payload: { email: LOGIN_EMAIL, password: 'Test123!@' },
|
||||
headers: { 'x-forwarded-for': '1.1.1.1' },
|
||||
})
|
||||
@@ -116,7 +127,8 @@ describe('POST /api/auth/login', () => {
|
||||
|
||||
it('rejects wrong password', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/auth/login',
|
||||
method: 'POST',
|
||||
url: '/api/auth/login',
|
||||
payload: { email: LOGIN_EMAIL, password: 'Wrong!!1!' },
|
||||
headers: { 'x-forwarded-for': '2.2.2.2' },
|
||||
})
|
||||
@@ -125,7 +137,8 @@ describe('POST /api/auth/login', () => {
|
||||
|
||||
it('rejects non-existent email', async () => {
|
||||
const res = await app.inject({
|
||||
method: 'POST', url: '/api/auth/login',
|
||||
method: 'POST',
|
||||
url: '/api/auth/login',
|
||||
payload: { email: 'nobody@nowhere.test', password: 'Test123!@' },
|
||||
headers: { 'x-forwarded-for': '3.3.3.3' },
|
||||
})
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { mapProductForApi, parseMaterialsInput, slugify } from './api/_product-helpers.js'
|
||||
import { registerAdminNotificationRoutes } from './api/admin/notifications.js'
|
||||
import { registerAdminProfileRoutes } from './api/admin-profile.js'
|
||||
import { registerAdminCategoryRoutes } from './api/admin-categories.js'
|
||||
import { registerAdminGalleryRoutes } from './api/admin-gallery.js'
|
||||
import { registerAdminOrderRoutes } from './api/admin-orders.js'
|
||||
import { registerAdminProductRoutes } from './api/admin-products.js'
|
||||
import { registerAdminProfileRoutes } from './api/admin-profile.js'
|
||||
import { registerAdminReviewRoutes } from './api/admin-reviews.js'
|
||||
import { registerAdminUserRoutes } from './api/admin-users.js'
|
||||
import { registerCatalogSliderRoutes } from './api/catalog-slider.js'
|
||||
|
||||
@@ -73,7 +73,9 @@ export async function registerAdminOrderRoutes(fastify) {
|
||||
const order = await prisma.order.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
user: { select: { id: true, email: true, displayName: true, avatar: true, avatarType: true, avatarStyle: true } },
|
||||
user: {
|
||||
select: { id: true, email: true, displayName: true, avatar: true, avatarType: true, avatarStyle: true },
|
||||
},
|
||||
items: true,
|
||||
messages: { orderBy: { createdAt: 'asc' } },
|
||||
},
|
||||
|
||||
@@ -87,7 +87,9 @@ export async function registerPublicReviewRoutes(fastify) {
|
||||
const total = await prisma.review.count({ where })
|
||||
const rawItems = await prisma.review.findMany({
|
||||
where,
|
||||
include: { user: { select: { email: true, displayName: true, avatar: true, avatarType: true, avatarStyle: true } } },
|
||||
include: {
|
||||
user: { select: { email: true, displayName: true, avatar: true, avatarType: true, avatarStyle: true } },
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
|
||||
@@ -105,10 +105,7 @@ export async function registerOAuthSocialRoutes(fastify) {
|
||||
if (!clientId || !clientSecret) return reply.code(503).send({ error: 'VK OAuth не настроен' })
|
||||
|
||||
const redirectUri = `${serverPublic}/api/auth/oauth/vk/callback`
|
||||
const state = fastify.jwt.sign(
|
||||
{ oauth: 'vk', action: 'link', userId: request.user.sub },
|
||||
{ expiresIn: '15m' },
|
||||
)
|
||||
const state = fastify.jwt.sign({ oauth: 'vk', action: 'link', userId: request.user.sub }, { expiresIn: '15m' })
|
||||
|
||||
const url = new URL('https://oauth.vk.com/authorize')
|
||||
url.searchParams.set('client_id', clientId)
|
||||
@@ -128,13 +125,15 @@ export async function registerOAuthSocialRoutes(fastify) {
|
||||
return oauthErrorRedirect(reply, String(query.error_description || query.error || 'ошибка VK'))
|
||||
}
|
||||
|
||||
let statePayload = null
|
||||
try {
|
||||
const raw = typeof query.state === 'string' ? query.state : ''
|
||||
statePayload = fastify.jwt.verify(raw || '')
|
||||
} catch {
|
||||
return oauthErrorRedirect(reply, 'Недействительный state OAuth')
|
||||
}
|
||||
const statePayload = (() => {
|
||||
try {
|
||||
const raw = typeof query.state === 'string' ? query.state : ''
|
||||
return fastify.jwt.verify(raw || '')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
})()
|
||||
if (!statePayload) return oauthErrorRedirect(reply, 'Недействительный state OAuth')
|
||||
|
||||
const code = typeof query.code === 'string' ? query.code.trim() : ''
|
||||
if (!code) return oauthErrorRedirect(reply, 'Не получен код от VK')
|
||||
@@ -212,10 +211,7 @@ export async function registerOAuthSocialRoutes(fastify) {
|
||||
if (!clientId) return reply.code(503).send({ error: 'Yandex OAuth не настроен' })
|
||||
|
||||
const redirectUri = `${serverPublic}/api/auth/oauth/yandex/callback`
|
||||
const state = fastify.jwt.sign(
|
||||
{ oauth: 'yandex', action: 'link', userId: request.user.sub },
|
||||
{ expiresIn: '15m' },
|
||||
)
|
||||
const state = fastify.jwt.sign({ oauth: 'yandex', action: 'link', userId: request.user.sub }, { expiresIn: '15m' })
|
||||
|
||||
const url = new URL('https://oauth.yandex.ru/authorize')
|
||||
url.searchParams.set('response_type', 'code')
|
||||
@@ -231,13 +227,15 @@ export async function registerOAuthSocialRoutes(fastify) {
|
||||
const query = request.query ?? {}
|
||||
if (query.error) return oauthErrorRedirect(reply, String(query.error))
|
||||
|
||||
let statePayload = null
|
||||
try {
|
||||
const raw = typeof query.state === 'string' ? query.state : ''
|
||||
statePayload = fastify.jwt.verify(raw || '')
|
||||
} catch {
|
||||
return oauthErrorRedirect(reply, 'Недействительный state OAuth')
|
||||
}
|
||||
const statePayload = (() => {
|
||||
try {
|
||||
const raw = typeof query.state === 'string' ? query.state : ''
|
||||
return fastify.jwt.verify(raw || '')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
})()
|
||||
if (!statePayload) return oauthErrorRedirect(reply, 'Недействительный state OAuth')
|
||||
|
||||
const code = typeof query.code === 'string' ? query.code.trim() : ''
|
||||
if (!code) return oauthErrorRedirect(reply, 'Не получен код от Яндекс')
|
||||
@@ -276,10 +274,7 @@ export async function registerOAuthSocialRoutes(fastify) {
|
||||
const yaUserId = String(info?.id || '')
|
||||
if (!yaUserId) return oauthErrorRedirect(reply, 'Не удалось получить профиль Yandex')
|
||||
|
||||
const emailGuess =
|
||||
(Array.isArray(info?.emails) && info.emails[0]) ||
|
||||
info?.default_email ||
|
||||
null
|
||||
const emailGuess = (Array.isArray(info?.emails) && info.emails[0]) || info?.default_email || null
|
||||
|
||||
if (!emailGuess) return oauthErrorRedirect(reply, 'no_email')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user