Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 7 additions & 54 deletions src/di/container.ts → src/app/integrations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { container } from 'tsyringe';
import { EmailService } from '@/common/services/email';
import { FileStorageService } from '@/common/services/file-storage';
export { type Dependencies, configureDependencies };

import env from '@/app/environment';
import { Postgres, defaultPoolSettings } from '@/lib/postgres';
import { Mongo } from '@/lib/mongo';
Expand All @@ -12,65 +11,17 @@ import { ServerApiVersion } from 'mongodb';
import * as eventStore from '@/app/eventStore';
import { PostgresEventStore, WithEventStore } from '@/app/eventStore';
import { schemas } from '@/app/events';
import { Services } from '@/app/services';
import { Services, initializeServices } from '@/app/services';
import { Repositories, initializeRepositories } from '@/app/projections';

function registerEnvironmentVariables() {
const postgresConnectionString =
`postgresql://${env.EVENT_STORE_USER}:${env.EVENT_STORE_PASSWORD}@` +
`${env.EVENT_STORE_HOST}:${env.EVENT_STORE_PORT}/` +
`${env.EVENT_STORE_DATABASE_NAME}`;
container.register('postgresConnectionString', {
useValue: postgresConnectionString,
});
container.register('eventStoreTable', {
useValue: env.EVENT_STORE_CREATE_TABLE_WITH_NAME,
});
container.register('eventStoreDatabaseName', {
useValue: env.EVENT_STORE_DATABASE_NAME,
});
container.register('eventStoreCreateReplicationUserWithUsername', {
useValue: env.EVENT_STORE_CREATE_REPLICATION_USER_WITH_USERNAME,
});
container.register('eventStoreCreateReplicationUserWithPassword', {
useValue: env.EVENT_STORE_CREATE_REPLICATION_USER_WITH_PASSWORD,
});
container.register('eventStoreCreateReplicationPublication', {
useValue: env.EVENT_STORE_CREATE_REPLICATION_PUBLICATION,
});

const mongoConnectionString =
`mongodb://${env.MONGODB_PROJECTION_DATABASE_USERNAME}:${env.MONGODB_PROJECTION_DATABASE_PASSWORD}@` +
`${env.MONGODB_PROJECTION_HOST}:${env.MONGODB_PROJECTION_PORT}/` +
`${env.MONGODB_PROJECTION_DATABASE_NAME}` +
'?serverSelectionTimeoutMS=10000&connectTimeoutMS=10000&authSource=admin';
const mongoDatabaseName = env.MONGODB_PROJECTION_DATABASE_NAME;
container.register('mongoConnectionString', {
useValue: mongoConnectionString,
});
container.register('mongoDatabaseName', { useValue: mongoDatabaseName });
}

function registerSingletons() {
// common/services
container.registerSingleton(EmailService);
container.registerSingleton(FileStorageService);
}

function registerScopedServices() {}

type Dependencies = {
withEventStore: WithEventStore;
withProjectionStore: WithProjectionStore;
services: Services;
repositories: Repositories;
};

export async function configureDependencies(): Promise<Dependencies> {
registerEnvironmentVariables();
registerSingletons();
registerScopedServices();

async function configureDependencies(): Promise<Dependencies> {
const postgres = new Postgres({
user: env.EVENT_STORE_USER,
password: env.EVENT_STORE_PASSWORD,
Expand Down Expand Up @@ -127,10 +78,12 @@ export async function configureDependencies(): Promise<Dependencies> {
initializeRepositories(new MongoProjectionStore(t)),
);

const services = initializeServices();

return {
withEventStore,
withProjectionStore,
services: {},
services,
repositories,
};
}
6 changes: 0 additions & 6 deletions src/app/services.ts

This file was deleted.

26 changes: 6 additions & 20 deletions src/common/services/email.ts → src/app/services/email.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
export { EmailService, type EmailServiceConfig };

import nodemailer from 'nodemailer';
import { log } from '@/common/util/Logger';
import env from '@/app/environment';

export interface EmailOptions {
interface EmailOptions {
to: string | string[];
subject: string;
text?: string;
html?: string;
from?: string;
}

export interface EmailServiceConfig {
interface EmailServiceConfig {
host: string;
port: number;
secure: boolean;
Expand All @@ -21,12 +22,10 @@ export interface EmailServiceConfig {
defaultFrom?: string;
}

export class EmailService {
class EmailService {
private transporter: nodemailer.Transporter;
private config: EmailServiceConfig;

constructor(config?: EmailServiceConfig) {
this.config = config || this.getRequiredConfig();
constructor(private config: EmailServiceConfig) {
this.transporter = nodemailer.createTransport({
host: this.config.host,
port: this.config.port,
Expand All @@ -41,19 +40,6 @@ export class EmailService {
});
}

private getRequiredConfig(): EmailServiceConfig {
return {
host: env.SMTP_HOST,
port: env.SMTP_PORT,
secure: true,
auth: {
user: env.SMTP_USERNAME,
pass: env.SMTP_PASSWORD,
},
defaultFrom: env.SMTP_FROM_EMAIL,
};
}

async sendEmail(options: EmailOptions): Promise<void> {
try {
const mailOptions = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
export { FileStorageService, type FileStorageServiceConfig };

import * as Minio from 'minio';
import { Readable } from 'stream';
import { log } from '@/common/util/Logger';
import env from '@/app/environment';

export interface FileStorageOptions {
interface FileStorageOptions {
bucketName: string;
objectName: string;
stream: string | Buffer | Readable;
size?: number;
metaData?: Minio.ItemBucketMetadata;
}

export interface FileStorageServiceConfig {
interface FileStorageServiceConfig {
endPoint: string;
port: number;
useSSL: boolean;
Expand All @@ -20,23 +21,21 @@ export interface FileStorageServiceConfig {
region?: string;
}

export interface FileStorageResult {
interface FileStorageResult {
etag?: string;
objectName: string;
bucketName: string;
}

export interface FileDownloadResult {
interface FileDownloadResult {
stream: Readable;
stat: Minio.BucketItemStat;
}

export class FileStorageService {
class FileStorageService {
private client: Minio.Client;
private config: FileStorageServiceConfig;

constructor(config?: FileStorageServiceConfig) {
this.config = config || this.getRequiredConfig();
constructor(private config: FileStorageServiceConfig) {
this.client = new Minio.Client({
endPoint: this.config.endPoint,
port: this.config.port,
Expand All @@ -53,24 +52,6 @@ export class FileStorageService {
});
}

private getRequiredConfig(): FileStorageServiceConfig {
const endpointUrl = env.S3_ENDPOINT_URL;
const url = new URL(endpointUrl);

return {
endPoint: url.hostname,
port: url.port
? parseInt(url.port)
: url.protocol === 'https:'
? 443
: 80,
useSSL: url.protocol === 'https:',
accessKey: env.S3_ACCESS_KEY,
secretKey: env.S3_SECRET_KEY,
region: env.S3_REGION,
};
}

async createBucket(bucketName: string, region?: string): Promise<void> {
try {
const exists = await this.client.bucketExists(bucketName);
Expand Down
41 changes: 41 additions & 0 deletions src/app/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
All application services
*/
export { type Services, initializeServices };

import { EmailService } from '@/app/services/email';
import { FileStorageService } from '@/app/services/file-storage';
import env from '@/app/environment';

type Services = {
fileStorage: FileStorageService;
email: EmailService;
};

function initializeServices(): Services {
const url = new URL(env.S3_ENDPOINT_URL);
return {
fileStorage: new FileStorageService({
endPoint: url.hostname,
port: url.port
? parseInt(url.port)
: url.protocol === 'https:'
? 443
: 80,
useSSL: url.protocol === 'https:',
accessKey: env.S3_ACCESS_KEY,
secretKey: env.S3_SECRET_KEY,
region: env.S3_REGION,
}),
email: new EmailService({
host: env.SMTP_HOST,
port: env.SMTP_PORT,
secure: true,
auth: {
user: env.SMTP_USERNAME,
pass: env.SMTP_PASSWORD,
},
defaultFrom: env.SMTP_FROM_EMAIL,
}),
};
}
2 changes: 0 additions & 2 deletions src/di/index.ts

This file was deleted.

24 changes: 0 additions & 24 deletions src/di/scopedContainer.ts

This file was deleted.

6 changes: 1 addition & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import 'tsconfig-paths/register'; // enable absolute paths
import 'reflect-metadata';
import express from 'express';
import { configureDependencies } from '@/di/container';
import { scopedContainer } from '@/di/scopedContainer';
import { configureDependencies } from '@/app/integrations';
import { log } from '@/common/util/Logger';
import { handleCommand, CommandController } from '@/app/handleCommand';
import { Event } from '@/lib/eventSourcing/event';
Expand All @@ -27,9 +26,6 @@ async function main() {
const app = express();
app.use(express.json());

// Add scoped container middleware
app.use(scopedContainer);

const command = <T>(endpoint: string, controller: CommandController<T>) =>
app.post(
endpoint,
Expand Down