refactor(server): use proxy mechanisms to remove server_url config
This commit is contained in:
parent
e52edaa552
commit
5a2594eb88
|
@ -3,8 +3,7 @@ TZ=UTC
|
|||
SECRET_KEY=change-me
|
||||
|
||||
# URLs
|
||||
PUBLIC_APP_URL=http://localhost:3000
|
||||
PUBLIC_SERVER_URL=http://localhost:3100
|
||||
PUBLIC_URL=http://localhost:3000
|
||||
|
||||
# Database
|
||||
POSTGRES_HOST=localhost
|
||||
|
|
|
@ -15,6 +15,19 @@ const nextConfig = {
|
|||
domains: ['www.gravatar.com'],
|
||||
},
|
||||
|
||||
async rewrites() {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: 'http://localhost:3100/api/:path*',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
|
||||
// Hack to make Tailwind darkMode 'class' strategy with CSS Modules
|
||||
// Ref: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomment-968368156
|
||||
webpack: (config) => {
|
||||
|
|
|
@ -16,7 +16,7 @@ import Page from '@/components/build/Center/Page';
|
|||
import { ServerError } from '@/services/axios';
|
||||
import { printResumeAsPdf, PrintResumeAsPdfParams } from '@/services/printer';
|
||||
import { fetchResumeByShortId } from '@/services/resume';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { useAppDispatch } from '@/store/hooks';
|
||||
import { setResume } from '@/store/resume/resumeSlice';
|
||||
import styles from '@/styles/pages/Preview.module.scss';
|
||||
|
||||
|
@ -26,34 +26,18 @@ type QueryParams = {
|
|||
|
||||
type Props = {
|
||||
shortId: string;
|
||||
resume?: Resume;
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async ({ query, locale = 'en' }) => {
|
||||
const { shortId } = query as QueryParams;
|
||||
|
||||
try {
|
||||
const resume = await fetchResumeByShortId({ shortId });
|
||||
|
||||
return { props: { shortId, resume, ...(await serverSideTranslations(locale, ['common'])) } };
|
||||
} catch {
|
||||
return { props: { shortId, ...(await serverSideTranslations(locale, ['common'])) } };
|
||||
}
|
||||
return { props: { shortId, ...(await serverSideTranslations(locale, ['common'])) } };
|
||||
};
|
||||
|
||||
const Preview: NextPage<Props> = ({ shortId, resume: initialData }) => {
|
||||
const Preview: NextPage<Props> = ({ shortId }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const resume = useAppSelector((state) => state.resume);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData && !isEmpty(initialData)) {
|
||||
dispatch(setResume(initialData));
|
||||
}
|
||||
}, [dispatch, initialData]);
|
||||
|
||||
useQuery<Resume>(`resume/${shortId}`, () => fetchResumeByShortId({ shortId }), {
|
||||
initialData,
|
||||
const { data: resume } = useQuery<Resume>(`resume/${shortId}`, () => fetchResumeByShortId({ shortId }), {
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnWindowFocus: false,
|
||||
|
@ -64,7 +48,11 @@ const Preview: NextPage<Props> = ({ shortId, resume: initialData }) => {
|
|||
|
||||
const { mutateAsync, isLoading } = useMutation<string, ServerError, PrintResumeAsPdfParams>(printResumeAsPdf);
|
||||
|
||||
if (isEmpty(resume)) return null;
|
||||
useEffect(() => {
|
||||
if (resume) dispatch(setResume(resume));
|
||||
}, [resume, dispatch]);
|
||||
|
||||
if (!resume || isEmpty(resume)) return null;
|
||||
|
||||
const layout: string[][][] = get(resume, 'metadata.layout', []);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import env from '@beam-australia/react-env';
|
||||
import _axios, { AxiosError } from 'axios';
|
||||
import Router from 'next/router';
|
||||
|
||||
|
@ -13,13 +12,7 @@ export type ServerError = {
|
|||
path: string;
|
||||
};
|
||||
|
||||
const axios = _axios.create({
|
||||
baseURL: `${env('SERVER_URL')}/api`,
|
||||
});
|
||||
|
||||
export const uninterceptedAxios = _axios.create({
|
||||
baseURL: `${env('SERVER_URL')}/api`,
|
||||
});
|
||||
const axios = _axios.create({ baseURL: '/api' });
|
||||
|
||||
axios.interceptors.request.use((config) => {
|
||||
const { accessToken } = store.getState().auth;
|
||||
|
|
|
@ -2,6 +2,8 @@ import { Resume } from '@reactive-resume/schema';
|
|||
import { AxiosResponse } from 'axios';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
import isBrowser from '@/utils/isBrowser';
|
||||
|
||||
import axios from './axios';
|
||||
|
||||
export type CreateResumeParams = {
|
||||
|
@ -69,9 +71,10 @@ export const fetchResumeByIdentifier = async ({
|
|||
slug,
|
||||
options = { secretKey: '' },
|
||||
}: FetchResumeByIdentifierParams) => {
|
||||
const prefix = !isBrowser && process.env.NODE_ENV === 'development' ? 'http://localhost:3100/api' : '';
|
||||
const requestOptions = isEmpty(options.secretKey) ? {} : { params: { secretKey: options.secretKey } };
|
||||
|
||||
return axios.get<Resume>(`/resume/${username}/${slug}`, requestOptions).then((res) => res.data);
|
||||
return axios.get<Resume>(`${prefix}/resume/${username}/${slug}`, requestOptions).then((res) => res.data);
|
||||
};
|
||||
|
||||
export const createResume = (createResumeParams: CreateResumeParams) =>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import env from '@beam-australia/react-env';
|
||||
import { Resume } from '@reactive-resume/schema';
|
||||
import get from 'lodash/get';
|
||||
|
||||
|
@ -19,7 +20,7 @@ const getResumeUrl = (resume: Resume, options: Options = defaultOptions): string
|
|||
const slug: string = get(resume, 'slug');
|
||||
|
||||
let url = '';
|
||||
let hostname = '';
|
||||
let hostname = env('URL');
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
hostname = window.location.origin;
|
||||
|
|
|
@ -7,8 +7,6 @@ services:
|
|||
env_file: .env
|
||||
ports:
|
||||
- 3100:3100
|
||||
depends_on:
|
||||
- postgres
|
||||
|
||||
client:
|
||||
image: amruthpillai/reactive-resume:client-latest
|
||||
|
@ -18,6 +16,3 @@ services:
|
|||
- 3000:3000
|
||||
depends_on:
|
||||
- server
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
|
|
|
@ -11,29 +11,29 @@ services:
|
|||
- ./scripts/database/initialize.sql:/docker-entrypoint-initdb.d/initialize.sql
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
||||
server:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: server/Dockerfile
|
||||
container_name: server
|
||||
env_file: .env
|
||||
environment:
|
||||
- POSTGRES_HOST=postgres
|
||||
ports:
|
||||
- 3100:3100
|
||||
depends_on:
|
||||
- postgres
|
||||
# server:
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: server/Dockerfile
|
||||
# container_name: server
|
||||
# env_file: .env
|
||||
# environment:
|
||||
# - POSTGRES_HOST=postgres
|
||||
# ports:
|
||||
# - 3100:3100
|
||||
# depends_on:
|
||||
# - postgres
|
||||
|
||||
client:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: client/Dockerfile
|
||||
container_name: client
|
||||
env_file: .env
|
||||
ports:
|
||||
- 3000:3000
|
||||
depends_on:
|
||||
- server
|
||||
# client:
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: client/Dockerfile
|
||||
# container_name: client
|
||||
# env_file: .env
|
||||
# ports:
|
||||
# - 3000:3000
|
||||
# depends_on:
|
||||
# - server
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
|
|
|
@ -192,6 +192,7 @@ importers:
|
|||
'@reactive-resume/schema': workspace:*
|
||||
'@sendgrid/mail': ^7.6.1
|
||||
'@types/bcrypt': ^5.0.0
|
||||
'@types/cookie-parser': ^1.4.2
|
||||
'@types/express': ^4.17.13
|
||||
'@types/multer': ^1.4.7
|
||||
'@types/node': ^17.0.21
|
||||
|
@ -271,6 +272,7 @@ importers:
|
|||
'@nestjs/schematics': 8.0.7_typescript@4.5.5
|
||||
'@reactive-resume/schema': link:../schema
|
||||
'@types/bcrypt': 5.0.0
|
||||
'@types/cookie-parser': 1.4.2
|
||||
'@types/express': 4.17.13
|
||||
'@types/multer': 1.4.7
|
||||
'@types/node': 17.0.21
|
||||
|
@ -1965,6 +1967,12 @@ packages:
|
|||
dependencies:
|
||||
'@types/node': 17.0.21
|
||||
|
||||
/@types/cookie-parser/1.4.2:
|
||||
resolution: {integrity: sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==}
|
||||
dependencies:
|
||||
'@types/express': 4.17.13
|
||||
dev: true
|
||||
|
||||
/@types/debug/4.1.7:
|
||||
resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
|
||||
dependencies:
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
"@nestjs/schematics": "^8.0.7",
|
||||
"@reactive-resume/schema": "workspace:*",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^17.0.21",
|
||||
|
|
|
@ -18,6 +18,7 @@ import { UsersModule } from './users/users.module';
|
|||
@Module({
|
||||
imports: [
|
||||
ServeStaticModule.forRoot({
|
||||
serveRoot: '/api',
|
||||
rootPath: join(__dirname, 'assets'),
|
||||
}),
|
||||
ConfigModule,
|
||||
|
|
|
@ -5,6 +5,5 @@ export default registerAs('app', () => ({
|
|||
environment: process.env.NODE_ENV,
|
||||
secretKey: process.env.SECRET_KEY,
|
||||
port: parseInt(process.env.PORT, 10) || 3100,
|
||||
url: process.env.PUBLIC_APP_URL || 'http://localhost:3000',
|
||||
serverUrl: process.env.PUBLIC_SERVER_URL || 'http://localhost:3100',
|
||||
url: process.env.PUBLIC_URL || 'http://localhost:3000',
|
||||
}));
|
||||
|
|
|
@ -16,8 +16,7 @@ const validationSchema = Joi.object({
|
|||
NODE_ENV: Joi.string().valid('development', 'production').default('development'),
|
||||
|
||||
// URLs
|
||||
PUBLIC_APP_URL: Joi.string().default('http://localhost:3000'),
|
||||
PUBLIC_SERVER_URL: Joi.string().default('http://localhost:3100'),
|
||||
PUBLIC_URL: Joi.string().default('http://localhost:3000'),
|
||||
|
||||
// Database
|
||||
POSTGRES_HOST: Joi.string().required(),
|
||||
|
@ -38,6 +37,9 @@ const validationSchema = Joi.object({
|
|||
|
||||
// SendGrid
|
||||
SENDGRID_API_KEY: Joi.string().allow(''),
|
||||
SENDGRID_FORGOT_PASSWORD_TEMPLATE_ID: Joi.string().allow(''),
|
||||
SENDGRID_FROM_NAME: Joi.string().allow(''),
|
||||
SENDGRID_FROM_EMAIL: Joi.string().allow(''),
|
||||
});
|
||||
|
||||
@Module({
|
||||
|
|
|
@ -3,7 +3,6 @@ import { ConfigService } from '@nestjs/config';
|
|||
import { NestFactory } from '@nestjs/core';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import { join } from 'path';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
|
@ -15,24 +14,18 @@ const bootstrap = async () => {
|
|||
app.setGlobalPrefix(globalPrefix);
|
||||
|
||||
// Middleware
|
||||
app.enableCors({ credentials: true });
|
||||
app.enableShutdownHooks();
|
||||
app.use(cookieParser());
|
||||
|
||||
// Pipes
|
||||
app.useGlobalPipes(new ValidationPipe({ transform: true }));
|
||||
|
||||
// Email Templates
|
||||
app.setBaseViewsDir(join(__dirname, 'mail/templates'));
|
||||
app.setViewEngine('hbs');
|
||||
|
||||
const configService = app.get(ConfigService);
|
||||
const serverUrl = configService.get<number>('app.serverUrl');
|
||||
const port = configService.get<number>('app.port');
|
||||
|
||||
await app.listen(port);
|
||||
|
||||
Logger.log(`🚀 Server is running on: ${serverUrl}/${globalPrefix}`);
|
||||
Logger.log(`🚀 Server is up and running!`);
|
||||
};
|
||||
|
||||
bootstrap();
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ConfigService } from '@nestjs/config';
|
|||
import { SchedulerRegistry } from '@nestjs/schedule';
|
||||
import { mkdir, unlink, writeFile } from 'fs/promises';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { join, resolve } from 'path';
|
||||
import { join } from 'path';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { Browser, chromium } from 'playwright-chromium';
|
||||
|
||||
|
@ -28,7 +28,6 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
|||
async printAsPdf(username: string, slug: string): Promise<string> {
|
||||
const url = this.configService.get<string>('app.url');
|
||||
const secretKey = this.configService.get<string>('app.secretKey');
|
||||
const serverUrl = this.configService.get<string>('app.serverUrl');
|
||||
|
||||
const page = await this.browser.newPage();
|
||||
|
||||
|
@ -44,9 +43,9 @@ export class PrinterService implements OnModuleInit, OnModuleDestroy {
|
|||
});
|
||||
|
||||
const pdf = await PDFDocument.create();
|
||||
const directory = resolve('dist/assets/resumes');
|
||||
const directory = join(__dirname, '..', 'assets/exports');
|
||||
const filename = `RxResume_PDFExport_${nanoid()}.pdf`;
|
||||
const publicUrl = `${serverUrl}/resumes/${filename}`;
|
||||
const publicUrl = `/api/exports/${filename}`;
|
||||
|
||||
for (let index = 0; index < resumePages.length; index++) {
|
||||
await page.evaluate((page) => (document.body.innerHTML = page.innerHTML), resumePages[index]);
|
||||
|
|
|
@ -22,8 +22,8 @@ import { ResumeService } from './resume.service';
|
|||
storage: diskStorage({
|
||||
destination: async (req, _, cb) => {
|
||||
const userId = (req.user as User).id;
|
||||
const resumeId = req.params.id;
|
||||
const destination = join(__dirname, `assets/uploads/${userId}/${resumeId}`);
|
||||
const resumeId = +req.params.id;
|
||||
const destination = join(__dirname, '..', `assets/uploads/${userId}/${resumeId}`);
|
||||
|
||||
await mkdir(destination, { recursive: true });
|
||||
|
||||
|
|
|
@ -218,9 +218,8 @@ export class ResumeService {
|
|||
|
||||
async uploadPhoto(id: number, userId: number, filename: string) {
|
||||
const resume = await this.findOne(id, userId);
|
||||
const serverUrl = this.configService.get<string>('app.serverUrl');
|
||||
|
||||
const url = `${serverUrl}/uploads/${userId}/${id}/${filename}`;
|
||||
const url = `/api/uploads/${userId}/${id}/${filename}`;
|
||||
const updatedResume = set(resume, 'basics.photo.url', url);
|
||||
|
||||
return this.resumeRepository.save<Resume>(updatedResume);
|
||||
|
@ -228,8 +227,8 @@ export class ResumeService {
|
|||
|
||||
async deletePhoto(id: number, userId: number) {
|
||||
const resume = await this.findOne(id, userId);
|
||||
const key = new URL(resume.basics.photo.url).pathname;
|
||||
const photoPath = join(__dirname, 'assets', key);
|
||||
const filepath = new URL(resume.basics.photo.url).pathname;
|
||||
const photoPath = join(__dirname, '..', `assets/${filepath}`);
|
||||
const updatedResume = set(resume, 'basics.photo.url', '');
|
||||
|
||||
await unlink(photoPath);
|
||||
|
|
Loading…
Reference in New Issue