experiments with docker packaging, figuring out deploy plan
This commit is contained in:
parent
2aa3786f5f
commit
58160b2b6e
|
@ -1,4 +1,3 @@
|
|||
.git
|
||||
dist
|
||||
Dockerfile
|
||||
node_modules
|
||||
|
|
|
@ -4,12 +4,8 @@ WORKDIR /app
|
|||
|
||||
COPY ./package*.json .
|
||||
|
||||
RUN npm config set unsafe-perm true
|
||||
|
||||
RUN npm ci
|
||||
|
||||
RUN npm install sharp --ignore-scripts=false
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
NEXT_PUBLIC_APP_VERSION=$npm_package_version
|
||||
|
||||
# App & Server URLs
|
||||
NEXT_PUBLIC_APP_URL=$APP_URL
|
||||
NEXT_PUBLIC_SERVER_URL=$SERVER_URL
|
||||
|
||||
# Google OAuth
|
||||
NEXT_PUBLIC_GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID
|
|
@ -0,0 +1,18 @@
|
|||
import HttpBackend from 'i18next-http-backend';
|
||||
|
||||
const i18nConfig = {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
},
|
||||
debug: false,
|
||||
nsSeparator: '.',
|
||||
ns: ['common', 'modals', 'landing', 'dashboard', 'builder'],
|
||||
serializeConfig: false,
|
||||
use: [HttpBackend],
|
||||
backend: {
|
||||
loadPath: `${process.env.NEXT_PUBLIC_APP_URL}/locales/{{lng}}/{{ns}}.json`,
|
||||
},
|
||||
};
|
||||
|
||||
export default i18nConfig;
|
|
@ -1,14 +0,0 @@
|
|||
/**
|
||||
* @type {import('next-i18next').UserConfig}
|
||||
**/
|
||||
module.exports = {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
},
|
||||
debug: false,
|
||||
nsSeparator: '.',
|
||||
initImmediate: false,
|
||||
localePath: './apps/client/public/locales',
|
||||
ns: ['common', 'modals', 'landing', 'dashboard', 'builder'],
|
||||
};
|
|
@ -1,12 +1,13 @@
|
|||
const withNx = require('@nrwl/next/plugins/with-nx');
|
||||
|
||||
const { i18n } = require('./next-i18next.config');
|
||||
|
||||
/**
|
||||
* @type {import('@nrwl/next/plugins/with-nx').WithNxOptions}
|
||||
**/
|
||||
const nextConfig = {
|
||||
i18n,
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
},
|
||||
|
||||
nx: {
|
||||
svgr: false,
|
||||
|
|
|
@ -4,11 +4,11 @@ import { GetServerSideProps, NextPage } from 'next';
|
|||
import dynamic from 'next/dynamic';
|
||||
import Head from 'next/head';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import i18nConfig from 'next-i18next.config';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { useEffect } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import i18nConfig from '@/i18n/index';
|
||||
import { fetchResumeByIdentifier } from '@/services/resume';
|
||||
import { useAppDispatch } from '@/store/hooks';
|
||||
import { setResume } from '@/store/resume/resumeSlice';
|
||||
|
|
|
@ -9,12 +9,12 @@ import { GetServerSideProps, NextPage } from 'next';
|
|||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import i18nConfig from 'next-i18next.config';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
|
||||
import i18nConfig from '@/i18n/index';
|
||||
import { ServerError } from '@/services/axios';
|
||||
import { printResumeAsPdf, PrintResumeAsPdfParams } from '@/services/printer';
|
||||
import { fetchResumeByIdentifier } from '@/services/resume';
|
||||
|
@ -39,7 +39,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ query, loc
|
|||
const { username, slug } = query as QueryParams;
|
||||
|
||||
try {
|
||||
const resume = await fetchResumeByIdentifier({ username, slug, options: { withHost: true } });
|
||||
const resume = await fetchResumeByIdentifier({ username, slug });
|
||||
|
||||
return {
|
||||
props: { username, slug, resume, ...(await serverSideTranslations(locale, ['common'], i18nConfig)) },
|
||||
|
|
|
@ -31,7 +31,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ query }) =
|
|||
try {
|
||||
if (isEmpty(secretKey)) throw new Error('There is no secret key!');
|
||||
|
||||
const resume = await fetchResumeByIdentifier({ username, slug, options: { secretKey, withHost: true } });
|
||||
const resume = await fetchResumeByIdentifier({ username, slug, options: { secretKey } });
|
||||
|
||||
return { props: { resume } };
|
||||
} catch (error) {
|
||||
|
|
|
@ -4,36 +4,28 @@ import DateAdapter from '@mui/lab/AdapterDayjs';
|
|||
import LocalizationProvider from '@mui/lab/LocalizationProvider';
|
||||
import type { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
import { appWithTranslation, useTranslation } from 'next-i18next';
|
||||
import i18nConfig from 'next-i18next.config';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { appWithTranslation } from 'next-i18next';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import { QueryClientProvider } from 'react-query';
|
||||
import { Provider as ReduxProvider } from 'react-redux';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
|
||||
import Loading from '@/components/shared/Loading';
|
||||
import i18nConfig from '@/i18n/index';
|
||||
import ModalWrapper from '@/modals/index';
|
||||
import queryClient from '@/services/react-query';
|
||||
import store, { persistor } from '@/store/index';
|
||||
import WrapperRegistry from '@/wrappers/index';
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common', 'modals'], i18nConfig)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{t('common.title')}</title>
|
||||
<meta name="description" content={t('common.description')} />
|
||||
<title>Reactive Resume</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Reactive Resume is a free and open source resume builder that's built to make the mundane tasks of creating, updating and sharing your resume as easy as 1, 2, 3."
|
||||
/>
|
||||
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||
|
|
|
@ -4,11 +4,11 @@ import dynamic from 'next/dynamic';
|
|||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import i18nConfig from 'next-i18next.config';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import { RESUMES_QUERY } from '@/constants/index';
|
||||
import i18nConfig from '@/i18n/index';
|
||||
import { fetchResumes } from '@/services/resume';
|
||||
import styles from '@/styles/pages/Dashboard.module.scss';
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { Link as LinkIcon } from '@mui/icons-material';
|
||||
import { Button } from '@mui/material';
|
||||
import type { NextPage } from 'next';
|
||||
import type { GetServerSideProps, NextPage } from 'next';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { Trans, useTranslation } from 'next-i18next';
|
||||
import i18nConfig from 'next-i18next.config';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
|
||||
import { screenshots } from '@/config/screenshots';
|
||||
import i18nConfig from '@/i18n/index';
|
||||
import { logout } from '@/store/auth/authSlice';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import { setModalState } from '@/store/modal/modalSlice';
|
||||
|
@ -20,13 +20,13 @@ const Footer = dynamic(() => import('@/components/shared/Footer'));
|
|||
const Logo = dynamic(() => import('@/components/shared/Logo'));
|
||||
const NoSSR = dynamic(() => import('@/components/shared/NoSSR'));
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common', 'modals', 'landing'], i18nConfig)),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const Home: NextPage = () => {
|
||||
const { t } = useTranslation();
|
||||
|
|
|
@ -8,12 +8,12 @@ import isEmpty from 'lodash/isEmpty';
|
|||
import { GetServerSideProps, NextPage } from 'next';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import i18nConfig from 'next-i18next.config';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
import { useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
|
||||
import i18nConfig from '@/i18n/index';
|
||||
import { ServerError } from '@/services/axios';
|
||||
import { printResumeAsPdf, PrintResumeAsPdfParams } from '@/services/printer';
|
||||
import { fetchResumeByShortId } from '@/services/resume';
|
||||
|
@ -36,7 +36,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ query, loc
|
|||
const { shortId } = query as QueryParams;
|
||||
|
||||
try {
|
||||
const resume = await fetchResumeByShortId({ shortId, options: { withHost: true } });
|
||||
const resume = await fetchResumeByShortId({ shortId });
|
||||
|
||||
return { props: { shortId, resume, ...(await serverSideTranslations(locale, ['common'], i18nConfig)) } };
|
||||
} catch {
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:3100",
|
||||
"secure": false
|
||||
}
|
||||
}
|
|
@ -13,11 +13,11 @@ export type ServerError = {
|
|||
};
|
||||
|
||||
const axios = _axios.create({
|
||||
baseURL: '/api',
|
||||
baseURL: `${process.env.NEXT_PUBLIC_SERVER_URL}/api`,
|
||||
});
|
||||
|
||||
export const uninterceptedAxios = _axios.create({
|
||||
baseURL: '/api',
|
||||
baseURL: `${process.env.NEXT_PUBLIC_SERVER_URL}/api`,
|
||||
});
|
||||
|
||||
axios.interceptors.request.use((config) => {
|
||||
|
|
|
@ -14,7 +14,6 @@ export type FetchResumeByIdentifierParams = {
|
|||
username: string;
|
||||
slug: string;
|
||||
options?: {
|
||||
withHost?: boolean;
|
||||
secretKey?: string;
|
||||
};
|
||||
};
|
||||
|
@ -22,7 +21,6 @@ export type FetchResumeByIdentifierParams = {
|
|||
export type FetchResumeByShortIdParams = {
|
||||
shortId: string;
|
||||
options?: {
|
||||
withHost?: boolean;
|
||||
secretKey?: string;
|
||||
};
|
||||
};
|
||||
|
@ -60,25 +58,20 @@ export type DeleteResumeParams = {
|
|||
|
||||
export const fetchResumes = () => axios.get<Resume[]>('/resume').then((res) => res.data);
|
||||
|
||||
export const fetchResumeByShortId = async ({
|
||||
shortId,
|
||||
options = { secretKey: '', withHost: false },
|
||||
}: FetchResumeByShortIdParams) => {
|
||||
const hostname = options.withHost ? `${process.env.SERVER_URL}/api` : '';
|
||||
export const fetchResumeByShortId = async ({ shortId, options = { secretKey: '' } }: FetchResumeByShortIdParams) => {
|
||||
const requestOptions = isEmpty(options.secretKey) ? {} : { params: { secretKey: options.secretKey } };
|
||||
|
||||
return axios.get<Resume>(`${hostname}/resume/short/${shortId}`, requestOptions).then((res) => res.data);
|
||||
return axios.get<Resume>(`/resume/short/${shortId}`, requestOptions).then((res) => res.data);
|
||||
};
|
||||
|
||||
export const fetchResumeByIdentifier = async ({
|
||||
username,
|
||||
slug,
|
||||
options = { secretKey: '', withHost: false },
|
||||
options = { secretKey: '' },
|
||||
}: FetchResumeByIdentifierParams) => {
|
||||
const hostname = options.withHost ? `${process.env.SERVER_URL}/api` : '';
|
||||
const requestOptions = isEmpty(options.secretKey) ? {} : { params: { secretKey: options.secretKey } };
|
||||
|
||||
return axios.get<Resume>(`${hostname}/resume/${username}/${slug}`, requestOptions).then((res) => res.data);
|
||||
return axios.get<Resume>(`/resume/${username}/${slug}`, requestOptions).then((res) => res.data);
|
||||
};
|
||||
|
||||
export const createResume = (createResumeParams: CreateResumeParams) =>
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
"paths": {
|
||||
"@/components/*": ["components/*"],
|
||||
"@/config/*": ["config/*"],
|
||||
"@/constants/*": ["constants/*"],
|
||||
"@/i18n/*": ["i18n/*"],
|
||||
"@/modals/*": ["modals/*"],
|
||||
"@/pages/*": ["pages/*"],
|
||||
"@/public/*": ["public/*"],
|
||||
"@/services/*": ["services/*"],
|
||||
"@/store/*": ["store/*"],
|
||||
"@/constants/*": ["constants/*"],
|
||||
"@/styles/*": ["styles/*"],
|
||||
"@/templates/*": ["templates/*"],
|
||||
"@/types/*": ["types/*"],
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2020-2022 Amruth Pillai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
|
@ -10,7 +10,9 @@
|
|||
"serve": "nx run-many --target=serve --all",
|
||||
"test": "nx run-many --target=test --all",
|
||||
"lint": "nx run-many --target=lint --all --fix",
|
||||
"start": "nx run-many --target=serve --all --prod",
|
||||
"start:server": "node dist/apps/server/main",
|
||||
"start:client": "cd dist/apps/client && npm start",
|
||||
"start": "concurrently --kill-others \"npm run start:server\" \"npm run start:client\"",
|
||||
"build": "nx run-many --target=build --all --prod",
|
||||
"format": "prettier --write \"./**/*.{js,jsx,ts,tsx,json}\"",
|
||||
"prepare": "husky install",
|
||||
|
@ -55,6 +57,7 @@
|
|||
"downloadjs": "^1.4.7",
|
||||
"googleapis": "^95.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"i18next-http-backend": "^1.3.2",
|
||||
"joi": "^17.6.0",
|
||||
"lodash": "^4.17.21",
|
||||
"md5-hex": "^4.0.0",
|
||||
|
@ -93,6 +96,7 @@
|
|||
"regenerator-runtime": "0.13.9",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"rxjs": "^7.5.4",
|
||||
"sharp": "^0.30.2",
|
||||
"tailwindcss": "^3.0.23",
|
||||
"tslib": "^2.3.1",
|
||||
"typeorm": "^0.2.44",
|
||||
|
@ -138,6 +142,7 @@
|
|||
"@typescript-eslint/parser": "~5.10.2",
|
||||
"babel-jest": "27.5.1",
|
||||
"babel-loader": "^8.2.3",
|
||||
"concurrently": "^7.0.0",
|
||||
"csstype": "^3.0.10",
|
||||
"cypress": "^9.5.1",
|
||||
"eslint": "~8.10.0",
|
||||
|
|
Loading…
Reference in New Issue