chore: eslint — убрать 2 error (unused), журнал A1–A4 проверки
Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Database Migration Script
|
||||||
|
* Executes SQL migration files in order
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync, readdirSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import pg from 'pg';
|
||||||
|
|
||||||
|
const { Pool } = pg;
|
||||||
|
|
||||||
|
// Database configuration
|
||||||
|
const dbConfig = {
|
||||||
|
host: process.env.DB_HOST || 'localhost',
|
||||||
|
port: parseInt(process.env.DB_PORT || '5433', 10),
|
||||||
|
database: process.env.DB_NAME || 'clinic_tests',
|
||||||
|
user: process.env.DB_USER || 'developer',
|
||||||
|
password: process.env.DB_PASSWORD || 'dev_password',
|
||||||
|
};
|
||||||
|
|
||||||
|
const MIGRATIONS_DIR = join(process.cwd(), 'src', 'db', 'migrations');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of migration files sorted by name
|
||||||
|
*/
|
||||||
|
function getMigrationFiles() {
|
||||||
|
const files = readdirSync(MIGRATIONS_DIR)
|
||||||
|
.filter((file) => file.endsWith('.sql'))
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create migrations tracking table if not exists
|
||||||
|
*/
|
||||||
|
async function ensureMigrationsTable(pool) {
|
||||||
|
await pool.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
executed_at TIMESTAMP DEFAULT NOW()
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of already executed migrations
|
||||||
|
*/
|
||||||
|
async function getExecutedMigrations(pool) {
|
||||||
|
const result = await pool.query('SELECT name FROM migrations ORDER BY name');
|
||||||
|
return result.rows.map((row) => row.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a single migration file
|
||||||
|
*/
|
||||||
|
async function executeMigration(pool, filename) {
|
||||||
|
const filePath = join(MIGRATIONS_DIR, filename);
|
||||||
|
const sql = readFileSync(filePath, 'utf-8');
|
||||||
|
|
||||||
|
console.log(`Executing migration: ${filename}`);
|
||||||
|
|
||||||
|
await pool.query('BEGIN');
|
||||||
|
try {
|
||||||
|
await pool.query(sql);
|
||||||
|
await pool.query(
|
||||||
|
'INSERT INTO migrations (name) VALUES ($1)',
|
||||||
|
[filename]
|
||||||
|
);
|
||||||
|
await pool.query('COMMIT');
|
||||||
|
console.log(`✓ Migration ${filename} completed successfully`);
|
||||||
|
} catch (error) {
|
||||||
|
await pool.query('ROLLBACK');
|
||||||
|
console.error(`✗ Migration ${filename} failed:`, error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main migration function
|
||||||
|
*/
|
||||||
|
async function migrate() {
|
||||||
|
const pool = new Pool(dbConfig);
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Connecting to database...');
|
||||||
|
await pool.connect();
|
||||||
|
console.log('Connected to database\n');
|
||||||
|
|
||||||
|
// Ensure migrations table exists
|
||||||
|
await ensureMigrationsTable(pool);
|
||||||
|
|
||||||
|
// Get migration files and already executed migrations
|
||||||
|
const migrationFiles = getMigrationFiles();
|
||||||
|
const executedMigrations = await getExecutedMigrations(pool);
|
||||||
|
|
||||||
|
console.log(`Found ${migrationFiles.length} migration file(s)`);
|
||||||
|
console.log(`Already executed: ${executedMigrations.length} migration(s)\n`);
|
||||||
|
|
||||||
|
// Execute pending migrations
|
||||||
|
const pendingMigrations = migrationFiles.filter(
|
||||||
|
(file) => !executedMigrations.includes(file)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pendingMigrations.length === 0) {
|
||||||
|
console.log('All migrations already executed.');
|
||||||
|
} else {
|
||||||
|
console.log(`Pending migrations: ${pendingMigrations.length}\n`);
|
||||||
|
|
||||||
|
for (const filename of pendingMigrations) {
|
||||||
|
await executeMigration(pool, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n✓ Successfully executed ${pendingMigrations.length} migration(s)`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('\n✗ Migration failed:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
await pool.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run migrations if this script is executed directly
|
||||||
|
migrate();
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
import cookieParser from 'cookie-parser';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
import authRoutes from './routes/auth.js';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.PORT || 3001;
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(cors({
|
||||||
|
origin: process.env.NODE_ENV === 'production'
|
||||||
|
? process.env.FRONTEND_URL
|
||||||
|
: ['http://localhost:5173', 'http://localhost:3000'],
|
||||||
|
credentials: true,
|
||||||
|
}));
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(cookieParser());
|
||||||
|
|
||||||
|
// Mount auth routes
|
||||||
|
app.use('/api/auth', authRoutes);
|
||||||
|
|
||||||
|
// Health check route
|
||||||
|
app.get('/api/health', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
status: 'ok',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
message: 'Server is running',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Error handling middleware
|
||||||
|
app.use((err, req, res, _next) => {
|
||||||
|
console.error('Error:', err);
|
||||||
|
res.status(err.status || 500).json({
|
||||||
|
error: err.message || 'Internal Server Error',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 404 handler
|
||||||
|
app.use((req, res) => {
|
||||||
|
res.status(404).json({ error: 'Not found' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server is running on port ${PORT}`);
|
||||||
|
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
|
||||||
|
});
|
||||||
@@ -22,10 +22,10 @@
|
|||||||
|
|
||||||
| № | Что проверено | Статус | Дата |
|
| № | Что проверено | Статус | Дата |
|
||||||
|---|----------------|--------|------|
|
|---|----------------|--------|------|
|
||||||
| A1 | В проекте есть миграция базы: связь версий «родитель» (`parent_id`) и правило «только одна активная версия на тест» | [ ] | |
|
| A1 | В проекте есть миграция базы: связь версий «родитель» (`parent_id`) и правило «только одна активная версия на тест» | [x] `002_…sql` | 2026-04-24 |
|
||||||
| A2 | Линтер (`npm run lint`) без **новых** ошибок в добавленных файлах; в проекте есть старые замечания линтера | частично | 2026-04-24 |
|
| A2 | Линтер (`npm run lint`): **0 errors**; остаются **warnings** `no-console` в существующих файлах | готово (errors) | 2026-04-24 |
|
||||||
| A3 | Автотесты: функция «есть ли уже хотя бы одна попытка по этому тесту» (`npm test`) | [x] готово | 2026-04-23 |
|
| A3 | Автотесты: функция «есть ли уже хотя бы одна попытка по этому тесту» (`npm test`) | [x] готово | 2026-04-23 |
|
||||||
| A4 | Запрос «здоров ли сервер» по адресу `/api/health` при запущенном backend | [ ] | |
|
| A4 | Запрос «здоров ли сервер» по адресу `/api/health` при запущенном backend | [x] `{"status":"ok"}` | 2026-04-24 |
|
||||||
|
|
||||||
**Техническая заметка:** реализация `hasAnyAttemptForTest` в `backend/src/services/testChainService.js`, тесты в `testChainService.test.js`.
|
**Техническая заметка:** реализация `hasAnyAttemptForTest` в `backend/src/services/testChainService.js`, тесты в `testChainService.test.js`.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user