feat(health): add health checks to server api

This commit is contained in:
Amruth Pillai 2022-03-10 09:25:15 +01:00
parent 8f48f5fcd6
commit eca80a1663
No known key found for this signature in database
GPG Key ID: E3C57DF9B80855AD
12 changed files with 114 additions and 26 deletions

View File

@ -41,7 +41,7 @@ const Profiles = () => {
<footer className="flex justify-end">
<Button variant="outlined" startIcon={<Add />} onClick={handleAdd}>
{t('builder.common.actions.add', {
section: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
})}
</Button>
</footer>

View File

@ -122,7 +122,7 @@ const LoginModal: React.FC = () => {
startIcon={<Google />}
onClick={handleLoginWithGoogle}
>
{t('modals.auth.login.actions.login-google')}
{t('modals.auth.login.actions.google')}
</Button>
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>

View File

@ -1,13 +1,15 @@
import env from '@beam-australia/react-env';
import { joiResolver } from '@hookform/resolvers/joi';
import { HowToReg } from '@mui/icons-material';
import { Google, HowToReg } from '@mui/icons-material';
import { Button, TextField } from '@mui/material';
import Joi from 'joi';
import { Trans, useTranslation } from 'next-i18next';
import { GoogleLoginResponse, GoogleLoginResponseOffline, useGoogleLogin } from 'react-google-login';
import { Controller, useForm } from 'react-hook-form';
import { useMutation } from 'react-query';
import BaseModal from '@/components/shared/BaseModal';
import { register as registerUser, RegisterParams } from '@/services/auth';
import { loginWithGoogle, LoginWithGoogleParams, register as registerUser, RegisterParams } from '@/services/auth';
import { ServerError } from '@/services/axios';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { setModalState } from '@/store/modal/modalSlice';
@ -56,6 +58,19 @@ const RegisterModal: React.FC = () => {
const { mutateAsync, isLoading } = useMutation<void, ServerError, RegisterParams>(registerUser);
const { mutateAsync: loginWithGoogleMutation } = useMutation<void, ServerError, LoginWithGoogleParams>(
loginWithGoogle
);
const { signIn } = useGoogleLogin({
clientId: env('GOOGLE_CLIENT_ID'),
onSuccess: async (response: GoogleLoginResponse | GoogleLoginResponseOffline) => {
await loginWithGoogleMutation({ accessToken: (response as GoogleLoginResponse).accessToken });
handleClose();
},
});
const handleClose = () => {
dispatch(setModalState({ modal: 'auth.register', state: { open: false } }));
reset();
@ -63,7 +78,6 @@ const RegisterModal: React.FC = () => {
const onSubmit = async ({ name, username, email, password }: FormData) => {
await mutateAsync({ name, username, email, password });
handleClose();
};
@ -72,6 +86,10 @@ const RegisterModal: React.FC = () => {
dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
};
const handleLoginWithGoogle = () => {
signIn();
};
return (
<BaseModal
icon={<HowToReg />}
@ -79,9 +97,21 @@ const RegisterModal: React.FC = () => {
heading={t('modals.auth.register.heading')}
handleClose={handleClose}
footerChildren={
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
{t('modals.auth.register.actions.register')}
</Button>
<>
<Button
type="submit"
variant="outlined"
disabled={isLoading}
startIcon={<Google />}
onClick={handleLoginWithGoogle}
>
{t('modals.auth.register.actions.google')}
</Button>
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
{t('modals.auth.register.actions.register')}
</Button>
</>
}
>
<p>{t('modals.auth.register.body')}</p>

View File

@ -45,10 +45,10 @@ const ProfileModal: React.FC = () => {
const isEditMode = useMemo(() => !!item, [item]);
const addText = t('builder.common.actions.add', {
section: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
});
const editText = t('builder.common.actions.edit', {
section: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
});
const { reset, control, handleSubmit } = useForm<FormData>({

View File

@ -147,18 +147,6 @@ const Home: NextPage = () => {
<h6>{t('landing.links.heading')}</h6>
<div>
<a href={GITHUB_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<LinkIcon />}>
{t('landing.links.links.github')}
</Button>
</a>
<a href={DONATION_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<LinkIcon />}>
{t('landing.links.links.donate')}
</Button>
</a>
<Link href="/meta/privacy" passHref>
<Button variant="text" startIcon={<LinkIcon />}>
{t('landing.links.links.privacy')}
@ -170,6 +158,18 @@ const Home: NextPage = () => {
{t('landing.links.links.service')}
</Button>
</Link>
<a href={GITHUB_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<LinkIcon />}>
{t('landing.links.links.github')}
</Button>
</a>
<a href={DONATION_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<LinkIcon />}>
{t('landing.links.links.donate')}
</Button>
</a>
</div>
</section>

View File

@ -16,7 +16,7 @@
"login": {
"actions": {
"login": "Login",
"login-google": "Login with Google"
"google": "Login with Google"
},
"body": "Please enter your username and password associated with your account to login and access, manage and share your resumes.",
"form": {
@ -34,7 +34,8 @@
},
"register": {
"actions": {
"register": "Register"
"register": "Register",
"google": "Register with Google"
},
"body": "Please enter your personal information to create an account.",
"form": {
@ -112,7 +113,7 @@
"actions": {
"upload-json": "Upload JSON"
},
"body": "If you have a JSON that was exported with the current version of Reactive Resume, you can import it back here to get an editable version again.",
"body": "If you have a JSON that was exported with the current version of Reactive Resume, you can import it back here to get an editable version again. Previous versions of Reactive Resume are unfortunately not supported at the moment.",
"heading": "Import From Reactive Resume"
}
},

View File

@ -1,6 +1,6 @@
{
"name": "reactive-resume",
"version": "3.0.0-beta.2",
"version": "3.0.0-beta.4",
"private": true,
"workspaces": [
"schema",

View File

@ -188,6 +188,7 @@ importers:
'@nestjs/schedule': ^1.0.2
'@nestjs/schematics': ^8.0.8
'@nestjs/serve-static': ^2.2.2
'@nestjs/terminus': ^8.0.4
'@nestjs/typeorm': ^8.0.3
'@reactive-resume/schema': workspace:*
'@sendgrid/mail': ^7.6.1
@ -240,6 +241,7 @@ importers:
'@nestjs/platform-express': 8.4.0_31e7036b193d6d3c9cadab18cbb4af84
'@nestjs/schedule': 1.0.2_1ce925e2290a1cea9e3700e8a60baeb5
'@nestjs/serve-static': 2.2.2_31e7036b193d6d3c9cadab18cbb4af84
'@nestjs/terminus': 8.0.4_44ad68f90df6df0ad3d3ea7593df94f3
'@nestjs/typeorm': 8.0.3_d17aee4fbe284d59b832be708c000fe0
'@sendgrid/mail': 7.6.1
'@types/passport': 1.0.7
@ -1413,6 +1415,21 @@ packages:
path-to-regexp: 0.1.7
dev: false
/@nestjs/terminus/8.0.4_44ad68f90df6df0ad3d3ea7593df94f3:
resolution: {integrity: sha512-KjeY7VLt0Az6pA2wO67nkL1QbE68yBb+FLZ7+aa+C/g/IKoDR668nqSuFzJarBrnFBTGEwDD09BwsgqkmymrbQ==}
peerDependencies:
'@nestjs/common': 8.x
'@nestjs/core': 8.x
reflect-metadata: 0.1.x
rxjs: 7.x
dependencies:
'@nestjs/common': 8.4.0_add13df2cdecb4b62cd3f7664ea82e18
'@nestjs/core': 8.4.0_ded80713f50ec1c99e9ba95af1765d72
check-disk-space: 3.1.0
reflect-metadata: 0.1.13
rxjs: 7.5.5
dev: false
/@nestjs/typeorm/8.0.3_d17aee4fbe284d59b832be708c000fe0:
resolution: {integrity: sha512-tf9rTXP6LeFInkwd+tktQhtLRsKp4RRYImprqT8gcHcJDx+xMP1IygnXELOKwF5vo2/mnhrGtBlRQ/iiS6170g==}
peerDependencies:
@ -2808,6 +2825,11 @@ packages:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
dev: true
/check-disk-space/3.1.0:
resolution: {integrity: sha512-4L3WVw4uPaBJocwnxTCWTTHc8mNu080pjCVBhZeWFdnaQBAremLHpJ1H90G+uEA0rJcW43fghYMsLBXID9X4Zg==}
engines: {node: '>=12'}
dev: false
/chokidar/3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}

View File

@ -20,6 +20,7 @@
"@nestjs/platform-express": "^8.4.0",
"@nestjs/schedule": "^1.0.2",
"@nestjs/serve-static": "^2.2.2",
"@nestjs/terminus": "^8.0.4",
"@nestjs/typeorm": "^8.0.3",
"@sendgrid/mail": "^7.6.1",
"@types/passport": "^1.0.7",

View File

@ -9,6 +9,7 @@ import { ConfigModule } from './config/config.module';
import { DatabaseModule } from './database/database.module';
import { HttpExceptionFilter } from './filters/http-exception.filter';
import { FontsModule } from './fonts/fonts.module';
import { HealthModule } from './health/health.module';
import { IntegrationsModule } from './integrations/integrations.module';
import { MailModule } from './mail/mail.module';
import { PrinterModule } from './printer/printer.module';
@ -32,6 +33,7 @@ import { UsersModule } from './users/users.module';
FontsModule,
IntegrationsModule,
PrinterModule,
HealthModule,
],
providers: [
{

View File

@ -0,0 +1,21 @@
import { Controller, Get } from '@nestjs/common';
import { HealthCheck, HealthCheckService, HttpHealthIndicator, TypeOrmHealthIndicator } from '@nestjs/terminus';
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
private http: HttpHealthIndicator
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.db.pingCheck('database'),
() => this.http.pingCheck('app', 'https://rxresu.me'),
() => this.http.pingCheck('docs', 'https://beta.rxresu.me'),
]);
}
}

View File

@ -0,0 +1,11 @@
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';
import { HealthController } from './health.controller';
@Module({
imports: [HttpModule, TerminusModule],
controllers: [HealthController],
})
export class HealthModule {}