Добавлены модели и функциональность для управления администраторами, включая авторизацию через JWT, запросы и мутации для получения информации об администраторах и управления пользователями. Обновлены стили и логика работы с токенами в Apollo Client. Улучшен интерфейс взаимодействия с пользователем.
This commit is contained in:
@ -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
|
||||
}
|
||||
`
|
@ -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
|
||||
}
|
||||
}
|
||||
`
|
@ -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
|
||||
}
|
@ -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!
|
||||
}
|
||||
`
|
Reference in New Issue
Block a user