109 lines
5.7 KiB
JavaScript
109 lines
5.7 KiB
JavaScript
import 'dotenv/config';
|
|
|
|
import { buildApp } from './build-app.js';
|
|
import { createDatabase } from '../infrastructure/database/client.js';
|
|
import { createPasswordResetNotifier } from '../infrastructure/email/password-reset-notifier.js';
|
|
import { createRedisConnection } from '../infrastructure/redis/client.js';
|
|
import { createAuthenticationService } from '../modules/auth/services/authentication-service.js';
|
|
import { createMfaService } from '../modules/auth/services/mfa-service.js';
|
|
import { createSecretProtector } from '../modules/auth/domain/secret-protection.js';
|
|
import { createMfaMethodRepository } from '../modules/auth/repositories/mfa-method-repository.js';
|
|
import { createSecurityAuditRepository } from '../modules/auth/repositories/security-audit-repository.js';
|
|
import { createSessionRepository } from '../modules/auth/repositories/session-repository.js';
|
|
import { createUserRepository } from '../modules/auth/repositories/user-repository.js';
|
|
import { createPasswordResetRepository } from '../modules/auth/repositories/password-reset-repository.js';
|
|
import { createRedisLoginRateLimiter } from '../modules/auth/services/login-rate-limiter.js';
|
|
import { createPasswordRecoveryService } from '../modules/auth/services/password-recovery-service.js';
|
|
import { createInventoryItemRepository } from '../modules/inventory/repositories/inventory-item-repository.js';
|
|
import { createInventoryItemService } from '../modules/inventory/services/inventory-item-service.js';
|
|
import { createPublicInventoryService } from '../modules/inventory/services/public-inventory-service.js';
|
|
import { createInventoryImageRepository } from '../modules/inventory/repositories/inventory-image-repository.js';
|
|
import { createInventoryImageService } from '../modules/inventory/services/inventory-image-service.js';
|
|
import { createLocalImageStorage } from '../infrastructure/images/local-image-storage.js';
|
|
import { createConditionRepository } from '../modules/inventory/repositories/condition-repository.js';
|
|
import { createListingRepository } from '../modules/listings/repositories/listing-repository.js';
|
|
import { createListingService } from '../modules/listings/services/listing-service.js';
|
|
import { createPublicListingService } from '../modules/listings/services/public-listing-service.js';
|
|
import { createProductRepository } from '../modules/products/repositories/product-repository.js';
|
|
import { createProductService } from '../modules/products/services/product-service.js';
|
|
import { createPublicProductService } from '../modules/products/services/public-product-service.js';
|
|
import { createSearchService } from '../modules/search/services/search-service.js';
|
|
|
|
function parsePort(value) {
|
|
const port = Number.parseInt(value ?? '3000', 10);
|
|
|
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
throw new Error('API_PORT must be an integer between 1 and 65535.');
|
|
}
|
|
|
|
return port;
|
|
}
|
|
|
|
async function startServer() {
|
|
const database = createDatabase();
|
|
const redis = await createRedisConnection();
|
|
const productRepository = createProductRepository(database.db);
|
|
const inventoryItemRepository = createInventoryItemRepository(database.db);
|
|
const inventoryImageRepository = createInventoryImageRepository(database.db);
|
|
const listingRepository = createListingRepository(database.db);
|
|
const conditionRepository = createConditionRepository(database.db);
|
|
const userRepository = createUserRepository(database.db);
|
|
const sessionRepository = createSessionRepository(database.db);
|
|
const passwordResetRepository = createPasswordResetRepository(database.db);
|
|
const mfaMethodRepository = createMfaMethodRepository(database.db);
|
|
const securityAuditRepository = createSecurityAuditRepository(database.db);
|
|
const passwordResetNotifier = createPasswordResetNotifier();
|
|
const authenticationService = createAuthenticationService({
|
|
mfaService,
|
|
sessionRepository,
|
|
securityAuditRepository,
|
|
userRepository,
|
|
});
|
|
const app = buildApp({
|
|
authenticationService,
|
|
authenticateAdmin: (request) =>
|
|
authenticationService.authenticate(request.cookies.pokemon_inventory_session),
|
|
authorizeUnsafeRequest: (sessionToken, csrfToken) =>
|
|
authenticationService.authorizeUnsafeRequest(sessionToken, csrfToken),
|
|
isProduction: process.env.NODE_ENV === 'production',
|
|
loginRateLimiter: createRedisLoginRateLimiter({ redisClient: redis.client }),
|
|
mfaService,
|
|
passwordRecoveryService: createPasswordRecoveryService({
|
|
passwordResetNotifier,
|
|
passwordResetRepository,
|
|
securityAuditRepository,
|
|
userRepository,
|
|
}),
|
|
passwordResetRateLimiter: createRedisLoginRateLimiter({
|
|
keyPrefix: 'pokemon-inventory:auth:password-reset:',
|
|
redisClient: redis.client,
|
|
}),
|
|
inventoryItemService: createInventoryItemService({
|
|
conditionRepository,
|
|
inventoryItemRepository,
|
|
productRepository,
|
|
}),
|
|
publicInventoryService: createPublicInventoryService({ inventoryItemRepository }),
|
|
inventoryImageService: createInventoryImageService({
|
|
imageStorage: createLocalImageStorage(process.env.IMAGE_STORAGE_ROOT ?? '.local/image-storage'),
|
|
inventoryImageRepository,
|
|
}),
|
|
listingService: createListingService({ listingRepository, inventoryImageRepository }),
|
|
publicListingService: createPublicListingService({ listingRepository }),
|
|
productService: createProductService({ productRepository }),
|
|
publicProductService: createPublicProductService({ productRepository }),
|
|
searchService: createSearchService({ listingRepository }),
|
|
});
|
|
|
|
app.addHook('onClose', async () => {
|
|
await database.close();
|
|
await redis.close();
|
|
});
|
|
|
|
await app.listen({ host: '0.0.0.0', port: parsePort(process.env.API_PORT) });
|
|
}
|
|
|
|
startServer().catch((error) => {
|
|
console.error(error);
|
|
process.exitCode = 1;
|
|
}); |