Skip to content
Snippets Groups Projects
Commit 6ff2d4d9 authored by Christophe's avatar Christophe
Browse files

feat: Add next.js frontend with docker-compose

parent 36453370
No related branches found
No related tags found
1 merge request!3[backend, frontend] Update Backend and Frontend for Auth, Resolve POST data via API and Frontend
Showing
with 495 additions and 0 deletions
### General configuration
# Domain name, leave empty for localhost
DOMAIN=example.com
### Certificate configuration
# letsencrypt certificate email
# You may leave this blank, but it is strongly recommended to add one
LETSENCRYPT_EMAIL=
# certbot staging mode
# Set to 1 if you're testing your setup to avoid hitting request limits
LETSENCRYPT_STAGING=0
# HTTPS SSL key size
CERT_KEY_SIZE=4096
GIT_REPO_DOWNLOAD_URL=https://raw.githubusercontent.com/deephdc/cookiecutter-deep/
CC_GIT_REPO_URL=https://github.com/deephdc/cookiecutter-deep/
CC_GIT_BRANCH=advanced
CC_TEMPLATE=cookiecutter.json
.idea/
.env
cookie_secret.txt
nginx_api_credentials.txt
FROM certbot/certbot
RUN set -x \
&& apk add --no-cache \
curl \
bash \
&& rm -rf /var/cache/apk/* \
/tmp/* \
/var/tmp/*
COPY docker-entrypoint.sh /
RUN ["chmod", "+x", "/docker-entrypoint.sh"]
ENTRYPOINT ["/docker-entrypoint.sh"]
#!/usr/bin/env bash
# vim:sw=2:ts=2:et
set -ueo pipefail
# DEBUG
# set -x
# convert space-delimited string from the ENV to array
#domains=(${domains:-example.org})
domains=(${DOMAINS})
NGINX_CREDENTIALS=/run/secrets/nginx_api_credentials
exec 5< ${NGINX_CREDENTIALS}
read -r NGINX_API_USER <&5
read -r NGINX_API_PASS <&5
# if domains are provided
if (( ${#domains[@]} )); then
primary_domain=${domains[0]}
data_path="/etc/letsencrypt"
path="$data_path/live/$primary_domain"
rsa_key_size=${CERT_KEY_SIZE:-4096}
trap exit TERM
echo "### Let nginx bootstrap"
while ! curl --fail --silent --max-time 3 http://reverse_proxy
do
echo "nginx not up yet..."
sleep 3
done
# Select appropriate email arg
case "$LETSENCRYPT_EMAIL" in
"") email_arg="--register-unsafely-without-email" ;;
*) email_arg="--email $LETSENCRYPT_EMAIL" ;;
esac
if [ -f "$path/is-selfsigned" ]; then
echo "### Deleting self-signed cert..."
rm -r "$path"
echo "### Requesting Let's Encrypt certificate for $domains ..."
# join $domains to -d args
domain_args=""
for domain in "${domains[@]}"; do
domain_args="$domain_args -d $domain"
done
# Enable staging mode if needed
if [ $LETSENCRYPT_STAGING != "0" ]; then
staging_arg="--staging"
else
staging_arg=""
fi
certbot certonly \
--webroot -w /var/www/certbot \
$staging_arg \
$email_arg \
$domain_args \
--rsa-key-size $rsa_key_size \
--agree-tos \
--force-renewal
echo "### Reloading nginx ..."
curl --fail --silent --user ${NGINX_API_USER}:${NGINX_API_PASS} http://reverse_proxy/nginx/reload
fi
while :; do
certbot renew \
--webroot -w /var/www/certbot \
$email_arg \
--rsa-key-size $rsa_key_size \
--agree-tos
curl --fail --silent --user ${NGINX_API_USER}:${NGINX_API_PASS} http://reverse_proxy/nginx/reload
sleep 12h & wait ${!}
done
else
echo "No domains provided, doing nothing."
while :; do
sleep 24h
done
fi
\ No newline at end of file
version: "3.5"
services:
frontend:
build:
target: production
backend_v1:
build:
target: production
environment:
FLASK_ENV: production
GUNICORN_WORKERS: 1
reverse_proxy:
build:
target: production
version: "3.5"
services:
frontend:
container_name: frontend
restart: unless-stopped
image: frontend
build:
context: frontend
network: host
target: development
depends_on:
- backend_v1
environment:
NEXT_PUBLIC_BACKEND_URL: /api/v1
backend_v1:
container_name: backend_v1
restart: unless-stopped
image: backend_v1
build:
context: backend
network: host
target: development
args:
INSTALL_PYTHON_VERSION: 3.8
env_file: .env
environment:
BACKEND_ROUTE: /api/v1
FLASK_ENV: development
SECRET_KEY_FILE: /run/secrets/cookie_secret
volumes:
- ./backend/:/app
secrets:
- cookie_secret
reverse_proxy:
container_name: reverse_proxy
restart: unless-stopped
image: reverse_proxy
build:
context: nginx
network: host
target: development
depends_on:
- backend_v1
- frontend
env_file: .env
environment:
DOMAINS: ${DOMAIN}
CERT_KEY_SIZE: ${CERT_KEY_SIZE}
BACKEND_ROUTE: /api
volumes:
- ssl:/etc/letsencrypt
- ssl:/var/www/certbot
secrets:
- nginx_api_credentials
ports:
- "80:80"
- "443:443"
certificate:
container_name: certificate
restart: unless-stopped
image: certificate
build:
context: certbot
network: host
env_file: .env
environment:
DOMAINS: ${DOMAIN}
NGINX_CREDENTIALS: /run/secrets/nginx_api_credentials
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
LETSENCRYPT_STAGING: ${LETSENCRYPT_STAGING}
CERT_KEY_SIZE: ${CERT_KEY_SIZE}
volumes:
- ssl:/etc/letsencrypt
- ssl:/var/www/certbot
secrets:
- nginx_api_credentials
volumes:
ssl: { }
secrets:
cookie_secret:
file: cookie_secret.txt
nginx_api_credentials:
file: nginx_api_credentials.txt
{
"extends": "next/core-web-vitals"
}
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
{
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"printWidth": 100
}
FROM node:18 as git-version
WORKDIR /app
COPY lib lib
COPY ["package.json", "yarn.lock", "next.config.js", "next-env.d.ts", "tsconfig.json", ".eslintrc.json", ".prettierrc", "./"]
# determine footer version
#COPY .git/ ./.git/
#RUN yarn git-info
FROM node:18 as base
# new base because we don't want .git
WORKDIR /app
COPY ["package.json", "yarn.lock", "next.config.js", "next-env.d.ts", "tsconfig.json", ".eslintrc.json", ".prettierrc", "./"]
ENV NEXT_TELEMETRY_DISABLED=1
RUN yarn install
# copy necessary files
#COPY [".env", "./"]
COPY public public
COPY styles styles
COPY pages pages
COPY components components
COPY lib lib
#COPY --from=git-version /app/utils/generatedGitInfo.json utils/generatedGitInfo.json
FROM base as production
ENV NODE_ENV=production
EXPOSE 3000
#RUN npm run build
# move build command into CMD to take into account run-time env vars for static pages
CMD ["/bin/sh", "-c", "yarn build && yarn start"]
FROM base as development
ENV NODE_ENV=development
EXPOSE 3000
CMD [ "yarn", "dev" ]
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/template.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
import { BLANK_FIELD, LegalField, SelectField, StringField } from '../lib/template';
import { FC } from 'react';
import SelectInput from './SelectInput';
import TextInput from './TextInput';
type FormProps = {
fields: LegalField[];
};
export const Form: FC<FormProps> = ({ fields }) => {
// TODO: better handling of StringField and SelectField typing
return (
<>
{fields.map((field) => (
<div key={field.key}>
{field.description ?? field.key}
{field.default != BLANK_FIELD &&
(typeof field.default == 'string' ? (
<TextInput field={field as StringField} />
) : Array.isArray(field.default) ? (
<SelectInput field={field as SelectField} />
) : undefined)}
</div>
))}
</>
);
};
export default Form;
import { SelectField } from '../lib/template';
import { FC } from 'react';
type SelectInputProps = {
field: SelectField;
};
const SelectInput: FC<SelectInputProps> = ({ field }) => {
return (
<>
<select name={field.key} className="selectpicker form-control">
{field.default.map((option) => (
<option value={option} key={option}>
{option}
</option>
))}
</select>
</>
);
};
export default SelectInput;
import { FC } from 'react';
import { StringField } from '../lib/template';
type TextInputProps = {
field: StringField;
};
const TextInput: FC<TextInputProps> = ({ field }) => {
return (
<>
<input className="input" type="text" name={field.key} placeholder={field.default} />
</>
);
};
export default TextInput;
export const GIT_REPO_DOWNLOAD_URL =
process.env.CC_GIT_REPO_DOWNLOAD_URL ??
'https://raw.githubusercontent.com/deephdc/cookiecutter-deep/';
export const GIT_REPO_URL =
process.env.CC_GIT_REPO_URL ?? 'https://github.com/deephdc/cookiecutter-deep/';
export const GIT_BRANCH_NAME = process.env.CC_GIT_BRANCH ?? 'advanced';
export const GIT_DOWNLOAD_ROOT = GIT_REPO_DOWNLOAD_URL + GIT_BRANCH_NAME + '/';
export const COOKIECUTTER_TEMPLATE = process.env.CC_TEMPLATE ?? 'cookiecutter.json';
export const COOKIECUTTER_TEMPLATE_URL = GIT_DOWNLOAD_ROOT + COOKIECUTTER_TEMPLATE;
export const COOKIECUTTER_HELP = COOKIECUTTER_TEMPLATE.split('.')[0] + '-help.json';
export const COOKIECUTTER_HELP_URL = GIT_DOWNLOAD_ROOT + COOKIECUTTER_HELP;
export const BACKEND_ROUTE = process.env.NEXT_PUBLIC_BACKEND_URL ?? '/api/v1';
export const BLANK_FIELD = 'Press Enter and configure';
export type Field<T> = {
key: string;
description?: string;
default: T;
};
export type StringField = Field<string>;
export type SelectField = Field<Array<number>> | Field<Array<string>>;
export type LegalField = StringField | SelectField;
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}
module.exports = nextConfig
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@types/node": "18.11.9",
"@types/react": "18.0.25",
"@types/react-dom": "18.0.9",
"eslint": "8.27.0",
"eslint-config-next": "13.0.3",
"next": "13.0.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-query": "^3.39.2",
"typescript": "4.9.3"
},
"devDependencies": {
"prettier": "^2.8.1"
}
}
import 'styles/style.css';
import type { AppProps } from 'next/app';
import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import { FC, PropsWithChildren } from 'react';
const QueryClientWrapper: FC<PropsWithChildren> = ({ children }) => {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
{children} <ReactQueryDevtools />
</QueryClientProvider>
);
};
const NextApp = ({ Component, pageProps }: AppProps) => {
return (
<QueryClientWrapper>
<Component {...pageProps} />
</QueryClientWrapper>
);
};
export default NextApp;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment