feat: initial commit
14
.gitignore
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Backend config file
|
||||||
|
/config.py
|
||||||
|
|
||||||
|
# Local environments
|
||||||
|
/.venv/
|
||||||
|
/node_modules/
|
||||||
|
|
||||||
|
# Cache files and directories
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
|
/.parcel-cache/
|
||||||
|
|
||||||
|
# Bundled files
|
||||||
|
/dist/
|
7
.postcssrc
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"plugins": {
|
||||||
|
"postcss-import": {},
|
||||||
|
"tailwindcss/nesting": {},
|
||||||
|
"tailwindcss": {},
|
||||||
|
}
|
||||||
|
}
|
29
Caddyfile
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
:3000 {
|
||||||
|
|
||||||
|
encode zstd gzip
|
||||||
|
|
||||||
|
@staticfiles {
|
||||||
|
method GET
|
||||||
|
path /static/*
|
||||||
|
}
|
||||||
|
handle @staticfiles {
|
||||||
|
file_server {
|
||||||
|
root /app/public/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_path /api/* {
|
||||||
|
reverse_proxy * portfolio-backend:3001
|
||||||
|
}
|
||||||
|
|
||||||
|
handle * {
|
||||||
|
root * /app/dist/
|
||||||
|
file_server
|
||||||
|
try_files {path} /
|
||||||
|
}
|
||||||
|
|
||||||
|
log {
|
||||||
|
output stderr
|
||||||
|
format console
|
||||||
|
}
|
||||||
|
}
|
41
README.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
In `index.html`: make parcel replace the hard-coded base URL (in the meta tags).
|
||||||
|
|
||||||
|
# About
|
||||||
|
|
||||||
|
This is a template project for a webapp using the following tech stack:
|
||||||
|
|
||||||
|
- **Frontend**:
|
||||||
|
- Vue.js
|
||||||
|
- TailwindCSS
|
||||||
|
- Axios
|
||||||
|
- **Backend**:
|
||||||
|
- FastAPI (python)
|
||||||
|
- **Other**:
|
||||||
|
- Caddy (webserver/reverse proxy)
|
||||||
|
- Parcel (bundler)
|
||||||
|
|
||||||
|
# Customization
|
||||||
|
|
||||||
|
A number of default template files and assets for the frontend are already present in the repository.
|
||||||
|
These include essential assets like a set of fonts, and a default favicon.
|
||||||
|
|
||||||
|
## Favicon
|
||||||
|
|
||||||
|
The standards for the correct favicons are annoying, many browser vendors use a different standard.
|
||||||
|
|
||||||
|
A quick and easy way to create a set of favicons with broad compatibility is to create your favicon as an `.svg`-file,
|
||||||
|
and then use [realfavicongenerator.net](https://realfavicongenerator.net/) to create a set of relevant files and HTML
|
||||||
|
for your favicon.
|
||||||
|
|
||||||
|
Place the generated icons into `/frontend/assets/images/common/` and be sure to adjust the generated HTML to point to
|
||||||
|
this path.
|
||||||
|
|
||||||
|
## OG Image
|
||||||
|
|
||||||
|
The OG image is served directly by Caddy, and thus needs to be placed into `/public/static/images/common/`,
|
||||||
|
ideally as a `.webp`-file.
|
||||||
|
|
||||||
|
The image's dimensions should be 1200 x 630 pixels, as according to the
|
||||||
|
[OGP standard](https://developers.facebook.com/docs/sharing/webmasters/images/).
|
25
backend.Dockerfile
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM python:3
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
ARG CUSTOM_UID
|
||||||
|
ARG CUSTOM_GID
|
||||||
|
ENV CUSTOM_USERNAME=backend
|
||||||
|
ENV CUSTOM_GROUPNAME=backend
|
||||||
|
RUN groupadd --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
|
||||||
|
useradd --uid ${CUSTOM_UID:-1000} --gid ${CUSTOM_GID:-1000} --create-home --shell /bin/bash ${CUSTOM_USERNAME} && \
|
||||||
|
mkdir /app && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /app
|
||||||
|
|
||||||
|
# Copy source files
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} requirements.txt /app/
|
||||||
|
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} backend /app/backend/
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Run ASGI server
|
||||||
|
EXPOSE 3001/tcp
|
||||||
|
USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
|
||||||
|
ENTRYPOINT ["uvicorn", "backend.main:app", "--root-path", "/api", "--host", "0.0.0.0", "--port", "3001"]
|
0
backend/__init__.py
Normal file
8
backend/main.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.get("/hello/")
|
||||||
|
async def hello() -> str:
|
||||||
|
return "Hello World!"
|
31
docker-compose.yml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
portfolio-frontend:
|
||||||
|
container_name: portfolio-frontend
|
||||||
|
restart: unless-stopped
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./frontend.Dockerfile
|
||||||
|
args:
|
||||||
|
CUSTOM_UID: 1000
|
||||||
|
CUSTOM_GID: 1000
|
||||||
|
environment:
|
||||||
|
TZ: Europe/Berlin
|
||||||
|
ports:
|
||||||
|
- "8000:3000"
|
||||||
|
portfolio-backend:
|
||||||
|
container_name: portfolio-backend
|
||||||
|
restart: unless-stopped
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./backend.Dockerfile
|
||||||
|
args:
|
||||||
|
CUSTOM_UID: 1000
|
||||||
|
CUSTOM_GID: 1000
|
||||||
|
expose:
|
||||||
|
- "3001"
|
||||||
|
|
||||||
|
...
|
36
frontend.Dockerfile
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM debian:latest
|
||||||
|
|
||||||
|
# Install packages
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
#RUN apt update && apt install -y python3-pip python3-venv && rm -rf /var/lib/apt/lists/*
|
||||||
|
RUN apt update && apt install -y curl && \
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_19.x | bash - && apt-get install -y nodejs && \
|
||||||
|
apt install -y debian-keyring debian-archive-keyring apt-transport-https && \
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg && \
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list && \
|
||||||
|
apt update && apt install -y caddy && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
ARG CUSTOM_UID
|
||||||
|
ARG CUSTOM_GID
|
||||||
|
ENV CUSTOM_USERNAME=webserver
|
||||||
|
ENV CUSTOM_GROUPNAME=webserver
|
||||||
|
RUN groupadd --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
|
||||||
|
useradd --uid ${CUSTOM_UID:-1000} --gid ${CUSTOM_GID:-1000} --create-home --shell /bin/bash ${CUSTOM_USERNAME} && \
|
||||||
|
mkdir /app && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /app
|
||||||
|
|
||||||
|
# Copy source files
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} package.json package-lock.json .postcssrc postcss.config.js tailwind.config.js Caddyfile /app/
|
||||||
|
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} frontend /app/frontend/
|
||||||
|
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} public /app/public/
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
|
||||||
|
RUN npm install
|
||||||
|
RUN npx parcel build frontend/src/html/index.html
|
||||||
|
|
||||||
|
ENTRYPOINT ["caddy", "run", "--config", "/app/Caddyfile"]
|
BIN
frontend/assets/fonts/kanit/kanit-black-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-black.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-bold-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-bold.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-extrabold-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-extrabold.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-extralight-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-extralight.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-light-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-light.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-medium-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-medium.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-regular-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-regular.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-semibold-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-semibold.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-thin-italic.ttf
Normal file
BIN
frontend/assets/fonts/kanit/kanit-thin.ttf
Normal file
BIN
frontend/assets/fonts/lora/lora-bold-italic.ttf
Normal file
BIN
frontend/assets/fonts/lora/lora-bold.ttf
Normal file
BIN
frontend/assets/fonts/lora/lora-medium-italic.ttf
Normal file
BIN
frontend/assets/fonts/lora/lora-medium.ttf
Normal file
BIN
frontend/assets/fonts/lora/lora-regular-italic.ttf
Normal file
BIN
frontend/assets/fonts/lora/lora-regular.ttf
Normal file
BIN
frontend/assets/fonts/lora/lora-semibold-italic.ttf
Normal file
BIN
frontend/assets/fonts/lora/lora-semibold.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-black-italic.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-black.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-bold-italic.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-bold.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-extrabold-italic.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-extrabold.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-extralight.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-light-italic.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-light.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-medium-italic.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-medium.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-regular-italic.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-regular.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-semibold-italic.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-semibold.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-thin-italic.ttf
Normal file
BIN
frontend/assets/fonts/montserrat/montserrat-thin.ttf
Normal file
BIN
frontend/assets/fonts/notocoloremoji/notocoloremoji-regular.ttf
Normal file
BIN
frontend/assets/fonts/sourcecodepro/sourcecodepro-black.ttf
Normal file
BIN
frontend/assets/fonts/sourcecodepro/sourcecodepro-bold.ttf
Normal file
BIN
frontend/assets/fonts/sourcecodepro/sourcecodepro-extrabold.ttf
Normal file
BIN
frontend/assets/fonts/sourcecodepro/sourcecodepro-extralight.ttf
Normal file
BIN
frontend/assets/fonts/sourcecodepro/sourcecodepro-light.ttf
Normal file
BIN
frontend/assets/fonts/sourcecodepro/sourcecodepro-medium.ttf
Normal file
BIN
frontend/assets/fonts/sourcecodepro/sourcecodepro-regular.ttf
Normal file
BIN
frontend/assets/fonts/sourcecodepro/sourcecodepro-semibold.ttf
Normal file
BIN
frontend/assets/images/common/favicon/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
frontend/assets/images/common/favicon/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
frontend/assets/images/common/favicon/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
9
frontend/assets/images/common/favicon/browserconfig.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig>
|
||||||
|
<msapplication>
|
||||||
|
<tile>
|
||||||
|
<square150x150logo src="/mstile-150x150.png"/>
|
||||||
|
<TileColor>#da532c</TileColor>
|
||||||
|
</tile>
|
||||||
|
</msapplication>
|
||||||
|
</browserconfig>
|
BIN
frontend/assets/images/common/favicon/favicon-16x16.png
Normal file
After Width: | Height: | Size: 994 B |
BIN
frontend/assets/images/common/favicon/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
frontend/assets/images/common/favicon/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
4
frontend/assets/images/common/favicon/favicon.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#00aaff" d="M20.501 6.028V6h-.02A10.28 10.28 0 0 0 4.519 6H4.5v.028a10.262 10.262 0 0 0 0 12.944V19h.02a10.28 10.28 0 0 0 15.962 0h.021v-.028a10.262 10.262 0 0 0 0-12.944zM13 6V3.272A4.533 4.533 0 0 1 15.54 6zm2.935 1a16.827 16.827 0 0 1 .853 5H13V7zM12 3.272V6H9.46A4.533 4.533 0 0 1 12 3.272zM12 7v5H8.212a16.827 16.827 0 0 1 .853-5zm-4.787 5H3.226a9.234 9.234 0 0 1 1.792-5h2.984a17.952 17.952 0 0 0-.79 5zm0 1a17.952 17.952 0 0 0 .789 5H5.018a9.234 9.234 0 0 1-1.792-5zm1 0H12v5H9.065a16.827 16.827 0 0 1-.853-5zM12 19v2.728A4.533 4.533 0 0 1 9.46 19zm1 2.728V19h2.54A4.533 4.533 0 0 1 13 21.728zM13 18v-5h3.788a16.827 16.827 0 0 1-.853 5zm4.787-5h3.987a9.234 9.234 0 0 1-1.792 5h-2.984a17.952 17.952 0 0 0 .79-5zm0-1a17.952 17.952 0 0 0-.789-5h2.984a9.234 9.234 0 0 1 1.792 5zm1.352-6h-2.501a8.524 8.524 0 0 0-1.441-2.398A9.306 9.306 0 0 1 19.139 6zM9.803 3.602A8.524 8.524 0 0 0 8.363 6H5.86a9.306 9.306 0 0 1 3.942-2.398zM5.861 19h2.501a8.524 8.524 0 0 0 1.441 2.398A9.306 9.306 0 0 1 5.861 19zm9.336 2.398A8.524 8.524 0 0 0 16.637 19h2.502a9.306 9.306 0 0 1-3.942 2.398z"/>
|
||||||
|
<path fill="none" d="M0 0h24v24H0z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
frontend/assets/images/common/favicon/mstile-144x144.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
frontend/assets/images/common/favicon/mstile-150x150.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
frontend/assets/images/common/favicon/mstile-310x150.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
frontend/assets/images/common/favicon/mstile-310x310.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
frontend/assets/images/common/favicon/mstile-70x70.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
71
frontend/assets/images/common/favicon/safari-pinned-tab.svg
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M3468 6355 c-2 -1 -37 -5 -78 -9 -114 -9 -170 -16 -230 -27 -30 -6
|
||||||
|
-68 -13 -85 -15 -16 -3 -48 -10 -70 -15 -22 -5 -51 -12 -65 -15 -53 -11 -110
|
||||||
|
-27 -235 -69 -451 -149 -844 -392 -1180 -729 -111 -111 -248 -272 -305 -356
|
||||||
|
-8 -12 -33 -47 -56 -78 -119 -167 -211 -346 -339 -657 -28 -66 -88 -273 -111
|
||||||
|
-380 -12 -52 -25 -123 -30 -155 -2 -19 -6 -44 -9 -55 -5 -24 -13 -99 -23 -220
|
||||||
|
-9 -102 -8 -400 2 -490 10 -89 17 -146 22 -180 3 -16 7 -43 10 -60 2 -16 14
|
||||||
|
-73 25 -125 96 -441 284 -841 568 -1210 88 -114 115 -145 256 -286 360 -361
|
||||||
|
812 -625 1308 -764 43 -12 86 -23 95 -25 9 -1 35 -8 57 -13 35 -10 135 -28
|
||||||
|
255 -46 90 -14 222 -21 405 -21 178 1 370 11 386 21 3 1 28 6 56 9 49 5 74 9
|
||||||
|
123 20 14 3 34 7 45 9 11 3 40 9 65 15 25 7 52 13 60 15 298 73 616 213 890
|
||||||
|
392 206 134 346 251 521 432 400 413 661 902 783 1462 24 111 27 132 41 250 4
|
||||||
|
30 9 66 12 80 9 45 11 483 3 550 -5 36 -11 90 -13 120 -3 30 -18 116 -32 190
|
||||||
|
-92 475 -297 921 -592 1293 -32 40 -62 78 -68 86 -42 55 -330 334 -414 400
|
||||||
|
-233 185 -492 337 -741 436 -52 20 -104 41 -115 45 -138 57 -479 138 -650 156
|
||||||
|
-33 3 -73 8 -90 10 -34 6 -452 14 -457 9z m32 -710 l0 -395 -370 0 -370 0 24
|
||||||
|
63 c105 280 338 538 619 686 43 23 82 41 87 41 6 0 10 -151 10 -395z m385 357
|
||||||
|
c171 -86 381 -272 488 -433 52 -78 135 -241 152 -296 l6 -23 -370 0 -371 0 0
|
||||||
|
396 0 395 23 -7 c12 -3 45 -18 72 -32z m-1035 -60 c0 -4 -10 -18 -23 -32 -108
|
||||||
|
-118 -296 -431 -370 -617 l-17 -43 -365 0 -366 0 58 57 c252 246 585 456 938
|
||||||
|
589 159 61 145 56 145 46z m1803 -71 c321 -129 607 -314 871 -563 l61 -58
|
||||||
|
-366 0 -366 0 -48 108 c-26 59 -63 133 -81 165 -19 32 -34 60 -34 62 0 11 -98
|
||||||
|
157 -164 245 -39 52 -74 100 -79 107 -8 13 77 -14 206 -66z m-2323 -921 c0 -4
|
||||||
|
-9 -36 -19 -71 -16 -52 -47 -170 -67 -249 -20 -84 -73 -358 -78 -405 -4 -33
|
||||||
|
-9 -69 -11 -80 -7 -30 -13 -83 -20 -155 -4 -36 -8 -76 -10 -89 -6 -33 -18
|
||||||
|
-264 -19 -338 l-1 -63 -583 0 -582 0 5 63 c7 77 14 135 30 247 18 126 101 422
|
||||||
|
150 535 7 17 23 55 35 85 49 121 209 408 262 469 9 11 21 28 27 39 10 19 24
|
||||||
|
20 446 20 239 0 435 -4 435 -8z m1170 -721 l0 -729 -552 2 -553 3 2 65 c1 58
|
||||||
|
4 109 18 290 6 79 22 210 29 245 2 11 7 43 10 70 12 93 57 309 92 445 20 74
|
||||||
|
37 140 38 145 10 40 50 163 57 176 8 15 50 17 434 17 l425 -1 0 -728z m1163
|
||||||
|
677 c29 -81 106 -363 121 -446 3 -14 8 -36 11 -50 24 -104 59 -334 79 -520 6
|
||||||
|
-53 17 -228 20 -322 l3 -68 -554 0 -553 0 0 728 0 729 428 0 427 0 18 -51z
|
||||||
|
m1226 -35 c87 -129 182 -300 234 -421 8 -19 22 -51 30 -70 54 -120 124 -361
|
||||||
|
156 -535 20 -110 25 -151 37 -278 l6 -67 -581 0 c-320 0 -581 3 -582 8 0 4 -2
|
||||||
|
48 -4 97 -3 94 -14 255 -20 315 -14 125 -36 297 -40 310 -2 8 -7 35 -10 60 -4
|
||||||
|
25 -11 68 -16 95 -6 28 -12 59 -14 70 -10 66 -78 336 -122 486 -4 15 35 16
|
||||||
|
432 16 l436 1 58 -87z m-3788 -1669 c1 -4 3 -45 4 -92 6 -148 28 -413 40 -480
|
||||||
|
2 -14 7 -45 10 -70 4 -39 35 -224 50 -300 10 -52 48 -217 60 -265 14 -51 53
|
||||||
|
-195 62 -227 5 -17 -20 -18 -429 -18 l-435 0 -60 88 c-229 336 -383 735 -438
|
||||||
|
1132 -4 25 -8 59 -10 75 -3 17 -7 60 -10 97 l-5 67 580 0 c319 0 580 -3 581
|
||||||
|
-7z m1399 -722 l0 -730 -429 0 -428 0 -41 138 c-37 122 -77 271 -88 327 -2 11
|
||||||
|
-13 65 -24 120 -21 110 -36 196 -45 265 -3 25 -8 52 -10 60 -2 8 -7 48 -10 88
|
||||||
|
-4 41 -8 82 -10 93 -2 10 -7 76 -11 146 -10 187 -11 207 -7 215 2 4 251 8 553
|
||||||
|
8 l550 0 0 -730z m1394 652 c-5 -122 -14 -265 -18 -292 -3 -14 -7 -56 -11 -95
|
||||||
|
-3 -38 -8 -77 -10 -85 -2 -8 -7 -42 -10 -75 -5 -48 -55 -324 -71 -385 -26
|
||||||
|
-106 -56 -221 -64 -245 -5 -16 -21 -69 -35 -117 l-26 -88 -430 0 -429 0 0 730
|
||||||
|
0 729 554 0 553 0 -3 -77z m1452 6 c-8 -99 -43 -329 -55 -361 -6 -15 -9 -27
|
||||||
|
-7 -27 5 0 -24 -109 -57 -215 -75 -243 -217 -529 -365 -737 l-34 -48 -435 0
|
||||||
|
c-409 0 -434 1 -429 18 2 9 14 49 25 87 23 82 74 286 90 365 20 97 21 104 26
|
||||||
|
140 3 19 8 44 10 56 10 45 34 224 41 294 3 41 8 83 10 93 5 27 19 256 23 384
|
||||||
|
l1 22 581 0 580 0 -5 -71z m-3870 -1767 c84 -187 210 -397 324 -539 28 -34 44
|
||||||
|
-62 37 -62 -8 0 -45 11 -83 24 -291 102 -580 263 -807 449 -102 84 -215 188
|
||||||
|
-221 202 -3 10 69 13 354 13 l358 0 38 -87z m1024 -307 c0 -263 -3 -394 -10
|
||||||
|
-394 -20 0 -135 62 -220 119 -113 74 -280 242 -354 355 -53 81 -134 239 -150
|
||||||
|
292 l-6 21 370 1 370 0 0 -394z m1024 372 c-19 -63 -85 -193 -141 -278 -119
|
||||||
|
-181 -295 -343 -482 -441 -49 -26 -93 -47 -100 -47 -8 0 -11 108 -11 394 l0
|
||||||
|
394 370 0 371 -1 -7 -21z m1046 15 c0 -10 -152 -149 -236 -216 -198 -160 -505
|
||||||
|
-334 -739 -421 -135 -50 -157 -56 -148 -41 8 14 75 100 84 107 3 3 40 57 81
|
||||||
|
120 81 123 154 257 207 381 l34 77 358 0 c198 0 359 -3 359 -7z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.7 KiB |
19
frontend/assets/images/common/favicon/site.webmanifest
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"short_name": "",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
379
frontend/src/css/fonts.css
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
/* Kanit */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-thin.ttf') format('truetype');
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-thin-italic.ttf') format('truetype');
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-extralight.ttf') format('truetype');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-extralight-italic.ttf') format('truetype');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-light.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-light-italic.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-regular.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-regular-italic.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-medium.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-medium-italic.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-semibold.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-semibold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-bold.ttf') format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-bold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-extrabold.ttf') format('truetype');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-extrabold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-black.ttf') format('truetype');
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Kanit';
|
||||||
|
src: url('/frontend/assets/fonts/kanit/kanit-black-italic.ttf') format('truetype');
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Montserrat */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-thin.ttf') format('truetype');
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-thin-italic.ttf') format('truetype');
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-extralight.ttf') format('truetype');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-extralight-italic.ttf') format('truetype');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-light.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-light-italic.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-regular.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-regular-italic.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-medium.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-medium-italic.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-semibold.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-semibold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-bold.ttf') format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-bold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-extrabold.ttf') format('truetype');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-extrabold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-black.ttf') format('truetype');
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
src: url('/frontend/assets/fonts/montserrat/montserrat-black-italic.ttf') format('truetype');
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* SourceCodePro */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-extralight.ttf') format('truetype');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-extralight-italic.ttf') format('truetype');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-light.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-light-italic.ttf') format('truetype');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-regular.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-regular-italic.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-medium.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-medium-italic.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-semibold.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-semibold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-bold.ttf') format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-bold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-extrabold.ttf') format('truetype');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-extrabold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 800;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-black.ttf') format('truetype');
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'SourceCodePro';
|
||||||
|
src: url('/frontend/assets/fonts/sourcecodepro/sourcecodepro-black-italic.ttf') format('truetype');
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Lora */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lora';
|
||||||
|
src: url('/frontend/assets/fonts/lora/lora-regular.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lora';
|
||||||
|
src: url('/frontend/assets/fonts/lora/lora-regular-italic.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lora';
|
||||||
|
src: url('/frontend/assets/fonts/lora/lora-medium.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lora';
|
||||||
|
src: url('/frontend/assets/fonts/lora/lora-medium-italic.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lora';
|
||||||
|
src: url('/frontend/assets/fonts/lora/lora-semibold.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lora';
|
||||||
|
src: url('/frontend/assets/fonts/lora/lora-semibold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lora';
|
||||||
|
src: url('/frontend/assets/fonts/lora/lora-bold.ttf') format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lora';
|
||||||
|
src: url('/frontend/assets/fonts/lora/lora-bold-italic.ttf') format('truetype');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* NotoColorEmoji */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'NotoColorEmoji';
|
||||||
|
src: url('/frontend/assets/fonts/notocoloremoji/notocoloremoji-regular.ttf') format('truetype');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
4
frontend/src/css/styles.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
@import "./fonts.css";
|
||||||
|
@import "tailwindcss/base";
|
||||||
|
@import "tailwindcss/components";
|
||||||
|
@import "tailwindcss/utilities";
|
48
frontend/src/html/index.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<title>Example Site</title>
|
||||||
|
<meta name="description" content="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.">
|
||||||
|
<meta name="author" content="John Doe">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Example Site">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content="https://example.com">
|
||||||
|
<meta property="og:description" content="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.">
|
||||||
|
<meta property="og:image" content="https://example.com/static/images/common/og-image.webp">
|
||||||
|
<meta property="og:image:type" content="image/png">
|
||||||
|
<meta property="og:image:width" content="1200">
|
||||||
|
<meta property="og:image:height" content="630">
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/frontend/assets/images/common/favicon/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/frontend/assets/images/common/favicon/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/frontend/assets/images/common/favicon/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/frontend/assets/images/common/favicon/site.webmanifest">
|
||||||
|
<link rel="mask-icon" href="/frontend/assets/images/common/favicon/safari-pinned-tab.svg" color="#5bbad5">
|
||||||
|
<meta name="msapplication-TileColor" content="#da532c">
|
||||||
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/frontend/src/css/styles.css">
|
||||||
|
<link rel="stylesheet" href="/frontend/src/css/fonts.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<div class="flex justify-center w-full min-h-screen">
|
||||||
|
<div class="flex flex-col gap-8 max-w-xl min-h-screen justify-center items-center text-center m-8">
|
||||||
|
<div class="flex flex-col justify-center items-center w-full border-2 border-darkaccent rounded-xl p-8">
|
||||||
|
<svg fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="block w-12 h-12">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
|
||||||
|
</svg>
|
||||||
|
<p class="text-xl font-semibold mt-4">This page requires JavaScript.</p>
|
||||||
|
<p class="text-lg mt-4">Please enable JavaScript in your browser to continue.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/frontend/src/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
frontend/src/js/App.vue
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
5
frontend/src/js/components/ExampleComponent.vue
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>Amet cursus sit amet dictum. Feugiat vivamus at augue eget arcu dictum. Sed enim ut sem viverra aliquet eget sit. Pellentesque eu tincidunt tortor aliquam nulla facilisi.</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
12
frontend/src/js/main.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import App from './App.vue';
|
||||||
|
import router from './router';
|
||||||
|
|
||||||
|
// Enable global Vue feature flags:
|
||||||
|
// https://stackoverflow.com/questions/70083869/parcel-2-vue-3-how-to-set-global-feature-flags-vue-devtools-disabled
|
||||||
|
globalThis.__VUE_OPTIONS_API__ = true;
|
||||||
|
globalThis.__VUE_PROD_DEVTOOLS__ = false;
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
app.use(router);
|
||||||
|
app.mount("#app");
|
22
frontend/src/js/router/index.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import Home from '../views/Home.vue';
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Home',
|
||||||
|
component: Home,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/:pathMatch(.*)*',
|
||||||
|
name: 'NotFound',
|
||||||
|
component: () => import('../views/NotFound.vue'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
17
frontend/src/js/views/Home.vue
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import ExampleComponent from '../components/ExampleComponent.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ExampleComponent,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ExampleComponent />
|
||||||
|
</div>
|
||||||
|
</template>
|
14
frontend/src/js/views/NotFound.vue
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>The page you are looking for was not found F641;</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
23
package.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "Vue/Tailwind/FastAPI Template",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "A template for a new web application.",
|
||||||
|
"scripts": {},
|
||||||
|
"private": true,
|
||||||
|
"author": "John Doe",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"devDependencies": {
|
||||||
|
"@parcel/transformer-vue": "^2.8.2",
|
||||||
|
"buffer": "^5.7.1",
|
||||||
|
"parcel": "^2.8.2",
|
||||||
|
"postcss": "^8.4.20",
|
||||||
|
"postcss-import": "^15.1.0",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"tailwindcss": "^3.2.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.2.2",
|
||||||
|
"vue": "^3.2.45",
|
||||||
|
"vue-router": "^4.1.6"
|
||||||
|
}
|
||||||
|
}
|
8
postcss.config.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('postcss-import'),
|
||||||
|
require('tailwindcss/nesting'),
|
||||||
|
require('tailwindcss'),
|
||||||
|
require('autoprefixer'),
|
||||||
|
]
|
||||||
|
};
|
BIN
public/static/images/common/og-image.webp
Normal file
After Width: | Height: | Size: 1.1 MiB |
16
requirements.txt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
anyio==3.6.2
|
||||||
|
click==8.1.3
|
||||||
|
fastapi==0.88.0
|
||||||
|
h11==0.14.0
|
||||||
|
httptools==0.5.0
|
||||||
|
idna==3.4
|
||||||
|
pydantic==1.10.4
|
||||||
|
python-dotenv==0.21.0
|
||||||
|
PyYAML==6.0
|
||||||
|
sniffio==1.3.0
|
||||||
|
starlette==0.22.0
|
||||||
|
typing_extensions==4.4.0
|
||||||
|
uvicorn==0.20.0
|
||||||
|
uvloop==0.17.0
|
||||||
|
watchfiles==0.18.1
|
||||||
|
websockets==10.4
|
28
tailwind.config.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module.exports = {
|
||||||
|
content: [
|
||||||
|
"./frontend/src/**/*.html",
|
||||||
|
"./frontend/src/**/*.js",
|
||||||
|
"./frontend/src/**/*.vue",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: ["Montserrat", "NotoColorEmoji"],
|
||||||
|
serif: ["Lora", "NotoColorEmoji"],
|
||||||
|
mono: ["SourceCodePro", "NotoColorEmoji"],
|
||||||
|
accent: ["Kanit", "NotoColorEmoji"],
|
||||||
|
emoji: ["NotoColorEmoji"],
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
transparent: 'transparent',
|
||||||
|
current: 'currentColor',
|
||||||
|
lightshade: '#eeebed',
|
||||||
|
lightaccent: '#8C919C',
|
||||||
|
main: '#1C4F97',
|
||||||
|
darkaccent: '#B3425C',
|
||||||
|
darkshade: '#221F2F',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|