mirror of
https://github.com/hexastack/hexabot
synced 2024-11-24 04:53:41 +00:00
commit
ced9f0538c
11
Makefile
11
Makefile
@ -9,7 +9,10 @@ define add_service
|
||||
COMPOSE_FILES += -f ./docker/docker-compose.$(1).prod.yml
|
||||
endif
|
||||
else
|
||||
COMPOSE_FILES += -f ./docker/docker-compose.$(1).yml -f ./docker/docker-compose.$(1).dev.yml
|
||||
COMPOSE_FILES += -f ./docker/docker-compose.$(1).yml
|
||||
ifneq ($(wildcard ./docker/docker-compose.$(1).dev.yml),)
|
||||
COMPOSE_FILES += -f ./docker/docker-compose.$(1).dev.yml
|
||||
endif
|
||||
endif
|
||||
endef
|
||||
|
||||
@ -23,6 +26,10 @@ ifneq ($(NLU),)
|
||||
$(eval $(call add_service,nlu))
|
||||
endif
|
||||
|
||||
ifneq ($(SMTP4DEV),)
|
||||
$(eval $(call add_service,smtp4dev))
|
||||
endif
|
||||
|
||||
# Ensure .env file exists and matches .env.example
|
||||
check-env:
|
||||
@if [ ! -f "./docker/.env" ]; then \
|
||||
@ -48,4 +55,4 @@ destroy: check-env
|
||||
docker compose $(COMPOSE_FILES) down -v
|
||||
|
||||
migrate-up:
|
||||
docker-compose $(COMPOSE_FILES) up --no-deps -d database-init
|
||||
docker-compose $(COMPOSE_FILES) up --no-deps -d database-init
|
||||
|
@ -57,24 +57,28 @@ const i18nOptions: I18nOptions = {
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MailerModule.forRoot({
|
||||
transport: new SMTPTransport({
|
||||
...config.emails.smtp,
|
||||
logger: true,
|
||||
}),
|
||||
template: {
|
||||
adapter: new MjmlAdapter('ejs', { inlineCssEnabled: false }),
|
||||
dir: './src/templates',
|
||||
options: {
|
||||
context: {
|
||||
appName: config.parameters.appName,
|
||||
appUrl: config.parameters.appUrl,
|
||||
// TODO: add i18n support
|
||||
},
|
||||
},
|
||||
},
|
||||
defaults: { from: config.parameters.email.main },
|
||||
}),
|
||||
...(config.emails.isEnabled
|
||||
? [
|
||||
MailerModule.forRoot({
|
||||
transport: new SMTPTransport({
|
||||
...config.emails.smtp,
|
||||
logger: true,
|
||||
debug: false,
|
||||
}),
|
||||
template: {
|
||||
adapter: new MjmlAdapter('ejs', { inlineCssEnabled: false }),
|
||||
dir: './src/templates',
|
||||
options: {
|
||||
context: {
|
||||
appName: config.parameters.appName,
|
||||
appUrl: config.parameters.appUrl,
|
||||
},
|
||||
},
|
||||
},
|
||||
defaults: { from: config.emails.from },
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
MongooseModule.forRoot(config.mongo.uri, {
|
||||
dbName: config.mongo.dbName,
|
||||
connectionFactory: (connection) => {
|
||||
|
@ -8,19 +8,14 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
|
||||
import { ExtendedI18nService } from './extended-i18n.service';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
constructor(
|
||||
private readonly i18n: ExtendedI18nService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
constructor(private readonly i18n: ExtendedI18nService) {}
|
||||
|
||||
getHello(): string {
|
||||
this.eventEmitter.emit('hook:i18n:refresh', []);
|
||||
return this.i18n.t('Welcome');
|
||||
return this.i18n.t('welcome', { lang: 'en' });
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,21 @@
|
||||
{
|
||||
"invitation_subject": "[Hexabot] Sign-Up Invitation",
|
||||
"account_confirmation_subject": "[Hexabot] Account Confirmation",
|
||||
"password_reset_subject": "[Hexabot] Password Reset",
|
||||
"welcome": "Welcome",
|
||||
"hi": "Hi",
|
||||
"invitation_to_join": "Invitation to join",
|
||||
"invitation_for_account_creation": "You have been invited to create a",
|
||||
"account": "account",
|
||||
"create_account:": "Click on the button below to create your account:",
|
||||
"create_account": "Click on the button below to create your account:",
|
||||
"registration_failed": "Registration failed! Either email address or invitation token is invalid.",
|
||||
"create_account_button": "Click on the button below to create your account",
|
||||
"join": "Join",
|
||||
"account_successfully_created_confirm_password": "You have successfully created an account but you will need to confirm your email address.",
|
||||
"confirm_account:": "Click on the button below in order to confirm your account:",
|
||||
"confirm_account": "Click on the button below in order to confirm your account:",
|
||||
"password_reset_request": "A request to reset the password for your account has been made.",
|
||||
"reset": "Reset",
|
||||
"click_to_reset_password:": "Click on the button below in order to set a new password:",
|
||||
"click_to_reset_password": "Click on the button below in order to set a new password:",
|
||||
"confirm": "Confirm",
|
||||
"best_regards": "Best Regards,"
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
{
|
||||
"invitation_subject": "[Hexabot] Invitation à s'inscrire",
|
||||
"account_confirmation_subject": "[Hexabot] Confirmation de compte",
|
||||
"password_reset_subject": "[Hexabot] Réinitialisation du mot de passe",
|
||||
"welcome": "Bienvenue",
|
||||
"hi": "Bonjour",
|
||||
"invitation_to_join": "Invitation",
|
||||
|
@ -13,7 +13,7 @@ import { Config } from './types';
|
||||
|
||||
export const config: Config = {
|
||||
i18n: {
|
||||
translationFilename: process.env.I18N_TRANSLATION_FILENAME || '',
|
||||
translationFilename: process.env.I18N_TRANSLATION_FILENAME || 'messages',
|
||||
},
|
||||
appPath: process.cwd(),
|
||||
apiPath: process.env.API_ORIGIN,
|
||||
@ -77,7 +77,7 @@ export const config: Config = {
|
||||
: [undefined], // ['http://example.com', 'https://example.com'],
|
||||
},
|
||||
session: {
|
||||
secret: process.env.SESSION_SECRET || '4fac3596aeb0d048e7b6b38235c29248',
|
||||
secret: process.env.SESSION_SECRET || 'changeme',
|
||||
name: process.env.SESSION_NAME || 'hex.sid',
|
||||
adapter: 'connect-mongo',
|
||||
url: 'mongodb://localhost:27017/hexabot',
|
||||
@ -90,23 +90,18 @@ export const config: Config = {
|
||||
},
|
||||
},
|
||||
emails: {
|
||||
isEnabled: process.env.EMAIL_SMTP_ENABLED === 'true' || false,
|
||||
smtp: {
|
||||
port: parseInt(process.env.EMAIL_SMTP_PORT) || 25,
|
||||
host: process.env.EMAIL_SMTP_HOST || 'smtp.mailgun.org',
|
||||
host: process.env.EMAIL_SMTP_HOST || 'localhost',
|
||||
ignoreTLS: false,
|
||||
secure: process.env.EMAIL_SMTP_SECURE === 'true' || false,
|
||||
auth: {
|
||||
user:
|
||||
process.env.EMAIL_SMTP_USER ||
|
||||
'postmaster@sandbox9471202ff10448c7ac917618fe94d8e1.mailgun.org',
|
||||
pass: process.env.EMAIL_SMTP_PASS || 'e58526b30ad640394b5c77a211a19c5b',
|
||||
user: process.env.EMAIL_SMTP_USER || '',
|
||||
pass: process.env.EMAIL_SMTP_PASS || '',
|
||||
},
|
||||
},
|
||||
},
|
||||
datastores: {
|
||||
default: {
|
||||
adapter: 'sails-mongo',
|
||||
url: 'mongodb://localhost:27017/hexabot',
|
||||
},
|
||||
from: process.env.EMAIL_SMTP_FROM || 'noreply@example.com',
|
||||
},
|
||||
parameters: {
|
||||
uploadDir:
|
||||
@ -117,17 +112,9 @@ export const config: Config = {
|
||||
maxUploadSize: process.env.UPLOAD_MAX_SIZE_IN_BYTES
|
||||
? Number(process.env.UPLOAD_MAX_SIZE_IN_BYTES)
|
||||
: 2000000,
|
||||
transport: 'smtp',
|
||||
email: {
|
||||
main: 'postmaster@sandbox9471202ff10448c7ac917618fe94d8e1.mailgun.org',
|
||||
},
|
||||
appName: 'Hexabot.ai',
|
||||
apiUrl: 'http://localhost:4000',
|
||||
appUrl: 'http://localhost:8081',
|
||||
geocoder: {
|
||||
provider: 'opencage',
|
||||
apiKey: 'c2a490d593b14612aefa6ec2e6b77c47',
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
limit: 10,
|
||||
@ -135,7 +122,7 @@ export const config: Config = {
|
||||
chatbot: {
|
||||
lang: {
|
||||
default: 'en',
|
||||
available: ['en', 'fr', 'ar', 'tn'],
|
||||
available: ['en', 'fr'],
|
||||
},
|
||||
messages: {
|
||||
track_delivery: false,
|
||||
|
@ -7,6 +7,7 @@
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||
import type { ServerOptions, Socket } from 'socket.io';
|
||||
|
||||
type TJwtOptions = {
|
||||
@ -69,38 +70,18 @@ export type Config = {
|
||||
};
|
||||
};
|
||||
emails: {
|
||||
smtp: {
|
||||
port: number;
|
||||
host: string;
|
||||
secure: boolean;
|
||||
auth: {
|
||||
user: string;
|
||||
pass: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
datastores: {
|
||||
default: {
|
||||
adapter: string;
|
||||
url: string;
|
||||
};
|
||||
isEnabled: boolean;
|
||||
smtp: Partial<SMTPConnection.Options>;
|
||||
from: string;
|
||||
};
|
||||
parameters: {
|
||||
uploadDir: string;
|
||||
avatarDir: string;
|
||||
storageMode: 'disk' | 'memory';
|
||||
maxUploadSize: number;
|
||||
transport: string;
|
||||
email: {
|
||||
main: string;
|
||||
};
|
||||
appName: string;
|
||||
apiUrl: string;
|
||||
appUrl: string;
|
||||
geocoder: {
|
||||
provider: string;
|
||||
apiKey: string;
|
||||
};
|
||||
};
|
||||
pagination: {
|
||||
limit: number;
|
||||
|
@ -58,9 +58,11 @@ export class ExtendedI18nService<
|
||||
initDynamicTranslations(translations: Translation[]) {
|
||||
this.dynamicTranslations = translations.reduce((acc, curr) => {
|
||||
const { str, translations } = curr;
|
||||
Object.entries(translations).forEach(([lang, t]) => {
|
||||
acc[lang][str] = t;
|
||||
});
|
||||
Object.entries(translations)
|
||||
.filter(([lang]) => lang in acc)
|
||||
.forEach(([lang, t]) => {
|
||||
acc[lang][str] = t;
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, this.dynamicTranslations);
|
||||
|
@ -111,10 +111,4 @@ export type Settings = {
|
||||
fallback_message: string[];
|
||||
fallback_block: string;
|
||||
};
|
||||
email_settings: {
|
||||
mailer: string;
|
||||
auth_user: string;
|
||||
auth_pass: string;
|
||||
from: string;
|
||||
};
|
||||
} & Record<string, any>;
|
||||
|
@ -99,56 +99,6 @@ export const settingModels: SettingCreateDto[] = [
|
||||
},
|
||||
weight: 6,
|
||||
},
|
||||
{
|
||||
group: 'email_settings',
|
||||
label: 'from',
|
||||
value: 'no-reply@domain.com',
|
||||
type: SettingType.text,
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
group: 'email_settings',
|
||||
label: 'mailer',
|
||||
value: 'sendmail',
|
||||
options: ['sendmail', 'smtp'],
|
||||
type: SettingType.select,
|
||||
weight: 2,
|
||||
},
|
||||
{
|
||||
group: 'email_settings',
|
||||
label: 'host',
|
||||
value: 'localhost',
|
||||
type: SettingType.text,
|
||||
weight: 3,
|
||||
},
|
||||
{
|
||||
group: 'email_settings',
|
||||
label: 'port',
|
||||
value: '25',
|
||||
type: SettingType.text,
|
||||
weight: 4,
|
||||
},
|
||||
{
|
||||
group: 'email_settings',
|
||||
label: 'secure',
|
||||
value: true,
|
||||
type: SettingType.checkbox,
|
||||
weight: 5,
|
||||
},
|
||||
{
|
||||
group: 'email_settings',
|
||||
label: 'auth_user',
|
||||
value: '',
|
||||
type: SettingType.text,
|
||||
weight: 6,
|
||||
},
|
||||
{
|
||||
group: 'email_settings',
|
||||
label: 'auth_pass',
|
||||
value: '',
|
||||
type: SettingType.text,
|
||||
weight: 7,
|
||||
},
|
||||
{
|
||||
group: 'contact',
|
||||
label: 'contact_email_recipient',
|
||||
|
@ -24,10 +24,10 @@
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text font-size="14px" color="#000" font-family="helvetica"
|
||||
><%= t('best_regards') %>,</mj-text
|
||||
<mj-text font-size="16px" color="#000" font-family="helvetica"
|
||||
><%= t('best_regards') %></mj-text
|
||||
>
|
||||
<mj-text font-size="14px" color="#000" font-family="helvetica"
|
||||
<mj-text font-size="16px" color="#000" font-family="helvetica"
|
||||
><%= this.appName %></mj-text
|
||||
>
|
||||
</mj-column>
|
||||
|
@ -26,11 +26,11 @@
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text font-size="14px" color="#000" font-family="helvetica"
|
||||
<mj-text font-size="16px" color="#000" font-family="helvetica"
|
||||
><%= t('best_regards') %></mj-text
|
||||
>
|
||||
|
||||
<mj-text font-size="14px" color="#000" font-family="helvetica"
|
||||
<mj-text font-size="16px" color="#000" font-family="helvetica"
|
||||
><%= this.appName %></mj-text
|
||||
>
|
||||
</mj-column>
|
||||
|
@ -23,10 +23,10 @@
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text font-size="14px" color="#000" font-family="helvetica"
|
||||
><%= t('best_regards') %>,</mj-text
|
||||
<mj-text font-size="16px" color="#000" font-family="helvetica"
|
||||
><%= t('best_regards') %></mj-text
|
||||
>
|
||||
<mj-text font-size="14px" color="#000" font-family="helvetica"
|
||||
<mj-text font-size="16px" color="#000" font-family="helvetica"
|
||||
><%= this.appName %></mj-text
|
||||
>
|
||||
</mj-column>
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
Inject,
|
||||
Injectable,
|
||||
InternalServerErrorException,
|
||||
Optional,
|
||||
} from '@nestjs/common';
|
||||
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
||||
import { MailerService } from '@nestjs-modules/mailer';
|
||||
@ -32,7 +33,7 @@ export class InvitationService extends BaseService<Invitation> {
|
||||
@Inject(InvitationRepository)
|
||||
readonly repository: InvitationRepository,
|
||||
@Inject(JwtService) private readonly jwtService: JwtService,
|
||||
private readonly mailerService: MailerService,
|
||||
@Optional() private readonly mailerService: MailerService | undefined,
|
||||
private logger: LoggerService,
|
||||
protected readonly i18n: ExtendedI18nService,
|
||||
) {
|
||||
@ -54,24 +55,28 @@ export class InvitationService extends BaseService<Invitation> {
|
||||
*/
|
||||
async create(dto: InvitationCreateDto): Promise<Invitation> {
|
||||
const jwt = await this.sign(dto);
|
||||
try {
|
||||
await this.mailerService.sendMail({
|
||||
to: dto.email,
|
||||
template: 'invitation.mjml',
|
||||
context: {
|
||||
token: jwt,
|
||||
// TODO: Which language should we use?
|
||||
t: (key: string) => this.i18n.t(key),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
'Could not send email',
|
||||
e.message,
|
||||
e.stack,
|
||||
'InvitationService',
|
||||
);
|
||||
throw new InternalServerErrorException('Could not send email');
|
||||
if (this.mailerService) {
|
||||
try {
|
||||
await this.mailerService.sendMail({
|
||||
to: dto.email,
|
||||
template: 'invitation.mjml',
|
||||
context: {
|
||||
token: jwt,
|
||||
// TODO: Which language should we use?
|
||||
t: (key: string) =>
|
||||
this.i18n.t(key, { lang: config.chatbot.lang.default }),
|
||||
},
|
||||
subject: this.i18n.t('invitation_subject'),
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
'Could not send email',
|
||||
e.message,
|
||||
e.stack,
|
||||
'InvitationService',
|
||||
);
|
||||
throw new InternalServerErrorException('Could not send email');
|
||||
}
|
||||
}
|
||||
const newInvitation = await super.create({ ...dto, token: jwt });
|
||||
return { ...newInvitation, token: jwt };
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
Injectable,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
Optional,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
||||
@ -30,7 +31,7 @@ import { UserRequestResetDto, UserResetPasswordDto } from '../dto/user.dto';
|
||||
export class PasswordResetService {
|
||||
constructor(
|
||||
@Inject(JwtService) private readonly jwtService: JwtService,
|
||||
private readonly mailerService: MailerService,
|
||||
@Optional() private readonly mailerService: MailerService | undefined,
|
||||
private logger: LoggerService,
|
||||
private readonly userService: UserService,
|
||||
public readonly i18n: ExtendedI18nService,
|
||||
@ -55,24 +56,29 @@ export class PasswordResetService {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
const jwt = await this.sign(dto);
|
||||
try {
|
||||
await this.mailerService.sendMail({
|
||||
to: dto.email,
|
||||
template: 'password_reset.mjml',
|
||||
context: {
|
||||
token: jwt,
|
||||
first_name: user.first_name,
|
||||
t: (key: string) => this.i18n.t(key),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
'Could not send email',
|
||||
e.message,
|
||||
e.stack,
|
||||
'InvitationService',
|
||||
);
|
||||
throw new InternalServerErrorException('Could not send email');
|
||||
|
||||
if (this.mailerService) {
|
||||
try {
|
||||
await this.mailerService.sendMail({
|
||||
to: dto.email,
|
||||
template: 'password_reset.mjml',
|
||||
context: {
|
||||
token: jwt,
|
||||
first_name: user.first_name,
|
||||
t: (key: string) =>
|
||||
this.i18n.t(key, { lang: config.chatbot.lang.default }),
|
||||
},
|
||||
subject: this.i18n.t('password_reset_subject'),
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
'Could not send email',
|
||||
e.message,
|
||||
e.stack,
|
||||
'InvitationService',
|
||||
);
|
||||
throw new InternalServerErrorException('Could not send email');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: hash the token before saving it
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
Inject,
|
||||
Injectable,
|
||||
InternalServerErrorException,
|
||||
Optional,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
||||
@ -33,7 +34,7 @@ export class ValidateAccountService {
|
||||
constructor(
|
||||
@Inject(JwtService) private readonly jwtService: JwtService,
|
||||
private readonly userService: UserService,
|
||||
private readonly mailerService: MailerService,
|
||||
@Optional() private readonly mailerService: MailerService | undefined,
|
||||
private readonly i18n: ExtendedI18nService,
|
||||
) {}
|
||||
|
||||
@ -71,16 +72,19 @@ export class ValidateAccountService {
|
||||
) {
|
||||
const confirmationToken = await this.sign({ email: dto.email });
|
||||
|
||||
await this.mailerService.sendMail({
|
||||
to: dto.email,
|
||||
template: 'account_confirmation.mjml',
|
||||
context: {
|
||||
token: confirmationToken,
|
||||
first_name: dto.first_name,
|
||||
t: (key: string) => this.i18n.t(key),
|
||||
},
|
||||
subject: 'Account confirmation Email',
|
||||
});
|
||||
if (this.mailerService) {
|
||||
await this.mailerService.sendMail({
|
||||
to: dto.email,
|
||||
template: 'account_confirmation.mjml',
|
||||
context: {
|
||||
token: confirmationToken,
|
||||
first_name: dto.first_name,
|
||||
t: (key: string) =>
|
||||
this.i18n.t(key, { lang: config.chatbot.lang.default }),
|
||||
},
|
||||
subject: this.i18n.t('account_confirmation_subject'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,13 +30,15 @@ MONGO_PASSWORD=dev_only
|
||||
MONGO_URI=mongodb://${MONGO_USER}:${MONGO_PASSWORD}@mongo:27017/
|
||||
MONGO_DB=hexabot
|
||||
|
||||
# SMTP Config for local dev env
|
||||
# SMTP Config (for local dev env, use smtp4dev by doing `make start SMTP4DEV=1`)
|
||||
APP_SMTP_4_DEV_PORT=9002
|
||||
EMAIL_SMTP_ENABLED=false
|
||||
EMAIL_SMTP_HOST=smtp4dev
|
||||
EMAIL_SMTP_PORT=25
|
||||
EMAIL_SMTP_SECURE=false
|
||||
EMAIL_SMTP_USER=dev_only
|
||||
EMAIL_SMTP_PASS=dev_only
|
||||
EMAIL_SMTP_FROM=noreply@example.com
|
||||
|
||||
# NLU Server
|
||||
AUTH_TOKEN=token123
|
||||
|
@ -17,21 +17,6 @@ services:
|
||||
#- ../api/node_modules:/app/node_modules
|
||||
command: ["npm", "run", "start:debug"]
|
||||
|
||||
smtp4dev:
|
||||
image: rnwood/smtp4dev:v3
|
||||
restart: always
|
||||
ports:
|
||||
- ${APP_SMTP_4_DEV_PORT}:80
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
volumes:
|
||||
- smtp4dev-data:/smtp4dev
|
||||
environment:
|
||||
- ServerOptions__HostName=smtp4dev
|
||||
- ServerOptions__LockSettings=true
|
||||
networks:
|
||||
- db-network
|
||||
|
||||
mongo-express:
|
||||
container_name: mongoUi
|
||||
image: mongo-express:1-20
|
||||
@ -52,6 +37,3 @@ services:
|
||||
- ../widget/src:/app/src
|
||||
ports:
|
||||
- ${APP_WIDGET_PORT}:5173
|
||||
|
||||
volumes:
|
||||
smtp4dev-data:
|
||||
|
20
docker/docker-compose.smtp4dev.yml
Normal file
20
docker/docker-compose.smtp4dev.yml
Normal file
@ -0,0 +1,20 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
smtp4dev:
|
||||
image: rnwood/smtp4dev:v3
|
||||
restart: always
|
||||
ports:
|
||||
- ${APP_SMTP_4_DEV_PORT}:80
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
volumes:
|
||||
- smtp4dev-data:/smtp4dev
|
||||
environment:
|
||||
- ServerOptions__HostName=smtp4dev
|
||||
- ServerOptions__LockSettings=true
|
||||
networks:
|
||||
- app-network
|
||||
|
||||
volumes:
|
||||
smtp4dev-data:
|
@ -40,7 +40,7 @@
|
||||
"edit_account_email": "A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.",
|
||||
"new_password": "To change the current password, enter the new password in both fields.",
|
||||
"account_update_success": "Account has been updated successfully",
|
||||
"account_disabled": "Your account has been disabled!",
|
||||
"account_disabled": "Your account has been either disabled or is pending confirmation.",
|
||||
"success_invitation_sent": "Invitation to join has been successfully sent.",
|
||||
"item_delete_confirm": "Are you sure you want to delete this item?",
|
||||
"item_delete_success": "Item has been deleted successfully",
|
||||
@ -213,7 +213,6 @@
|
||||
"offline": "Web Channel",
|
||||
"twitter": "Twitter",
|
||||
"dimelo": "Dimelo",
|
||||
"email_settings": "Email",
|
||||
"contact": "Contact Infos",
|
||||
"chatbot_settings": "Chatbot",
|
||||
"nlp_settings": "NLP Provider",
|
||||
|
@ -40,7 +40,7 @@
|
||||
"edit_account_email": "Une adresse e-mail valide Tous les e-mails du système seront envoyés à cette adresse. L'adresse e-mail n'est pas rendue publique et ne sera utilisée que si vous souhaitez recevoir un nouveau mot de passe ou si vous souhaitez recevoir certaines nouvelles ou notifications par e-mail.",
|
||||
"new_password": "Pour changer le mot de passe actuel, entrez le nouveau mot de passe dans les deux champs.",
|
||||
"account_update_success": "Le compte a été mis à jour avec succès",
|
||||
"account_disabled": "Votre compte a été désactivé!",
|
||||
"account_disabled": "Votre compte a été désactivé ou est en attente de confirmation.",
|
||||
"success_invitation_sent": "L'invitation a été envoyée avec succès.",
|
||||
"item_delete_confirm": "Êtes-vous sûr de bien vouloir supprimer cet élément?",
|
||||
"item_delete_success": "L'élément a été supprimé avec succès",
|
||||
|
Loading…
Reference in New Issue
Block a user