Добавлены модели и функциональность для управления администраторами, включая авторизацию через JWT, запросы и мутации для получения информации об администраторах и управления пользователями. Обновлены стили и логика работы с токенами в Apollo Client. Улучшен интерфейс взаимодействия с пользователем.

This commit is contained in:
Bivekich
2025-07-19 14:53:45 +03:00
parent f24c015021
commit 6287449521
26 changed files with 3931 additions and 19 deletions

View File

@ -991,4 +991,30 @@ export const UPDATE_EMPLOYEE_SCHEDULE = gql`
mutation UpdateEmployeeSchedule($input: UpdateScheduleInput!) {
updateEmployeeSchedule(input: $input)
}
`
// Админ мутации
export const ADMIN_LOGIN = gql`
mutation AdminLogin($username: String!, $password: String!) {
adminLogin(username: $username, password: $password) {
success
message
token
admin {
id
username
email
isActive
lastLogin
createdAt
updatedAt
}
}
}
`
export const ADMIN_LOGOUT = gql`
mutation AdminLogout {
adminLogout
}
`

View File

@ -567,4 +567,45 @@ export const GET_EMPLOYEE_SCHEDULE = gql`
}
}
}
`
// Админ запросы
export const ADMIN_ME = gql`
query AdminMe {
adminMe {
id
username
email
isActive
lastLogin
createdAt
updatedAt
}
}
`
export const ALL_USERS = gql`
query AllUsers($search: String, $limit: Int, $offset: Int) {
allUsers(search: $search, limit: $limit, offset: $offset) {
users {
id
phone
managerName
avatar
createdAt
updatedAt
organization {
id
inn
name
fullName
type
status
createdAt
}
}
total
hasMore
}
}
`

View File

@ -1,4 +1,5 @@
import jwt from 'jsonwebtoken'
import bcrypt from 'bcryptjs'
import { GraphQLError } from 'graphql'
import { GraphQLScalarType, Kind } from 'graphql'
import { prisma } from '@/lib/prisma'
@ -18,6 +19,10 @@ interface Context {
id: string
phone: string
}
admin?: {
id: string
username: string
}
}
interface CreateEmployeeInput {
@ -3736,4 +3741,167 @@ const logisticsMutations = {
resolvers.Mutation = {
...resolvers.Mutation,
...logisticsMutations
}
// Админ резолверы
const adminQueries = {
adminMe: async (_: unknown, __: unknown, context: Context) => {
if (!context.admin) {
throw new GraphQLError('Требуется авторизация администратора', {
extensions: { code: 'UNAUTHENTICATED' }
})
}
const admin = await prisma.admin.findUnique({
where: { id: context.admin.id }
})
if (!admin) {
throw new GraphQLError('Администратор не найден')
}
return admin
},
allUsers: async (_: unknown, args: { search?: string; limit?: number; offset?: number }, context: Context) => {
if (!context.admin) {
throw new GraphQLError('Требуется авторизация администратора', {
extensions: { code: 'UNAUTHENTICATED' }
})
}
const limit = args.limit || 50
const offset = args.offset || 0
// Строим условие поиска
const whereCondition: Prisma.UserWhereInput = args.search
? {
OR: [
{ phone: { contains: args.search, mode: 'insensitive' } },
{ managerName: { contains: args.search, mode: 'insensitive' } },
{
organization: {
OR: [
{ name: { contains: args.search, mode: 'insensitive' } },
{ fullName: { contains: args.search, mode: 'insensitive' } },
{ inn: { contains: args.search, mode: 'insensitive' } }
]
}
}
]
}
: {}
// Получаем пользователей с пагинацией
const [users, total] = await Promise.all([
prisma.user.findMany({
where: whereCondition,
include: {
organization: true
},
take: limit,
skip: offset,
orderBy: { createdAt: 'desc' }
}),
prisma.user.count({
where: whereCondition
})
])
return {
users,
total,
hasMore: offset + limit < total
}
}
}
const adminMutations = {
adminLogin: async (_: unknown, args: { username: string; password: string }) => {
try {
// Найти администратора
const admin = await prisma.admin.findUnique({
where: { username: args.username }
})
if (!admin) {
return {
success: false,
message: 'Неверные учетные данные'
}
}
// Проверить активность
if (!admin.isActive) {
return {
success: false,
message: 'Аккаунт заблокирован'
}
}
// Проверить пароль
const isPasswordValid = await bcrypt.compare(args.password, admin.password)
if (!isPasswordValid) {
return {
success: false,
message: 'Неверные учетные данные'
}
}
// Обновить время последнего входа
await prisma.admin.update({
where: { id: admin.id },
data: { lastLogin: new Date() }
})
// Создать токен
const token = jwt.sign(
{
adminId: admin.id,
username: admin.username,
type: 'admin'
},
process.env.JWT_SECRET!,
{ expiresIn: '24h' }
)
return {
success: true,
message: 'Успешная авторизация',
token,
admin: {
...admin,
password: undefined // Не возвращаем пароль
}
}
} catch (error) {
console.error('Admin login error:', error)
return {
success: false,
message: 'Ошибка авторизации'
}
}
},
adminLogout: async (_: unknown, __: unknown, context: Context) => {
if (!context.admin) {
throw new GraphQLError('Требуется авторизация администратора', {
extensions: { code: 'UNAUTHENTICATED' }
})
}
return true
}
}
// Добавляем админ запросы и мутации к основным резолверам
resolvers.Query = {
...resolvers.Query,
...adminQueries
}
resolvers.Mutation = {
...resolvers.Mutation,
...adminMutations
}

View File

@ -53,6 +53,10 @@ export const typeDefs = gql`
# Табель сотрудника за месяц
employeeSchedule(employeeId: ID!, year: Int!, month: Int!): [EmployeeSchedule!]!
# Админ запросы
adminMe: Admin
allUsers(search: String, limit: Int, offset: Int): UsersResponse!
}
type Mutation {
@ -128,6 +132,10 @@ export const typeDefs = gql`
updateEmployee(id: ID!, input: UpdateEmployeeInput!): EmployeeResponse!
deleteEmployee(id: ID!): Boolean!
updateEmployeeSchedule(input: UpdateScheduleInput!): Boolean!
# Админ мутации
adminLogin(username: String!, password: String!): AdminAuthResponse!
adminLogout: Boolean!
}
# Типы данных
@ -644,4 +652,28 @@ export const typeDefs = gql`
# JSON скаляр
scalar JSON
# Админ типы
type Admin {
id: ID!
username: String!
email: String
isActive: Boolean!
lastLogin: String
createdAt: String!
updatedAt: String!
}
type AdminAuthResponse {
success: Boolean!
message: String!
token: String
admin: Admin
}
type UsersResponse {
users: [User!]!
total: Int!
hasMore: Boolean!
}
`