mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-12 15:52:56 +10:00
feat(health): add health checks to server api
This commit is contained in:
@ -41,7 +41,7 @@ const Profiles = () => {
|
|||||||
<footer className="flex justify-end">
|
<footer className="flex justify-end">
|
||||||
<Button variant="outlined" startIcon={<Add />} onClick={handleAdd}>
|
<Button variant="outlined" startIcon={<Add />} onClick={handleAdd}>
|
||||||
{t('builder.common.actions.add', {
|
{t('builder.common.actions.add', {
|
||||||
section: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
|
token: t('builder.leftSidebar.sections.profiles.heading', { count: 1 }),
|
||||||
})}
|
})}
|
||||||
</Button>
|
</Button>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -122,7 +122,7 @@ const LoginModal: React.FC = () => {
|
|||||||
startIcon={<Google />}
|
startIcon={<Google />}
|
||||||
onClick={handleLoginWithGoogle}
|
onClick={handleLoginWithGoogle}
|
||||||
>
|
>
|
||||||
{t('modals.auth.login.actions.login-google')}
|
{t('modals.auth.login.actions.google')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
|
<Button type="submit" onClick={handleSubmit(onSubmit)} disabled={isLoading}>
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
|
import env from '@beam-australia/react-env';
|
||||||
import { joiResolver } from '@hookform/resolvers/joi';
|
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 { Button, TextField } from '@mui/material';
|
||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
import { Trans, useTranslation } from 'next-i18next';
|
import { Trans, useTranslation } from 'next-i18next';
|
||||||
|
import { GoogleLoginResponse, GoogleLoginResponseOffline, useGoogleLogin } from 'react-google-login';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
|
|
||||||
import BaseModal from '@/components/shared/BaseModal';
|
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 { ServerError } from '@/services/axios';
|
||||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||||
import { setModalState } from '@/store/modal/modalSlice';
|
import { setModalState } from '@/store/modal/modalSlice';
|
||||||
@ -56,6 +58,19 @@ const RegisterModal: React.FC = () => {
|
|||||||
|
|
||||||
const { mutateAsync, isLoading } = useMutation<void, ServerError, RegisterParams>(registerUser);
|
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 = () => {
|
const handleClose = () => {
|
||||||
dispatch(setModalState({ modal: 'auth.register', state: { open: false } }));
|
dispatch(setModalState({ modal: 'auth.register', state: { open: false } }));
|
||||||
reset();
|
reset();
|
||||||
@ -63,7 +78,6 @@ const RegisterModal: React.FC = () => {
|
|||||||
|
|
||||||
const onSubmit = async ({ name, username, email, password }: FormData) => {
|
const onSubmit = async ({ name, username, email, password }: FormData) => {
|
||||||
await mutateAsync({ name, username, email, password });
|
await mutateAsync({ name, username, email, password });
|
||||||
|
|
||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,6 +86,10 @@ const RegisterModal: React.FC = () => {
|
|||||||
dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
|
dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLoginWithGoogle = () => {
|
||||||
|
signIn();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseModal
|
<BaseModal
|
||||||
icon={<HowToReg />}
|
icon={<HowToReg />}
|
||||||
@ -79,9 +97,21 @@ const RegisterModal: React.FC = () => {
|
|||||||
heading={t('modals.auth.register.heading')}
|
heading={t('modals.auth.register.heading')}
|
||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
footerChildren={
|
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>
|
<p>{t('modals.auth.register.body')}</p>
|
||||||
|
|||||||
@ -45,10 +45,10 @@ const ProfileModal: React.FC = () => {
|
|||||||
const isEditMode = useMemo(() => !!item, [item]);
|
const isEditMode = useMemo(() => !!item, [item]);
|
||||||
|
|
||||||
const addText = t('builder.common.actions.add', {
|
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', {
|
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>({
|
const { reset, control, handleSubmit } = useForm<FormData>({
|
||||||
|
|||||||
@ -147,18 +147,6 @@ const Home: NextPage = () => {
|
|||||||
<h6>{t('landing.links.heading')}</h6>
|
<h6>{t('landing.links.heading')}</h6>
|
||||||
|
|
||||||
<div>
|
<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>
|
<Link href="/meta/privacy" passHref>
|
||||||
<Button variant="text" startIcon={<LinkIcon />}>
|
<Button variant="text" startIcon={<LinkIcon />}>
|
||||||
{t('landing.links.links.privacy')}
|
{t('landing.links.links.privacy')}
|
||||||
@ -170,6 +158,18 @@ const Home: NextPage = () => {
|
|||||||
{t('landing.links.links.service')}
|
{t('landing.links.links.service')}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
"login": {
|
"login": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"login": "Login",
|
"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.",
|
"body": "Please enter your username and password associated with your account to login and access, manage and share your resumes.",
|
||||||
"form": {
|
"form": {
|
||||||
@ -34,7 +34,8 @@
|
|||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"register": "Register"
|
"register": "Register",
|
||||||
|
"google": "Register with Google"
|
||||||
},
|
},
|
||||||
"body": "Please enter your personal information to create an account.",
|
"body": "Please enter your personal information to create an account.",
|
||||||
"form": {
|
"form": {
|
||||||
@ -112,7 +113,7 @@
|
|||||||
"actions": {
|
"actions": {
|
||||||
"upload-json": "Upload JSON"
|
"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"
|
"heading": "Import From Reactive Resume"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "reactive-resume",
|
"name": "reactive-resume",
|
||||||
"version": "3.0.0-beta.2",
|
"version": "3.0.0-beta.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"schema",
|
"schema",
|
||||||
|
|||||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@ -188,6 +188,7 @@ importers:
|
|||||||
'@nestjs/schedule': ^1.0.2
|
'@nestjs/schedule': ^1.0.2
|
||||||
'@nestjs/schematics': ^8.0.8
|
'@nestjs/schematics': ^8.0.8
|
||||||
'@nestjs/serve-static': ^2.2.2
|
'@nestjs/serve-static': ^2.2.2
|
||||||
|
'@nestjs/terminus': ^8.0.4
|
||||||
'@nestjs/typeorm': ^8.0.3
|
'@nestjs/typeorm': ^8.0.3
|
||||||
'@reactive-resume/schema': workspace:*
|
'@reactive-resume/schema': workspace:*
|
||||||
'@sendgrid/mail': ^7.6.1
|
'@sendgrid/mail': ^7.6.1
|
||||||
@ -240,6 +241,7 @@ importers:
|
|||||||
'@nestjs/platform-express': 8.4.0_31e7036b193d6d3c9cadab18cbb4af84
|
'@nestjs/platform-express': 8.4.0_31e7036b193d6d3c9cadab18cbb4af84
|
||||||
'@nestjs/schedule': 1.0.2_1ce925e2290a1cea9e3700e8a60baeb5
|
'@nestjs/schedule': 1.0.2_1ce925e2290a1cea9e3700e8a60baeb5
|
||||||
'@nestjs/serve-static': 2.2.2_31e7036b193d6d3c9cadab18cbb4af84
|
'@nestjs/serve-static': 2.2.2_31e7036b193d6d3c9cadab18cbb4af84
|
||||||
|
'@nestjs/terminus': 8.0.4_44ad68f90df6df0ad3d3ea7593df94f3
|
||||||
'@nestjs/typeorm': 8.0.3_d17aee4fbe284d59b832be708c000fe0
|
'@nestjs/typeorm': 8.0.3_d17aee4fbe284d59b832be708c000fe0
|
||||||
'@sendgrid/mail': 7.6.1
|
'@sendgrid/mail': 7.6.1
|
||||||
'@types/passport': 1.0.7
|
'@types/passport': 1.0.7
|
||||||
@ -1413,6 +1415,21 @@ packages:
|
|||||||
path-to-regexp: 0.1.7
|
path-to-regexp: 0.1.7
|
||||||
dev: false
|
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:
|
/@nestjs/typeorm/8.0.3_d17aee4fbe284d59b832be708c000fe0:
|
||||||
resolution: {integrity: sha512-tf9rTXP6LeFInkwd+tktQhtLRsKp4RRYImprqT8gcHcJDx+xMP1IygnXELOKwF5vo2/mnhrGtBlRQ/iiS6170g==}
|
resolution: {integrity: sha512-tf9rTXP6LeFInkwd+tktQhtLRsKp4RRYImprqT8gcHcJDx+xMP1IygnXELOKwF5vo2/mnhrGtBlRQ/iiS6170g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2808,6 +2825,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/check-disk-space/3.1.0:
|
||||||
|
resolution: {integrity: sha512-4L3WVw4uPaBJocwnxTCWTTHc8mNu080pjCVBhZeWFdnaQBAremLHpJ1H90G+uEA0rJcW43fghYMsLBXID9X4Zg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/chokidar/3.5.3:
|
/chokidar/3.5.3:
|
||||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||||
engines: {node: '>= 8.10.0'}
|
engines: {node: '>= 8.10.0'}
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"@nestjs/platform-express": "^8.4.0",
|
"@nestjs/platform-express": "^8.4.0",
|
||||||
"@nestjs/schedule": "^1.0.2",
|
"@nestjs/schedule": "^1.0.2",
|
||||||
"@nestjs/serve-static": "^2.2.2",
|
"@nestjs/serve-static": "^2.2.2",
|
||||||
|
"@nestjs/terminus": "^8.0.4",
|
||||||
"@nestjs/typeorm": "^8.0.3",
|
"@nestjs/typeorm": "^8.0.3",
|
||||||
"@sendgrid/mail": "^7.6.1",
|
"@sendgrid/mail": "^7.6.1",
|
||||||
"@types/passport": "^1.0.7",
|
"@types/passport": "^1.0.7",
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { ConfigModule } from './config/config.module';
|
|||||||
import { DatabaseModule } from './database/database.module';
|
import { DatabaseModule } from './database/database.module';
|
||||||
import { HttpExceptionFilter } from './filters/http-exception.filter';
|
import { HttpExceptionFilter } from './filters/http-exception.filter';
|
||||||
import { FontsModule } from './fonts/fonts.module';
|
import { FontsModule } from './fonts/fonts.module';
|
||||||
|
import { HealthModule } from './health/health.module';
|
||||||
import { IntegrationsModule } from './integrations/integrations.module';
|
import { IntegrationsModule } from './integrations/integrations.module';
|
||||||
import { MailModule } from './mail/mail.module';
|
import { MailModule } from './mail/mail.module';
|
||||||
import { PrinterModule } from './printer/printer.module';
|
import { PrinterModule } from './printer/printer.module';
|
||||||
@ -32,6 +33,7 @@ import { UsersModule } from './users/users.module';
|
|||||||
FontsModule,
|
FontsModule,
|
||||||
IntegrationsModule,
|
IntegrationsModule,
|
||||||
PrinterModule,
|
PrinterModule,
|
||||||
|
HealthModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
|||||||
21
server/src/health/health.controller.ts
Normal file
21
server/src/health/health.controller.ts
Normal 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'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
server/src/health/health.module.ts
Normal file
11
server/src/health/health.module.ts
Normal 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 {}
|
||||||
Reference in New Issue
Block a user