mirror of
https://github.com/hexastack/hexabot
synced 2024-11-28 06:52:20 +00:00
commit
ced9f0538c
11
Makefile
11
Makefile
@ -9,7 +9,10 @@ define add_service
|
|||||||
COMPOSE_FILES += -f ./docker/docker-compose.$(1).prod.yml
|
COMPOSE_FILES += -f ./docker/docker-compose.$(1).prod.yml
|
||||||
endif
|
endif
|
||||||
else
|
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
|
endif
|
||||||
endef
|
endef
|
||||||
|
|
||||||
@ -23,6 +26,10 @@ ifneq ($(NLU),)
|
|||||||
$(eval $(call add_service,nlu))
|
$(eval $(call add_service,nlu))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq ($(SMTP4DEV),)
|
||||||
|
$(eval $(call add_service,smtp4dev))
|
||||||
|
endif
|
||||||
|
|
||||||
# Ensure .env file exists and matches .env.example
|
# Ensure .env file exists and matches .env.example
|
||||||
check-env:
|
check-env:
|
||||||
@if [ ! -f "./docker/.env" ]; then \
|
@if [ ! -f "./docker/.env" ]; then \
|
||||||
@ -48,4 +55,4 @@ destroy: check-env
|
|||||||
docker compose $(COMPOSE_FILES) down -v
|
docker compose $(COMPOSE_FILES) down -v
|
||||||
|
|
||||||
migrate-up:
|
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({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
MailerModule.forRoot({
|
...(config.emails.isEnabled
|
||||||
transport: new SMTPTransport({
|
? [
|
||||||
...config.emails.smtp,
|
MailerModule.forRoot({
|
||||||
logger: true,
|
transport: new SMTPTransport({
|
||||||
}),
|
...config.emails.smtp,
|
||||||
template: {
|
logger: true,
|
||||||
adapter: new MjmlAdapter('ejs', { inlineCssEnabled: false }),
|
debug: false,
|
||||||
dir: './src/templates',
|
}),
|
||||||
options: {
|
template: {
|
||||||
context: {
|
adapter: new MjmlAdapter('ejs', { inlineCssEnabled: false }),
|
||||||
appName: config.parameters.appName,
|
dir: './src/templates',
|
||||||
appUrl: config.parameters.appUrl,
|
options: {
|
||||||
// TODO: add i18n support
|
context: {
|
||||||
},
|
appName: config.parameters.appName,
|
||||||
},
|
appUrl: config.parameters.appUrl,
|
||||||
},
|
},
|
||||||
defaults: { from: config.parameters.email.main },
|
},
|
||||||
}),
|
},
|
||||||
|
defaults: { from: config.emails.from },
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
MongooseModule.forRoot(config.mongo.uri, {
|
MongooseModule.forRoot(config.mongo.uri, {
|
||||||
dbName: config.mongo.dbName,
|
dbName: config.mongo.dbName,
|
||||||
connectionFactory: (connection) => {
|
connectionFactory: (connection) => {
|
||||||
|
@ -8,19 +8,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
||||||
|
|
||||||
import { ExtendedI18nService } from './extended-i18n.service';
|
import { ExtendedI18nService } from './extended-i18n.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppService {
|
export class AppService {
|
||||||
constructor(
|
constructor(private readonly i18n: ExtendedI18nService) {}
|
||||||
private readonly i18n: ExtendedI18nService,
|
|
||||||
private readonly eventEmitter: EventEmitter2,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
getHello(): string {
|
getHello(): string {
|
||||||
this.eventEmitter.emit('hook:i18n:refresh', []);
|
return this.i18n.t('welcome', { lang: 'en' });
|
||||||
return this.i18n.t('Welcome');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
{
|
{
|
||||||
|
"invitation_subject": "[Hexabot] Sign-Up Invitation",
|
||||||
|
"account_confirmation_subject": "[Hexabot] Account Confirmation",
|
||||||
|
"password_reset_subject": "[Hexabot] Password Reset",
|
||||||
"welcome": "Welcome",
|
"welcome": "Welcome",
|
||||||
"hi": "Hi",
|
"hi": "Hi",
|
||||||
"invitation_to_join": "Invitation to join",
|
"invitation_to_join": "Invitation to join",
|
||||||
"invitation_for_account_creation": "You have been invited to create a",
|
"invitation_for_account_creation": "You have been invited to create a",
|
||||||
"account": "account",
|
"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.",
|
"registration_failed": "Registration failed! Either email address or invitation token is invalid.",
|
||||||
"create_account_button": "Click on the button below to create your account",
|
"create_account_button": "Click on the button below to create your account",
|
||||||
"join": "Join",
|
"join": "Join",
|
||||||
"account_successfully_created_confirm_password": "You have successfully created an account but you will need to confirm your email address.",
|
"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.",
|
"password_reset_request": "A request to reset the password for your account has been made.",
|
||||||
"reset": "Reset",
|
"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",
|
"confirm": "Confirm",
|
||||||
"best_regards": "Best Regards,"
|
"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",
|
"welcome": "Bienvenue",
|
||||||
"hi": "Bonjour",
|
"hi": "Bonjour",
|
||||||
"invitation_to_join": "Invitation",
|
"invitation_to_join": "Invitation",
|
||||||
|
@ -13,7 +13,7 @@ import { Config } from './types';
|
|||||||
|
|
||||||
export const config: Config = {
|
export const config: Config = {
|
||||||
i18n: {
|
i18n: {
|
||||||
translationFilename: process.env.I18N_TRANSLATION_FILENAME || '',
|
translationFilename: process.env.I18N_TRANSLATION_FILENAME || 'messages',
|
||||||
},
|
},
|
||||||
appPath: process.cwd(),
|
appPath: process.cwd(),
|
||||||
apiPath: process.env.API_ORIGIN,
|
apiPath: process.env.API_ORIGIN,
|
||||||
@ -77,7 +77,7 @@ export const config: Config = {
|
|||||||
: [undefined], // ['http://example.com', 'https://example.com'],
|
: [undefined], // ['http://example.com', 'https://example.com'],
|
||||||
},
|
},
|
||||||
session: {
|
session: {
|
||||||
secret: process.env.SESSION_SECRET || '4fac3596aeb0d048e7b6b38235c29248',
|
secret: process.env.SESSION_SECRET || 'changeme',
|
||||||
name: process.env.SESSION_NAME || 'hex.sid',
|
name: process.env.SESSION_NAME || 'hex.sid',
|
||||||
adapter: 'connect-mongo',
|
adapter: 'connect-mongo',
|
||||||
url: 'mongodb://localhost:27017/hexabot',
|
url: 'mongodb://localhost:27017/hexabot',
|
||||||
@ -90,23 +90,18 @@ export const config: Config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
emails: {
|
emails: {
|
||||||
|
isEnabled: process.env.EMAIL_SMTP_ENABLED === 'true' || false,
|
||||||
smtp: {
|
smtp: {
|
||||||
port: parseInt(process.env.EMAIL_SMTP_PORT) || 25,
|
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,
|
secure: process.env.EMAIL_SMTP_SECURE === 'true' || false,
|
||||||
auth: {
|
auth: {
|
||||||
user:
|
user: process.env.EMAIL_SMTP_USER || '',
|
||||||
process.env.EMAIL_SMTP_USER ||
|
pass: process.env.EMAIL_SMTP_PASS || '',
|
||||||
'postmaster@sandbox9471202ff10448c7ac917618fe94d8e1.mailgun.org',
|
|
||||||
pass: process.env.EMAIL_SMTP_PASS || 'e58526b30ad640394b5c77a211a19c5b',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
from: process.env.EMAIL_SMTP_FROM || 'noreply@example.com',
|
||||||
datastores: {
|
|
||||||
default: {
|
|
||||||
adapter: 'sails-mongo',
|
|
||||||
url: 'mongodb://localhost:27017/hexabot',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
uploadDir:
|
uploadDir:
|
||||||
@ -117,17 +112,9 @@ export const config: Config = {
|
|||||||
maxUploadSize: process.env.UPLOAD_MAX_SIZE_IN_BYTES
|
maxUploadSize: process.env.UPLOAD_MAX_SIZE_IN_BYTES
|
||||||
? Number(process.env.UPLOAD_MAX_SIZE_IN_BYTES)
|
? Number(process.env.UPLOAD_MAX_SIZE_IN_BYTES)
|
||||||
: 2000000,
|
: 2000000,
|
||||||
transport: 'smtp',
|
|
||||||
email: {
|
|
||||||
main: 'postmaster@sandbox9471202ff10448c7ac917618fe94d8e1.mailgun.org',
|
|
||||||
},
|
|
||||||
appName: 'Hexabot.ai',
|
appName: 'Hexabot.ai',
|
||||||
apiUrl: 'http://localhost:4000',
|
apiUrl: 'http://localhost:4000',
|
||||||
appUrl: 'http://localhost:8081',
|
appUrl: 'http://localhost:8081',
|
||||||
geocoder: {
|
|
||||||
provider: 'opencage',
|
|
||||||
apiKey: 'c2a490d593b14612aefa6ec2e6b77c47',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@ -135,7 +122,7 @@ export const config: Config = {
|
|||||||
chatbot: {
|
chatbot: {
|
||||||
lang: {
|
lang: {
|
||||||
default: 'en',
|
default: 'en',
|
||||||
available: ['en', 'fr', 'ar', 'tn'],
|
available: ['en', 'fr'],
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
track_delivery: false,
|
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.
|
* 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';
|
import type { ServerOptions, Socket } from 'socket.io';
|
||||||
|
|
||||||
type TJwtOptions = {
|
type TJwtOptions = {
|
||||||
@ -69,38 +70,18 @@ export type Config = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
emails: {
|
emails: {
|
||||||
smtp: {
|
isEnabled: boolean;
|
||||||
port: number;
|
smtp: Partial<SMTPConnection.Options>;
|
||||||
host: string;
|
from: string;
|
||||||
secure: boolean;
|
|
||||||
auth: {
|
|
||||||
user: string;
|
|
||||||
pass: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
datastores: {
|
|
||||||
default: {
|
|
||||||
adapter: string;
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
parameters: {
|
parameters: {
|
||||||
uploadDir: string;
|
uploadDir: string;
|
||||||
avatarDir: string;
|
avatarDir: string;
|
||||||
storageMode: 'disk' | 'memory';
|
storageMode: 'disk' | 'memory';
|
||||||
maxUploadSize: number;
|
maxUploadSize: number;
|
||||||
transport: string;
|
|
||||||
email: {
|
|
||||||
main: string;
|
|
||||||
};
|
|
||||||
appName: string;
|
appName: string;
|
||||||
apiUrl: string;
|
apiUrl: string;
|
||||||
appUrl: string;
|
appUrl: string;
|
||||||
geocoder: {
|
|
||||||
provider: string;
|
|
||||||
apiKey: string;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: number;
|
limit: number;
|
||||||
|
@ -58,9 +58,11 @@ export class ExtendedI18nService<
|
|||||||
initDynamicTranslations(translations: Translation[]) {
|
initDynamicTranslations(translations: Translation[]) {
|
||||||
this.dynamicTranslations = translations.reduce((acc, curr) => {
|
this.dynamicTranslations = translations.reduce((acc, curr) => {
|
||||||
const { str, translations } = curr;
|
const { str, translations } = curr;
|
||||||
Object.entries(translations).forEach(([lang, t]) => {
|
Object.entries(translations)
|
||||||
acc[lang][str] = t;
|
.filter(([lang]) => lang in acc)
|
||||||
});
|
.forEach(([lang, t]) => {
|
||||||
|
acc[lang][str] = t;
|
||||||
|
});
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, this.dynamicTranslations);
|
}, this.dynamicTranslations);
|
||||||
|
@ -111,10 +111,4 @@ export type Settings = {
|
|||||||
fallback_message: string[];
|
fallback_message: string[];
|
||||||
fallback_block: string;
|
fallback_block: string;
|
||||||
};
|
};
|
||||||
email_settings: {
|
|
||||||
mailer: string;
|
|
||||||
auth_user: string;
|
|
||||||
auth_pass: string;
|
|
||||||
from: string;
|
|
||||||
};
|
|
||||||
} & Record<string, any>;
|
} & Record<string, any>;
|
||||||
|
@ -99,56 +99,6 @@ export const settingModels: SettingCreateDto[] = [
|
|||||||
},
|
},
|
||||||
weight: 6,
|
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',
|
group: 'contact',
|
||||||
label: 'contact_email_recipient',
|
label: 'contact_email_recipient',
|
||||||
|
@ -24,10 +24,10 @@
|
|||||||
</mj-section>
|
</mj-section>
|
||||||
<mj-section>
|
<mj-section>
|
||||||
<mj-column>
|
<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
|
><%= 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
|
><%= this.appName %></mj-text
|
||||||
>
|
>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
|
@ -26,11 +26,11 @@
|
|||||||
</mj-section>
|
</mj-section>
|
||||||
<mj-section>
|
<mj-section>
|
||||||
<mj-column>
|
<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
|
><%= 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
|
><%= this.appName %></mj-text
|
||||||
>
|
>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
|
@ -23,10 +23,10 @@
|
|||||||
</mj-section>
|
</mj-section>
|
||||||
<mj-section>
|
<mj-section>
|
||||||
<mj-column>
|
<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
|
><%= 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
|
><%= this.appName %></mj-text
|
||||||
>
|
>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
Inject,
|
Inject,
|
||||||
Injectable,
|
Injectable,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
|
Optional,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
||||||
import { MailerService } from '@nestjs-modules/mailer';
|
import { MailerService } from '@nestjs-modules/mailer';
|
||||||
@ -32,7 +33,7 @@ export class InvitationService extends BaseService<Invitation> {
|
|||||||
@Inject(InvitationRepository)
|
@Inject(InvitationRepository)
|
||||||
readonly repository: InvitationRepository,
|
readonly repository: InvitationRepository,
|
||||||
@Inject(JwtService) private readonly jwtService: JwtService,
|
@Inject(JwtService) private readonly jwtService: JwtService,
|
||||||
private readonly mailerService: MailerService,
|
@Optional() private readonly mailerService: MailerService | undefined,
|
||||||
private logger: LoggerService,
|
private logger: LoggerService,
|
||||||
protected readonly i18n: ExtendedI18nService,
|
protected readonly i18n: ExtendedI18nService,
|
||||||
) {
|
) {
|
||||||
@ -54,24 +55,28 @@ export class InvitationService extends BaseService<Invitation> {
|
|||||||
*/
|
*/
|
||||||
async create(dto: InvitationCreateDto): Promise<Invitation> {
|
async create(dto: InvitationCreateDto): Promise<Invitation> {
|
||||||
const jwt = await this.sign(dto);
|
const jwt = await this.sign(dto);
|
||||||
try {
|
if (this.mailerService) {
|
||||||
await this.mailerService.sendMail({
|
try {
|
||||||
to: dto.email,
|
await this.mailerService.sendMail({
|
||||||
template: 'invitation.mjml',
|
to: dto.email,
|
||||||
context: {
|
template: 'invitation.mjml',
|
||||||
token: jwt,
|
context: {
|
||||||
// TODO: Which language should we use?
|
token: jwt,
|
||||||
t: (key: string) => this.i18n.t(key),
|
// TODO: Which language should we use?
|
||||||
},
|
t: (key: string) =>
|
||||||
});
|
this.i18n.t(key, { lang: config.chatbot.lang.default }),
|
||||||
} catch (e) {
|
},
|
||||||
this.logger.error(
|
subject: this.i18n.t('invitation_subject'),
|
||||||
'Could not send email',
|
});
|
||||||
e.message,
|
} catch (e) {
|
||||||
e.stack,
|
this.logger.error(
|
||||||
'InvitationService',
|
'Could not send email',
|
||||||
);
|
e.message,
|
||||||
throw new InternalServerErrorException('Could not send email');
|
e.stack,
|
||||||
|
'InvitationService',
|
||||||
|
);
|
||||||
|
throw new InternalServerErrorException('Could not send email');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const newInvitation = await super.create({ ...dto, token: jwt });
|
const newInvitation = await super.create({ ...dto, token: jwt });
|
||||||
return { ...newInvitation, token: jwt };
|
return { ...newInvitation, token: jwt };
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
Injectable,
|
Injectable,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
|
Optional,
|
||||||
UnauthorizedException,
|
UnauthorizedException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
||||||
@ -30,7 +31,7 @@ import { UserRequestResetDto, UserResetPasswordDto } from '../dto/user.dto';
|
|||||||
export class PasswordResetService {
|
export class PasswordResetService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(JwtService) private readonly jwtService: JwtService,
|
@Inject(JwtService) private readonly jwtService: JwtService,
|
||||||
private readonly mailerService: MailerService,
|
@Optional() private readonly mailerService: MailerService | undefined,
|
||||||
private logger: LoggerService,
|
private logger: LoggerService,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
public readonly i18n: ExtendedI18nService,
|
public readonly i18n: ExtendedI18nService,
|
||||||
@ -55,24 +56,29 @@ export class PasswordResetService {
|
|||||||
throw new NotFoundException('User not found');
|
throw new NotFoundException('User not found');
|
||||||
}
|
}
|
||||||
const jwt = await this.sign(dto);
|
const jwt = await this.sign(dto);
|
||||||
try {
|
|
||||||
await this.mailerService.sendMail({
|
if (this.mailerService) {
|
||||||
to: dto.email,
|
try {
|
||||||
template: 'password_reset.mjml',
|
await this.mailerService.sendMail({
|
||||||
context: {
|
to: dto.email,
|
||||||
token: jwt,
|
template: 'password_reset.mjml',
|
||||||
first_name: user.first_name,
|
context: {
|
||||||
t: (key: string) => this.i18n.t(key),
|
token: jwt,
|
||||||
},
|
first_name: user.first_name,
|
||||||
});
|
t: (key: string) =>
|
||||||
} catch (e) {
|
this.i18n.t(key, { lang: config.chatbot.lang.default }),
|
||||||
this.logger.error(
|
},
|
||||||
'Could not send email',
|
subject: this.i18n.t('password_reset_subject'),
|
||||||
e.message,
|
});
|
||||||
e.stack,
|
} catch (e) {
|
||||||
'InvitationService',
|
this.logger.error(
|
||||||
);
|
'Could not send email',
|
||||||
throw new InternalServerErrorException('Could not send email');
|
e.message,
|
||||||
|
e.stack,
|
||||||
|
'InvitationService',
|
||||||
|
);
|
||||||
|
throw new InternalServerErrorException('Could not send email');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: hash the token before saving it
|
// TODO: hash the token before saving it
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
Inject,
|
Inject,
|
||||||
Injectable,
|
Injectable,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
|
Optional,
|
||||||
UnauthorizedException,
|
UnauthorizedException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
||||||
@ -33,7 +34,7 @@ export class ValidateAccountService {
|
|||||||
constructor(
|
constructor(
|
||||||
@Inject(JwtService) private readonly jwtService: JwtService,
|
@Inject(JwtService) private readonly jwtService: JwtService,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
private readonly mailerService: MailerService,
|
@Optional() private readonly mailerService: MailerService | undefined,
|
||||||
private readonly i18n: ExtendedI18nService,
|
private readonly i18n: ExtendedI18nService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -71,16 +72,19 @@ export class ValidateAccountService {
|
|||||||
) {
|
) {
|
||||||
const confirmationToken = await this.sign({ email: dto.email });
|
const confirmationToken = await this.sign({ email: dto.email });
|
||||||
|
|
||||||
await this.mailerService.sendMail({
|
if (this.mailerService) {
|
||||||
to: dto.email,
|
await this.mailerService.sendMail({
|
||||||
template: 'account_confirmation.mjml',
|
to: dto.email,
|
||||||
context: {
|
template: 'account_confirmation.mjml',
|
||||||
token: confirmationToken,
|
context: {
|
||||||
first_name: dto.first_name,
|
token: confirmationToken,
|
||||||
t: (key: string) => this.i18n.t(key),
|
first_name: dto.first_name,
|
||||||
},
|
t: (key: string) =>
|
||||||
subject: 'Account confirmation Email',
|
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_URI=mongodb://${MONGO_USER}:${MONGO_PASSWORD}@mongo:27017/
|
||||||
MONGO_DB=hexabot
|
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
|
APP_SMTP_4_DEV_PORT=9002
|
||||||
|
EMAIL_SMTP_ENABLED=false
|
||||||
EMAIL_SMTP_HOST=smtp4dev
|
EMAIL_SMTP_HOST=smtp4dev
|
||||||
EMAIL_SMTP_PORT=25
|
EMAIL_SMTP_PORT=25
|
||||||
EMAIL_SMTP_SECURE=false
|
EMAIL_SMTP_SECURE=false
|
||||||
EMAIL_SMTP_USER=dev_only
|
EMAIL_SMTP_USER=dev_only
|
||||||
EMAIL_SMTP_PASS=dev_only
|
EMAIL_SMTP_PASS=dev_only
|
||||||
|
EMAIL_SMTP_FROM=noreply@example.com
|
||||||
|
|
||||||
# NLU Server
|
# NLU Server
|
||||||
AUTH_TOKEN=token123
|
AUTH_TOKEN=token123
|
||||||
|
@ -17,21 +17,6 @@ services:
|
|||||||
#- ../api/node_modules:/app/node_modules
|
#- ../api/node_modules:/app/node_modules
|
||||||
command: ["npm", "run", "start:debug"]
|
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:
|
mongo-express:
|
||||||
container_name: mongoUi
|
container_name: mongoUi
|
||||||
image: mongo-express:1-20
|
image: mongo-express:1-20
|
||||||
@ -52,6 +37,3 @@ services:
|
|||||||
- ../widget/src:/app/src
|
- ../widget/src:/app/src
|
||||||
ports:
|
ports:
|
||||||
- ${APP_WIDGET_PORT}:5173
|
- ${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.",
|
"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.",
|
"new_password": "To change the current password, enter the new password in both fields.",
|
||||||
"account_update_success": "Account has been updated successfully",
|
"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.",
|
"success_invitation_sent": "Invitation to join has been successfully sent.",
|
||||||
"item_delete_confirm": "Are you sure you want to delete this item?",
|
"item_delete_confirm": "Are you sure you want to delete this item?",
|
||||||
"item_delete_success": "Item has been deleted successfully",
|
"item_delete_success": "Item has been deleted successfully",
|
||||||
@ -213,7 +213,6 @@
|
|||||||
"offline": "Web Channel",
|
"offline": "Web Channel",
|
||||||
"twitter": "Twitter",
|
"twitter": "Twitter",
|
||||||
"dimelo": "Dimelo",
|
"dimelo": "Dimelo",
|
||||||
"email_settings": "Email",
|
|
||||||
"contact": "Contact Infos",
|
"contact": "Contact Infos",
|
||||||
"chatbot_settings": "Chatbot",
|
"chatbot_settings": "Chatbot",
|
||||||
"nlp_settings": "NLP Provider",
|
"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.",
|
"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.",
|
"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_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.",
|
"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_confirm": "Êtes-vous sûr de bien vouloir supprimer cet élément?",
|
||||||
"item_delete_success": "L'élément a été supprimé avec succès",
|
"item_delete_success": "L'élément a été supprimé avec succès",
|
||||||
|
Loading…
Reference in New Issue
Block a user