forked from wangziqi/gongxue-base
Compare commits
6 Commits
302dbe0621
...
adf738288f
| Author | SHA1 | Date | |
|---|---|---|---|
| adf738288f | |||
| 585e955720 | |||
| eaca56617c | |||
| 0bdc8a067b | |||
| 490a337446 | |||
| 099cff7f00 |
@@ -37,7 +37,7 @@
|
||||
"@nestjs/jwt": "^11.0.2",
|
||||
"@nestjs/mapped-types": "^2.1.1",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.1.19",
|
||||
"@nestjs/platform-express": "^11.1.28",
|
||||
"@nestjs/schedule": "^6.1.3",
|
||||
"@nestjs/throttler": "^6.5.0",
|
||||
"@nestjs/typeorm": "^11.0.1",
|
||||
@@ -47,7 +47,7 @@
|
||||
"class-validator": "^0.15.1",
|
||||
"echarts": "^6.1.0",
|
||||
"exceljs": "^4.4.0",
|
||||
"multer": "^2.1.1",
|
||||
"multer": "^2.2.0",
|
||||
"mysql2": "^3.22.2",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
@@ -55,7 +55,7 @@
|
||||
"pdfkit": "^0.18.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "^0.3.28"
|
||||
"typeorm": "^0.3.31"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"better-sqlite3": "^12.9.0"
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import request from 'supertest';
|
||||
import type { Repository } from 'typeorm';
|
||||
import { AppModule } from '../app.module';
|
||||
import type { DingTalkAttendanceResult } from '../integration/dingtalk.service';
|
||||
import { DingTalkService } from '../integration/dingtalk.service';
|
||||
import {
|
||||
AttendanceRecord,
|
||||
Organization,
|
||||
Role,
|
||||
Student,
|
||||
StudentDingMapping,
|
||||
User,
|
||||
} from '../entities';
|
||||
import { createStudentImportTemplateWorkbook } from '../students/student-import';
|
||||
|
||||
const LESSON_DATE = new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone: 'Asia/Shanghai',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
}).format(new Date());
|
||||
const STUDENT_A_DING_ID = 'integration-student-a';
|
||||
const STUDENT_B_DING_ID = 'integration-student-b';
|
||||
|
||||
const auth = (token: string) => ({ Authorization: `Bearer ${token}` });
|
||||
|
||||
function chinaWeekDay(date: string): number {
|
||||
const day = new Date(`${date}T00:00:00+08:00`).getDay();
|
||||
return day === 0 ? 7 : day;
|
||||
}
|
||||
|
||||
function attendanceResult(
|
||||
userId: string,
|
||||
checkId: string,
|
||||
actualCheckTime: string,
|
||||
): DingTalkAttendanceResult {
|
||||
return {
|
||||
userId,
|
||||
userName: '',
|
||||
workDate: LESSON_DATE,
|
||||
timeResult: 'Normal',
|
||||
locationResult: 'Normal',
|
||||
planCheckTime: `${LESSON_DATE}T00:00:00+08:00`,
|
||||
actualCheckTime,
|
||||
checkId,
|
||||
checkType: 'OnDuty',
|
||||
sourceType: 'ATM',
|
||||
deviceName: '集成测试考勤机',
|
||||
deviceId: 'integration-device',
|
||||
};
|
||||
}
|
||||
|
||||
describe('attendance workflow integration', () => {
|
||||
let app: INestApplication;
|
||||
let adminToken: string;
|
||||
let teacherToken: string;
|
||||
let mockedPunches: DingTalkAttendanceResult[];
|
||||
const originalEnv = {
|
||||
DB_TYPE: process.env.DB_TYPE,
|
||||
DB_DATABASE: process.env.DB_DATABASE,
|
||||
DB_SYNCHRONIZE: process.env.DB_SYNCHRONIZE,
|
||||
SEED_DEV: process.env.SEED_DEV,
|
||||
ADMIN_PASSWORD: process.env.ADMIN_PASSWORD,
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
process.env.DB_TYPE = 'sqlite';
|
||||
process.env.DB_DATABASE = ':memory:';
|
||||
process.env.DB_SYNCHRONIZE = 'true';
|
||||
process.env.SEED_DEV = 'true';
|
||||
process.env.ADMIN_PASSWORD = 'admin123';
|
||||
|
||||
mockedPunches = [];
|
||||
const dingTalk = {
|
||||
fetchAttendanceResults: jest.fn(async () => mockedPunches),
|
||||
};
|
||||
|
||||
const moduleRef = await Test.createTestingModule({ imports: [AppModule] })
|
||||
.overrideProvider(DingTalkService)
|
||||
.useValue(dingTalk)
|
||||
.compile();
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
app.setGlobalPrefix('api');
|
||||
await app.init();
|
||||
|
||||
const login = await request(app.getHttpServer())
|
||||
.post('/api/auth/login')
|
||||
.send({ username: 'admin', password: 'admin123' })
|
||||
.expect(201);
|
||||
adminToken = login.body.access_token;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app?.close();
|
||||
for (const [key, value] of Object.entries(originalEnv)) {
|
||||
if (value === undefined) delete process.env[key];
|
||||
else process.env[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
it('imports students, builds a teacher class schedule, refreshes punches, and scopes reads', async () => {
|
||||
const roleRepo = app.get<Repository<Role>>(getRepositoryToken(Role));
|
||||
const userRepo = app.get<Repository<User>>(getRepositoryToken(User));
|
||||
const studentRepo = app.get<Repository<Student>>(getRepositoryToken(Student));
|
||||
const mappingRepo = app.get<Repository<StudentDingMapping>>(
|
||||
getRepositoryToken(StudentDingMapping),
|
||||
);
|
||||
const organizationRepo = app.get<Repository<Organization>>(getRepositoryToken(Organization));
|
||||
const attendanceRepo = app.get<Repository<AttendanceRecord>>(
|
||||
getRepositoryToken(AttendanceRecord),
|
||||
);
|
||||
|
||||
const teacherRole = await roleRepo.findOneByOrFail({ code: 'teacher' });
|
||||
const teacherCreate = await request(app.getHttpServer())
|
||||
.post('/api/rbac/users')
|
||||
.set(auth(adminToken))
|
||||
.send({
|
||||
username: 'integration-teacher',
|
||||
password: 'teacher123',
|
||||
name: '集成测试任课教师',
|
||||
roleIds: [teacherRole.id],
|
||||
})
|
||||
.expect(201);
|
||||
expect(teacherCreate.body.message).toBe('用户创建成功');
|
||||
|
||||
const teacher = await userRepo.findOneByOrFail({ username: 'integration-teacher' });
|
||||
const teacherId = teacher.id;
|
||||
const teacherLogin = await request(app.getHttpServer())
|
||||
.post('/api/auth/login')
|
||||
.send({ username: 'integration-teacher', password: 'teacher123' })
|
||||
.expect(201);
|
||||
teacherToken = teacherLogin.body.access_token;
|
||||
|
||||
const host = await organizationRepo.findOneByOrFail({ isHost: true, status: 'active' });
|
||||
const workbook = createStudentImportTemplateWorkbook();
|
||||
const sheet = workbook.getWorksheet('学生基础+档案+录取')!;
|
||||
sheet.spliceRows(2, 1);
|
||||
sheet.addRow({
|
||||
phone: '13800000001',
|
||||
name: '集成学生甲',
|
||||
studentNo: 'IT001',
|
||||
organization: host.name,
|
||||
});
|
||||
sheet.addRow({
|
||||
phone: '13800000002',
|
||||
name: '集成学生乙',
|
||||
studentNo: 'IT002',
|
||||
organization: host.name,
|
||||
});
|
||||
const workbookBuffer = Buffer.from(await workbook.xlsx.writeBuffer());
|
||||
|
||||
const importResult = await request(app.getHttpServer())
|
||||
.post('/api/students/import')
|
||||
.set(auth(adminToken))
|
||||
.attach('file', workbookBuffer, {
|
||||
filename: 'attendance-workflow-students.xlsx',
|
||||
contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
})
|
||||
.expect(201);
|
||||
expect(importResult.body).toMatchObject({ imported: 2, skipped: 0 });
|
||||
|
||||
const [studentA, studentB] = await Promise.all([
|
||||
studentRepo.findOneByOrFail({ phone: '13800000001' }),
|
||||
studentRepo.findOneByOrFail({ phone: '13800000002' }),
|
||||
]);
|
||||
await mappingRepo.save([
|
||||
mappingRepo.create({ dingUserId: STUDENT_A_DING_ID, studentId: studentA.id }),
|
||||
mappingRepo.create({ dingUserId: STUDENT_B_DING_ID, studentId: studentB.id }),
|
||||
]);
|
||||
|
||||
const classResult = await request(app.getHttpServer())
|
||||
.post('/api/classes')
|
||||
.set(auth(adminToken))
|
||||
.send({
|
||||
name: '集成考勤班',
|
||||
code: 'ATTENDANCE-INTEGRATION',
|
||||
classType: 'culture',
|
||||
status: 'active',
|
||||
startDate: LESSON_DATE,
|
||||
endDate: LESSON_DATE,
|
||||
})
|
||||
.expect(201);
|
||||
const classId = classResult.body.id;
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post(`/api/classes/${classId}/students`)
|
||||
.set(auth(adminToken))
|
||||
.send({ studentIds: [studentA.id, studentB.id] })
|
||||
.expect(201)
|
||||
.expect(({ body }) => expect(body).toMatchObject({ added: 2, skipped: 0 }));
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post(`/api/classes/${classId}/teachers`)
|
||||
.set(auth(adminToken))
|
||||
.send({ userId: teacherId, roleType: 'subject_teacher', subject: '语文' })
|
||||
.expect(201);
|
||||
|
||||
const classroomResult = await request(app.getHttpServer())
|
||||
.post('/api/classrooms')
|
||||
.set(auth(adminToken))
|
||||
.send({ name: '集成测试教室', building: '测试楼', floor: 1, capacity: 30, roomType: '小' })
|
||||
.expect(201);
|
||||
|
||||
const scheduleResult = await request(app.getHttpServer())
|
||||
.post('/api/class-schedules')
|
||||
.set(auth(adminToken))
|
||||
.send({
|
||||
classId,
|
||||
classroomId: classroomResult.body.id,
|
||||
weekDay: chinaWeekDay(LESSON_DATE),
|
||||
startTime: '00:00',
|
||||
endTime: '23:59',
|
||||
attendanceAdvanceMinutes: 0,
|
||||
startDate: LESSON_DATE,
|
||||
endDate: LESSON_DATE,
|
||||
subject: '语文',
|
||||
teacherId,
|
||||
scheduleType: 'INTERNAL',
|
||||
})
|
||||
.expect(201);
|
||||
const scheduleId = scheduleResult.body.id;
|
||||
|
||||
const initialPull = await request(app.getHttpServer())
|
||||
.post(`/api/attendance-lessons/schedules/${scheduleId}/pull`)
|
||||
.set(auth(teacherToken))
|
||||
.send({ date: LESSON_DATE })
|
||||
.expect(201);
|
||||
expect(initialPull.body.records).toHaveLength(2);
|
||||
expect(initialPull.body.records.map((record: AttendanceRecord) => record.status)).toEqual([
|
||||
'pending',
|
||||
'pending',
|
||||
]);
|
||||
|
||||
const studentARecord = initialPull.body.records.find(
|
||||
(record: AttendanceRecord) => record.studentId === studentA.id,
|
||||
);
|
||||
await request(app.getHttpServer())
|
||||
.put(`/api/attendance-records/${studentARecord.id}`)
|
||||
.set(auth(teacherToken))
|
||||
.send({ status: 'absent', remark: '教师本地覆盖' })
|
||||
.expect(200)
|
||||
.expect(({ body }) => expect(body).toMatchObject({ status: 'absent', source: 'manual' }));
|
||||
|
||||
mockedPunches = [
|
||||
attendanceResult(STUDENT_A_DING_ID, 'integration-check-a', `${LESSON_DATE}T01:00:00.000Z`),
|
||||
attendanceResult(STUDENT_B_DING_ID, 'integration-check-b', `${LESSON_DATE}T01:05:00.000Z`),
|
||||
];
|
||||
|
||||
const refreshed = await request(app.getHttpServer())
|
||||
.post(`/api/attendance-lessons/schedules/${scheduleId}/pull`)
|
||||
.set(auth(teacherToken))
|
||||
.send({ date: LESSON_DATE })
|
||||
.expect(201);
|
||||
expect(refreshed.body.records).toHaveLength(2);
|
||||
expect(
|
||||
refreshed.body.records.find((record: AttendanceRecord) => record.studentId === studentA.id),
|
||||
).toMatchObject({ status: 'absent', source: 'manual', remark: '教师本地覆盖' });
|
||||
expect(
|
||||
refreshed.body.records.find((record: AttendanceRecord) => record.studentId === studentB.id),
|
||||
).toMatchObject({ status: 'present', source: 'dingtalk', punchSource: 'ATM' });
|
||||
|
||||
const teacherRecords = await request(app.getHttpServer())
|
||||
.get(
|
||||
`/api/attendance-records?classId=${classId}&dateFrom=${LESSON_DATE}&dateTo=${LESSON_DATE}`,
|
||||
)
|
||||
.set(auth(teacherToken))
|
||||
.expect(200);
|
||||
expect(teacherRecords.body.list).toHaveLength(2);
|
||||
const teacherView: Array<Pick<AttendanceRecord, 'studentId' | 'status' | 'source'>> =
|
||||
teacherRecords.body.list
|
||||
.map((record: AttendanceRecord) => ({
|
||||
studentId: record.studentId,
|
||||
status: record.status,
|
||||
source: record.source,
|
||||
}))
|
||||
.sort((left, right) => left.studentId - right.studentId);
|
||||
expect(teacherView).toEqual([
|
||||
{ studentId: studentA.id, status: 'absent', source: 'manual' },
|
||||
{ studentId: studentB.id, status: 'present', source: 'dingtalk' },
|
||||
]);
|
||||
|
||||
const adminRecords = await request(app.getHttpServer())
|
||||
.get(
|
||||
`/api/attendance-records?classId=${classId}&dateFrom=${LESSON_DATE}&dateTo=${LESSON_DATE}`,
|
||||
)
|
||||
.set(auth(adminToken))
|
||||
.expect(200);
|
||||
expect(adminRecords.body.list).toHaveLength(2);
|
||||
expect(
|
||||
adminRecords.body.list
|
||||
.map((record: AttendanceRecord) => ({
|
||||
studentId: record.studentId,
|
||||
status: record.status,
|
||||
source: record.source,
|
||||
}))
|
||||
.sort(
|
||||
(left: Pick<AttendanceRecord, 'studentId'>, right: Pick<AttendanceRecord, 'studentId'>) =>
|
||||
left.studentId - right.studentId,
|
||||
),
|
||||
).toEqual(teacherView);
|
||||
|
||||
const unassignedClass = await request(app.getHttpServer())
|
||||
.post('/api/classes')
|
||||
.set(auth(adminToken))
|
||||
.send({
|
||||
name: '未分配教师班级',
|
||||
code: 'UNASSIGNED-INTEGRATION',
|
||||
classType: 'culture',
|
||||
status: 'active',
|
||||
})
|
||||
.expect(201);
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/attendance-records?classId=${unassignedClass.body.id}`)
|
||||
.set(auth(teacherToken))
|
||||
.expect(400);
|
||||
|
||||
const persisted = await attendanceRepo.find({
|
||||
where: { classId },
|
||||
order: { studentId: 'ASC' },
|
||||
});
|
||||
expect(persisted).toHaveLength(2);
|
||||
expect(persisted).toEqual([
|
||||
expect.objectContaining({ studentId: studentA.id, status: 'absent', source: 'manual' }),
|
||||
expect.objectContaining({ studentId: studentB.id, status: 'present', source: 'dingtalk' }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
138
package-lock.json
generated
138
package-lock.json
generated
@@ -65,7 +65,7 @@
|
||||
"@nestjs/jwt": "^11.0.2",
|
||||
"@nestjs/mapped-types": "^2.1.1",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.1.19",
|
||||
"@nestjs/platform-express": "^11.1.28",
|
||||
"@nestjs/schedule": "^6.1.3",
|
||||
"@nestjs/throttler": "^6.5.0",
|
||||
"@nestjs/typeorm": "^11.0.1",
|
||||
@@ -75,7 +75,7 @@
|
||||
"class-validator": "^0.15.1",
|
||||
"echarts": "^6.1.0",
|
||||
"exceljs": "^4.4.0",
|
||||
"multer": "^2.1.1",
|
||||
"multer": "^2.2.0",
|
||||
"mysql2": "^3.22.2",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
@@ -83,7 +83,7 @@
|
||||
"pdfkit": "^0.18.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"typeorm": "^0.3.28"
|
||||
"typeorm": "^0.3.31"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
@@ -2272,9 +2272,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/reporters/node_modules/brace-expansion": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.2.tgz",
|
||||
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3133,14 +3133,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express": {
|
||||
"version": "11.1.27",
|
||||
"resolved": "https://registry.npmmirror.com/@nestjs/platform-express/-/platform-express-11.1.27.tgz",
|
||||
"integrity": "sha512-0ZFhz6H6EdGh4xQVbUNwjoAwBuz73P7FvUAl67h9CTdMqQlJDaQYJApBv8pKfVZ1fGjMCbl0m9DcC6pXaZPWSQ==",
|
||||
"version": "11.1.28",
|
||||
"resolved": "https://registry.npmmirror.com/@nestjs/platform-express/-/platform-express-11.1.28.tgz",
|
||||
"integrity": "sha512-hU+9Sz4m+onHrR5AmelI59QKmY/Re546bPnygnpqqeQdHDiJpBgjWbL4t6Jr73CBpS60cpyng7WzjgphNB9iwA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "2.8.6",
|
||||
"express": "5.2.1",
|
||||
"multer": "2.1.1",
|
||||
"multer": "2.2.0",
|
||||
"path-to-regexp": "8.4.2",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
@@ -3153,68 +3153,6 @@
|
||||
"@nestjs/core": "^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/multer": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/multer/-/multer-2.1.1.tgz",
|
||||
"integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^1.6.0",
|
||||
"concat-stream": "^2.0.0",
|
||||
"type-is": "^1.6.18"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express/node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schedule": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@nestjs/schedule/-/schedule-6.1.3.tgz",
|
||||
@@ -7198,6 +7136,7 @@
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/ansis/-/ansis-4.2.0.tgz",
|
||||
"integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -7762,9 +7701,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.16.tgz",
|
||||
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@@ -10981,9 +10920,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-config/node_modules/brace-expansion": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.2.tgz",
|
||||
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -11506,9 +11445,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-runtime/node_modules/brace-expansion": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.2.tgz",
|
||||
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -14423,9 +14362,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/readdir-glob/node_modules/brace-expansion": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.2.tgz",
|
||||
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
@@ -16279,16 +16218,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typeorm": {
|
||||
"version": "0.3.30",
|
||||
"resolved": "https://registry.npmmirror.com/typeorm/-/typeorm-0.3.30.tgz",
|
||||
"integrity": "sha512-8T35PzjefOdqc2ZR9mwLQj0pUGp6lQhMbK2EvVMwJVJWlaoHm0v/Q6dThNOZkFchD+0yMg8gwjKM28ePiLSXSQ==",
|
||||
"version": "0.3.31",
|
||||
"resolved": "https://registry.npmmirror.com/typeorm/-/typeorm-0.3.31.tgz",
|
||||
"integrity": "sha512-6u9EFtdLBgHjnPm78NStVeM+I/1MolTzKykDDcydzKUkh6E++YS6XViU/fePJbvDvEGU4Xq34KOM/CLeer9I2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sqltools/formatter": "^1.2.5",
|
||||
"ansis": "^4.2.0",
|
||||
"ansis": "^4.3.1",
|
||||
"app-root-path": "^3.1.0",
|
||||
"buffer": "^6.0.3",
|
||||
"dayjs": "^1.11.20",
|
||||
"dayjs": "^1.11.21",
|
||||
"debug": "^4.4.3",
|
||||
"dedent": "^1.7.2",
|
||||
"dotenv": "^16.6.1",
|
||||
@@ -16298,7 +16237,7 @@
|
||||
"sql-highlight": "^6.1.0",
|
||||
"tslib": "^2.8.1",
|
||||
"uuid": "^11.1.1",
|
||||
"yargs": "^17.7.2"
|
||||
"yargs": "^17.7.3"
|
||||
},
|
||||
"bin": {
|
||||
"typeorm": "cli.js",
|
||||
@@ -16319,13 +16258,13 @@
|
||||
"mongodb": "^5.8.0 || ^6.0.0",
|
||||
"mssql": "^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0",
|
||||
"mysql2": "^2.2.5 || ^3.0.1",
|
||||
"oracledb": "^6.3.0",
|
||||
"oracledb": "^6.3.0 || ^7.0.0",
|
||||
"pg": "^8.5.1",
|
||||
"pg-native": "^3.0.0",
|
||||
"pg-query-stream": "^4.0.0",
|
||||
"redis": "^3.1.1 || ^4.0.0 || ^5.0.14",
|
||||
"sql.js": "^1.4.0",
|
||||
"sqlite3": "^5.0.3",
|
||||
"sqlite3": "^5.0.3 || ^6.0.0",
|
||||
"ts-node": "^10.7.0",
|
||||
"typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0"
|
||||
},
|
||||
@@ -16380,10 +16319,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/typeorm/node_modules/ansis": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/ansis/-/ansis-4.3.1.tgz",
|
||||
"integrity": "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/typeorm/node_modules/brace-expansion": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.1.tgz",
|
||||
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.2.tgz",
|
||||
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
|
||||
Reference in New Issue
Block a user