perf(app): working docker build stage, with github actions ci to push image

This commit is contained in:
Amruth Pillai 2022-03-04 10:54:31 +01:00
parent 36f41c2f9d
commit 5104ea6438
No known key found for this signature in database
GPG Key ID: E3C57DF9B80855AD
16 changed files with 712 additions and 657 deletions

View File

@ -3,7 +3,7 @@ TZ=UTC
NODE_ENV=development
SECRET_KEY=change-me
# Public URLs
# URLs
APP_URL=http://localhost:3000
SERVER_URL=http://localhost:3100
@ -19,6 +19,7 @@ JWT_SECRET=change-me
JWT_EXPIRY_TIME=604800
# Mail
MAIL_FROM=
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=

View File

@ -5,10 +5,8 @@ on:
branches: [v3]
jobs:
build:
name: Build Image and Push to Docker Hub
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
@ -23,7 +21,7 @@ jobs:
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: amruthpillai/reactive-resume
images: ${{ secrets.DOCKER_USERNAME }}/reactive-resume
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc

View File

@ -6,13 +6,13 @@ WORKDIR /app
COPY .npmrc package.json pnpm-lock.yaml ./
RUN pnpm install --ignore-scripts --frozen-lockfile
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm run build
RUN pnpm build
FROM playwright/chromium
FROM mcr.microsoft.com/playwright:focal AS production
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
@ -22,7 +22,7 @@ COPY --from=builder /app/package.json .
COPY --from=builder /app/pnpm-lock.yaml .
COPY --from=builder /app/dist ./dist
RUN pnpm install --ignore-scripts --frozen-lockfile --prod
RUN pnpm install --frozen-lockfile --prod
# Expose App
EXPOSE 3000

View File

@ -38,7 +38,7 @@ const ModalWrapper: React.FC = () => {
if (!modal) return;
dispatch(setModalState({ modal, state: { open: true, payload: rest } }));
dispatch(setModalState({ modal, state: { open: true, payload: { item: rest } } }));
}, [router.query, dispatch]);
return (

View File

@ -4,7 +4,7 @@
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/node:build",
"executor": "@nrwl/node:webpack",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/server",
@ -27,7 +27,7 @@
}
},
"serve": {
"executor": "@nrwl/node:execute",
"executor": "@nrwl/node:node",
"options": {
"buildTarget": "server:build"
},

View File

@ -0,0 +1,288 @@
<html>
<head>
<meta charset='utf-8' />
<meta http-equiv='x-ua-compatible' content='ie=edge' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<title>Reset Your Password</title>
<style type='text/css'>
@media screen { @font-face { font-family: "Source Sans Pro"; font-style: normal; font-weight: 400; src:
local("Source Sans Pro Regular"), local("SourceSansPro-Regular"),
url(https://fonts.gstatic.com/s/sourcesanspro/v10/ODelI1aHBYDBqgeIAH2zlBM0YzuT7MdOe03otPbuUS0.woff)
format("woff"); } @font-face { font-family: "Source Sans Pro"; font-style: normal; font-weight: 700; src:
local("Source Sans Pro Bold"), local("SourceSansPro-Bold"),
url(https://fonts.gstatic.com/s/sourcesanspro/v10/toadOcfmlt9b38dHJxOBGFkQc6VGVFSmCnC_l7QZG60.woff)
format("woff"); } } body, table, td, a { -ms-text-size-adjust: 100%; /* 1 */ -webkit-text-size-adjust: 100%; /* 2
*/ } /** * Remove extra space added to tables and cells in Outlook. */ table, td { mso-table-rspace: 0pt;
mso-table-lspace: 0pt; } /** * Better fluid images in Internet Explorer. */ img { -ms-interpolation-mode: bicubic;
} /** * Remove blue links for iOS devices. */ a[x-apple-data-detectors] { font-family: inherit !important;
font-size: inherit !important; font-weight: inherit !important; line-height: inherit !important; color: inherit
!important; text-decoration: none !important; } /** * Fix centering issues in Android 4.4. */ div[style*="margin:
16px 0; "] { margin: 0 !important; } body { width: 100% !important; height: 100% !important; padding: 0
!important; margin: 0 !important; } /** * Collapse table borders to avoid space between cells. */ table {
border-collapse: collapse !important; } a { color: #1a82e2; } img { height: auto; line-height: 100%;
text-decoration: none; border: 0; outline: none; }
</style>
</head>
<body style='background-color: #e9ecef'>
<!-- start preheader -->
<div
class='preheader'
style='
display: none;
max-width: 0;
max-height: 0;
overflow: hidden;
font-size: 1px;
line-height: 1px;
color: #fff;
opacity: 0;
'
>
Inside this email, you will find a unique link to reset your password. Do not share this email with anyone as your
account may become compromised.
</div>
<!-- end preheader -->
<!-- start body -->
<table border='0' cellpadding='0' cellspacing='0' width='100%'>
<!-- start logo -->
<tr>
<td align='center' bgcolor='#e9ecef'>
<!--[if (gte mso 9)|(IE)]>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tr>
<td align="center" valign="top" width="600">
<![endif]-->
<table border='0' cellpadding='0' cellspacing='0' width='100%' style='max-width: 600px'>
<tr>
<td align='center' valign='top' style='padding: 36px 24px'>
<a href='https://rxresu.me/' target='_blank' style='display: inline-block'>
<img
src='https://i.imgur.com/pc8Ingg.png'
alt='Reactive Resume Logo'
border='0'
width='128'
style='
display: block;
width: 128px;
max-width: 128px;
min-width: 128px;
'
/>
</a>
</td>
</tr>
</table>
<!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
<!-- end logo -->
<!-- start hero -->
<tr>
<td align='center' bgcolor='#e9ecef'>
<!--[if (gte mso 9)|(IE)]>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tr>
<td align="center" valign="top" width="600">
<![endif]-->
<table border='0' cellpadding='0' cellspacing='0' width='100%' style='max-width: 600px'>
<tr>
<td
align='left'
bgcolor='#ffffff'
style="
padding: 36px 24px 0;
font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
border-top: 3px solid #d4dadf;
"
>
<h1
style='
margin: 0;
font-size: 32px;
font-weight: 700;
letter-spacing: -1px;
line-height: 48px;
'
>
Hello,
{{name}}!
</h1>
</td>
</tr>
</table>
<!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
<!-- end hero -->
<!-- start copy block -->
<tr>
<td align='center' bgcolor='#e9ecef'>
<!--[if (gte mso 9)|(IE)]>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tr>
<td align="center" valign="top" width="600">
<![endif]-->
<table border='0' cellpadding='0' cellspacing='0' width='100%' style='max-width: 600px'>
<!-- start copy -->
<tr>
<td
align='left'
bgcolor='#ffffff'
style="
padding: 24px;
font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
"
>
<p style='margin: 0'>
Tap the button below to reset your account password. If you didn't request a new password, you can
safely delete this email.
</p>
</td>
</tr>
<!-- end copy -->
<!-- start button -->
<tr>
<td align='left' bgcolor='#ffffff'>
<table border='0' cellpadding='0' cellspacing='0' width='100%'>
<tr>
<td align='center' bgcolor='#ffffff' style='padding: 12px'>
<table border='0' cellpadding='0' cellspacing='0'>
<tr>
<td align='center' bgcolor='#1a82e2' style='border-radius: 6px'>
<a
href='{{url}}'
target='_blank'
style="
display: inline-block;
padding: 16px 36px;
font-family: 'Source Sans Pro', Helvetica, Arial,
sans-serif;
font-size: 16px;
color: #ffffff;
text-decoration: none;
border-radius: 6px;
"
>Reset Your Password</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- end button -->
<!-- start copy -->
<tr>
<td
align='left'
bgcolor='#ffffff'
style="
padding: 24px;
font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
"
>
<p style='margin: 0'>
If that doesn't work, copy and paste the following link in your browser:
</p>
<p style='margin: 0'>
<a href='{{url}}' target='_blank'>{{url}}</a>
</p>
</td>
</tr>
<!-- end copy -->
<!-- start copy -->
<tr>
<td
align='left'
bgcolor='#ffffff'
style="
padding: 24px;
font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
border-bottom: 3px solid #d4dadf;
"
>
<p style='margin: 0'>
Cheers,<br />
<a href='https://www.amruthpillai.com/'>Amruth Pillai</a>
</p>
</td>
</tr>
<!-- end copy -->
</table>
<!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
<!-- end copy block -->
<!-- start footer -->
<tr>
<td align='center' bgcolor='#e9ecef' style='padding: 24px'>
<!--[if (gte mso 9)|(IE)]>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="600">
<tr>
<td align="center" valign="top" width="600">
<![endif]-->
<table border='0' cellpadding='0' cellspacing='0' width='100%' style='max-width: 600px'>
<!-- start permission -->
<tr>
<td
align='center'
bgcolor='#e9ecef'
style="
padding: 12px 24px;
font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
color: #666;
"
>
<p style='margin: 0'>
You received this email because we received a request for resetting the password for your account. If
you didn't request a password reset you can safely delete this email.
</p>
</td>
</tr>
<!-- end permission -->
</table>
<!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
<![endif]-->
</td>
</tr>
<!-- end footer -->
</table>
<!-- end body -->
</body>
</html>

View File

@ -72,7 +72,7 @@ export class AuthService {
}
forgotPassword(email: string) {
this.usersService.generateResetToken(email);
return this.usersService.generateResetToken(email);
}
async resetPassword(resetPasswordDto: ResetPasswordDto) {

View File

@ -13,13 +13,15 @@ const validationSchema = Joi.object({
TZ: Joi.string().default('UTC'),
PORT: Joi.number().default(3100),
SECRET_KEY: Joi.string().required(),
NODE_ENV: Joi.string().valid('development', 'production').default('development'),
// URLs
APP_URL: Joi.string().default('http://localhost:3000'),
SERVER_URL: Joi.string().default('http://localhost:3100'),
NODE_ENV: Joi.string().valid('development', 'production').default('development'),
// Database
POSTGRES_HOST: Joi.string().required(),
POSTGRES_PORT: Joi.string().default(5432),
POSTGRES_PORT: Joi.number().default(5432),
POSTGRES_USERNAME: Joi.string().required(),
POSTGRES_PASSWORD: Joi.string().required(),
POSTGRES_DATABASE: Joi.string().required(),
@ -33,7 +35,7 @@ const validationSchema = Joi.object({
// Mail
MAIL_HOST: Joi.string().allow(''),
MAIL_PORT: Joi.string().allow(''),
MAIL_PORT: Joi.number().default(465),
MAIL_USERNAME: Joi.string().allow(''),
MAIL_PASSWORD: Joi.string().allow(''),
});

View File

@ -2,7 +2,7 @@ import { registerAs } from '@nestjs/config';
export default registerAs('mail', () => ({
host: process.env.MAIL_HOST,
port: parseInt(process.env.MAIL_PORT, 10),
port: parseInt(process.env.MAIL_PORT, 10) || 465,
username: process.env.MAIL_USERNAME,
password: process.env.MAIL_PASSWORD,
from: process.env.MAIL_FROM,

View File

@ -19,8 +19,6 @@ import { User } from '@/users/entities/user.entity';
database: configService.get<string>('postgres.database'),
synchronize: true,
entities: [User, Resume],
migrations: [__dirname + 'migrations'],
cli: { migrationsDir: __dirname + 'migrations' },
}),
}),
],

View File

@ -1,10 +0,0 @@
import { Controller, Get, Render } from '@nestjs/common';
@Controller('mail')
export class MailController {
@Get('forgot-password')
@Render('forgot-password')
forgotPassword() {
return { name: 'Amruth', url: 'https://amruthpillai.com/' };
}
}

View File

@ -1,7 +1,6 @@
import { DynamicModule, Global, Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MailController } from './mail.controller';
import { MailService } from './mail.service';
@Global()
@ -12,7 +11,6 @@ export class MailModule {
static register(): DynamicModule {
return {
module: MailModule,
controllers: [MailController],
providers: [MailService],
exports: [MailService],
};

View File

@ -15,7 +15,7 @@ export class MailService {
this.transporter = createTransport(
{
host: this.configService.get<string>('mail.host'),
port: this.configService.get<number>('mail.host'),
port: this.configService.get<number>('mail.port'),
auth: {
user: this.configService.get<string>('mail.username'),
pass: this.configService.get<string>('mail.password'),
@ -28,8 +28,9 @@ export class MailService {
}
async sendForgotPasswordEmail(user: User, resetToken: string) {
const url = `http://localhost:3000?modal=auth.reset&resetToken=${resetToken}`;
const templateSource = readFileSync(join(__dirname, 'templates/forgot-password.hbs'), 'utf-8');
const appUrl = this.configService.get<string>('app.url');
const url = `${appUrl}?modal=auth.reset&resetToken=${resetToken}`;
const templateSource = readFileSync(join(__dirname, 'assets/templates/forgot-password.hbs'), 'utf-8');
const template = compile(templateSource);
const html = template({ name: user.name, url });

View File

@ -1,68 +0,0 @@
<html lang='en'>
<head>
<meta charset='UTF-8' />
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<title>Reset Password | Reactive Resume</title>
<link rel='preconnect' href='https://fonts.googleapis.com' />
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin />
<link href='https://fonts.googleapis.com/css2?family=Inter&display=swap' rel='stylesheet' />
<style>
.container { margin: 0; padding: 48px 0; color: #eee; font-size: 14px; background-color: #222;
font-family: 'Inter', sans-serif; display: flex; align-items: center; justify-content: center;
} .inner { display: flex; align-items: center; flex-direction: column; } .logo {
padding-bottom: 32px; } .box { max-width: 640px; padding: 8px 16px; border-radius: 4px;
border: 1px solid #444; } .paragraph { line-height: 1.75em; } .button { display: inline-block;
color: #222; margin: 6px 0; background-color: #eee; padding: 10px 16px; border-radius: 4px;
text-decoration: none; } .footer { opacity: 0.5; align-self: flex-start; margin-top: 16px;
max-width: 420px; font-size: 12px; } .footer span { display: block; line-height: 1.75em; }
</style>
</head>
<body class='container'>
<div class='inner'>
<a href='https://rxresu.me'>
<img
class='logo'
src='https://raw.githubusercontent.com/AmruthPillai/Reactive-Resume/develop/static/images/logo.png'
alt='Reactive Resume'
width='128px'
/>
</a>
<div class='box'>
<p class='paragraph'>Hey {{name}},</p>
<p class='paragraph'>
Trouble signing in? Don't worry, resetting your password is easy.
<br />
We'll have you up and running in no time.
</p>
<p class='paragraph'>
Just click the button below to set a new password.
<br />
But hurry, because the link expires in 30 minutes.
</p>
<a class='button' href={{url}} target='_blank'>
Reset your password
</a>
<p class='paragraph'>If you did not request to change your password, then you can safely
ignore this email.</p>
</div>
<footer class='footer'>
<span>By the community, for the community.</span>
<span>Thank you for using Reactive Resume.</span>
</footer>
</div>
</body>
</html>

View File

@ -1,11 +1,17 @@
{
"name": "reactive-resume",
"private": true,
"version": "3.0.0",
"license": "MIT",
"engines": {
"npm": ">=6.0.0",
"node": ">=14.0.0"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"scripts": {
"serve": "nx run-many --target=serve --all",
"test": "nx run-many --target=test --all",
@ -18,12 +24,6 @@
"commit": "changeset && cz",
"prepare": "husky install"
},
"private": true,
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"dependencies": {
"@date-io/dayjs": "^2.13.1",
"@emotion/css": "^11.7.1",
@ -45,11 +45,10 @@
"@nestjs/schedule": "^1.0.2",
"@nestjs/schematics": "^8.0.7",
"@nestjs/serve-static": "^2.2.2",
"@nestjs/testing": "^8.4.0",
"@nestjs/typeorm": "^8.0.3",
"@nrwl/next": "13.8.3",
"@nrwl/tao": "13.8.3",
"@nrwl/workspace": "13.8.3",
"@nrwl/next": "13.8.4",
"@nrwl/tao": "13.8.4",
"@nrwl/workspace": "13.8.4",
"@reduxjs/toolkit": "^1.8.0",
"axios": "^0.26.0",
"bcrypt": "^5.0.1",
@ -113,15 +112,15 @@
"@babel/preset-env": "^7.16.11",
"@changesets/cli": "^2.21.0",
"@nestjs/testing": "^8.4.0",
"@nrwl/cli": "13.8.3",
"@nrwl/eslint-plugin-nx": "13.8.3",
"@nrwl/jest": "13.8.3",
"@nrwl/linter": "13.8.3",
"@nrwl/cli": "13.8.4",
"@nrwl/eslint-plugin-nx": "13.8.4",
"@nrwl/jest": "13.8.4",
"@nrwl/linter": "13.8.4",
"@nrwl/nest": "^13.8.4",
"@nrwl/node": "13.8.3",
"@nrwl/node": "13.8.4",
"@nrwl/nx-cloud": "latest",
"@nrwl/react": "13.8.3",
"@nrwl/web": "13.8.3",
"@nrwl/react": "13.8.4",
"@nrwl/web": "13.8.4",
"@testing-library/react": "12.1.3",
"@testing-library/react-hooks": "7.0.2",
"@types/bcrypt": "^5.0.0",
@ -137,12 +136,12 @@
"@types/passport-local": "^1.0.34",
"@types/react": "17.0.39",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-dom": "17.0.11",
"@types/react-dom": "17.0.13",
"@types/tailwindcss": "^3.0.9",
"@types/uuid": "^8.3.4",
"@types/webfontloader": "^1.6.34",
"@typescript-eslint/eslint-plugin": "~5.12.1",
"@typescript-eslint/parser": "~5.10.2",
"@typescript-eslint/eslint-plugin": "~5.10.0",
"@typescript-eslint/parser": "~5.10.0",
"autoprefixer": "^10.4.2",
"babel-jest": "27.5.1",
"babel-loader": "^8.2.3",
@ -152,10 +151,10 @@
"cz-conventional-changelog": "^3.3.0",
"eslint": "~8.10.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "8.4.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jsx-a11y": "6.5.1",
"eslint-plugin-react": "7.29.2",
"eslint-plugin-react": "7.29.3",
"eslint-plugin-react-hooks": "4.3.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
@ -168,7 +167,9 @@
"reflect-metadata": "^0.1.13",
"regenerator-runtime": "0.13.9",
"sass": "^1.49.9",
"typescript": "~4.5.5",
"ts-jest": "^27.1.3",
"ts-node": "^10.6.0",
"typescript": "<4.6.0",
"webpack": "^5.70.0"
}
}

File diff suppressed because it is too large Load Diff