add docker compose
This commit is contained in:
13
backend/Dockerfile
Normal file
13
backend/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
FROM python:3.12
|
||||||
|
|
||||||
|
WORKDIR /code
|
||||||
|
|
||||||
|
RUN apt update && apt install -y weasyprint
|
||||||
|
|
||||||
|
COPY ./backend/requirements.txt /code/requirements.txt
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||||
|
|
||||||
|
COPY ./backend /code/app
|
||||||
|
|
||||||
|
CMD ["fastapi", "run", "app/src/main.py", "--port", "8000"]
|
||||||
65
backend/requirements.txt
Normal file
65
backend/requirements.txt
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
annotated-doc==0.0.4
|
||||||
|
annotated-types==0.7.0
|
||||||
|
anyio==4.12.1
|
||||||
|
brotli==1.2.0
|
||||||
|
certifi==2026.1.4
|
||||||
|
cffi==2.0.0
|
||||||
|
charset-normalizer==3.4.4
|
||||||
|
click==8.3.1
|
||||||
|
cryptography==46.0.5
|
||||||
|
cssselect2==0.9.0
|
||||||
|
dnspython==2.8.0
|
||||||
|
email-validator==2.3.0
|
||||||
|
fastapi==0.129.0
|
||||||
|
fastapi-cli==0.0.23
|
||||||
|
fastapi-cloud-cli==0.13.0
|
||||||
|
fastar==0.8.0
|
||||||
|
fonttools==4.61.1
|
||||||
|
greenlet==3.3.1
|
||||||
|
h11==0.16.0
|
||||||
|
httpcore==1.0.9
|
||||||
|
httptools==0.7.1
|
||||||
|
httpx==0.28.1
|
||||||
|
idna==3.11
|
||||||
|
Jinja2==3.1.6
|
||||||
|
lxml==6.0.2
|
||||||
|
markdown-it-py==4.0.0
|
||||||
|
MarkupSafe==3.0.3
|
||||||
|
mdurl==0.1.2
|
||||||
|
odfdo==3.20.2
|
||||||
|
pillow==12.1.1
|
||||||
|
psycopg2-binary==2.9.11
|
||||||
|
pycparser==3.0
|
||||||
|
pydantic==2.12.5
|
||||||
|
pydantic-extra-types==2.11.0
|
||||||
|
pydantic-settings==2.13.1
|
||||||
|
pydantic_core==2.41.5
|
||||||
|
pydyf==0.12.1
|
||||||
|
Pygments==2.19.2
|
||||||
|
PyJWT==2.11.0
|
||||||
|
pyphen==0.17.2
|
||||||
|
python-dotenv==1.2.1
|
||||||
|
python-multipart==0.0.22
|
||||||
|
PyYAML==6.0.3
|
||||||
|
requests==2.32.5
|
||||||
|
rich==14.3.2
|
||||||
|
rich-toolkit==0.19.4
|
||||||
|
rignore==0.7.6
|
||||||
|
sentry-sdk==2.53.0
|
||||||
|
shellingham==1.5.4
|
||||||
|
SQLAlchemy==2.0.46
|
||||||
|
sqlmodel==0.0.34
|
||||||
|
starlette==0.52.1
|
||||||
|
tinycss2==1.5.1
|
||||||
|
tinyhtml5==2.0.0
|
||||||
|
typer==0.24.0
|
||||||
|
typing-inspection==0.4.2
|
||||||
|
typing_extensions==4.15.0
|
||||||
|
urllib3==2.6.3
|
||||||
|
uvicorn==0.41.0
|
||||||
|
uvloop==0.22.1
|
||||||
|
watchfiles==1.1.1
|
||||||
|
weasyprint==68.1
|
||||||
|
webencodings==0.5.1
|
||||||
|
websockets==16.0
|
||||||
|
zopfli==0.4.1
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from sqlmodel import create_engine, SQLModel, Session
|
from sqlmodel import create_engine, SQLModel, Session
|
||||||
from src.settings import settings
|
from src.settings import settings
|
||||||
|
|
||||||
engine = create_engine(f'postgresql://{settings.db_user}:{settings.db_pass}@{settings.db_host}:54321/{settings.db_name}')
|
engine = create_engine(f'postgresql://{settings.db_user}:{settings.db_pass}@{settings.db_host}:5432/{settings.db_name}')
|
||||||
|
|
||||||
def get_session():
|
def get_session():
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ app.add_middleware(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
app.include_router(template_router)
|
app.include_router(template_router, prefix="/api")
|
||||||
app.include_router(contracts_router)
|
app.include_router(contracts_router, prefix="/api")
|
||||||
app.include_router(forms_router)
|
app.include_router(forms_router, prefix="/api")
|
||||||
app.include_router(productors_router)
|
app.include_router(productors_router, prefix="/api")
|
||||||
app.include_router(products_router)
|
app.include_router(products_router, prefix="/api")
|
||||||
app.include_router(users_router)
|
app.include_router(users_router, prefix="/api")
|
||||||
app.include_router(auth_router)
|
app.include_router(auth_router, prefix="/api")
|
||||||
app.include_router(shipment_router)
|
app.include_router(shipment_router, prefix="/api")
|
||||||
|
|
||||||
SQLModel.metadata.create_all(engine)
|
SQLModel.metadata.create_all(engine)
|
||||||
@@ -1,32 +1,39 @@
|
|||||||
version: "3.9"
|
|
||||||
services:
|
services:
|
||||||
# nginx:
|
nginx:
|
||||||
# restart: always
|
restart: always
|
||||||
# build:
|
build:
|
||||||
# context: .
|
context: .
|
||||||
# dockerfile: front/Dockerfile
|
dockerfile: frontend/Dockerfile
|
||||||
# args:
|
args:
|
||||||
# VUE_APP_ROOT_FQDN: ${SERVICE_ROOT_FQDN}
|
VITE_API_URL: ${VITE_API_URL}
|
||||||
# ports:
|
ports:
|
||||||
# - 80:80
|
- 80:80
|
||||||
# depends_on:
|
depends_on:
|
||||||
# - back
|
- back
|
||||||
# back:
|
back:
|
||||||
# build:
|
build:
|
||||||
# context: .
|
context: .
|
||||||
# dockerfile: back/Dockerfile
|
dockerfile: backend/Dockerfile
|
||||||
# restart: always
|
restart: always
|
||||||
# environment:
|
environment:
|
||||||
# SERVICE_ORIGIN: ${SERVICE_ORIGIN}
|
ORIGINS: ${ORIGINS}
|
||||||
# DB_HOST: database
|
DB_HOST: database
|
||||||
# MARIADB_USER: ${MARIADB_USER}
|
DB_USER: ${DB_USER}
|
||||||
# MARIADB_PASSWORD: ${MARIADB_PASSWORD}
|
DB_PASS: ${DB_PASS}
|
||||||
# MARIADB_DATABASE: ${MARIADB_DATABASE}
|
DB_NAME: ${DB_NAME}
|
||||||
# SERVICE_SECRET_KEY: ${SERVICE_SECRET_KEY}
|
SECRET_KEY: ${SECRET_KEY}
|
||||||
# ports:
|
VITE_API_URL: ${VITE_API_URL}
|
||||||
# - 8000:8000
|
KEYCLOAK_SERVER: ${KEYCLOAK_SERVER}
|
||||||
# depends_on:
|
KEYCLOAK_REALM: ${KEYCLOAK_REALM}
|
||||||
# - database
|
KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID}
|
||||||
|
KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET}
|
||||||
|
KEYCLOAK_REDIRECT_URI: ${KEYCLOAK_REDIRECT_URI}
|
||||||
|
DEBUG: ${DEBUG}
|
||||||
|
MAX_AGE: ${MAX_AGE}
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
database:
|
database:
|
||||||
image: postgres
|
image: postgres
|
||||||
restart: always
|
restart: always
|
||||||
@@ -35,8 +42,5 @@ services:
|
|||||||
POSTGRES_USER: ${DB_USER}
|
POSTGRES_USER: ${DB_USER}
|
||||||
POSTGRES_PASSWORD: ${DB_PASS}
|
POSTGRES_PASSWORD: ${DB_PASS}
|
||||||
POSTGRES_DB: ${DB_NAME}
|
POSTGRES_DB: ${DB_NAME}
|
||||||
ROOT_FQDN: ${ROOT_FQDN}
|
|
||||||
ports:
|
|
||||||
- "54321:5432"
|
|
||||||
volumes:
|
volumes:
|
||||||
db:
|
db:
|
||||||
20
frontend/Dockerfile
Normal file
20
frontend/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
FROM node:20.19-alpine AS build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ARG VITE_API_URL
|
||||||
|
ENV VITE_API_URL=$VITE_API_URL
|
||||||
|
|
||||||
|
COPY frontend/package.json frontend/package-lock.json /app/
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY frontend/ .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM nginx:latest
|
||||||
|
|
||||||
|
COPY --from=build /app/dist /srv/www/frontend
|
||||||
|
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=build /app/nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||||
@@ -199,6 +199,7 @@
|
|||||||
"your session has expired please log in again": "your session has expired please log in again",
|
"your session has expired please log in again": "your session has expired please log in again",
|
||||||
"session expired": "session expired",
|
"session expired": "session expired",
|
||||||
"user not allowed": "user not allowed",
|
"user not allowed": "user not allowed",
|
||||||
|
"roles": "roles",
|
||||||
"your keycloak user has no roles, please contact your administrator": "your keycloak user has no roles, please contact your administrator",
|
"your keycloak user has no roles, please contact your administrator": "your keycloak user has no roles, please contact your administrator",
|
||||||
"choose payment method": "choose your payment method (you do not need to pay now).",
|
"choose payment method": "choose your payment method (you do not need to pay now).",
|
||||||
"the product unit will be assigned to the quantity requested in the form": "the product unit defines the unit used in the contract form.",
|
"the product unit will be assigned to the quantity requested in the form": "the product unit defines the unit used in the contract form.",
|
||||||
|
|||||||
@@ -199,6 +199,7 @@
|
|||||||
"your session has expired please log in again": "votre session a expiré veuillez vous reconnecter.",
|
"your session has expired please log in again": "votre session a expiré veuillez vous reconnecter.",
|
||||||
"session expired": "session expirée",
|
"session expired": "session expirée",
|
||||||
"user not allowed": "utilisateur non authorisé",
|
"user not allowed": "utilisateur non authorisé",
|
||||||
|
"roles": "roles",
|
||||||
"your keycloak user has no roles, please contact your administrator": "votre utilisateur keycloak n'a pas de roles configurés, contactez votre administrateur.",
|
"your keycloak user has no roles, please contact your administrator": "votre utilisateur keycloak n'a pas de roles configurés, contactez votre administrateur.",
|
||||||
"choose payment method": "choisissez votre méthode de paiement (vous n'avez pas à payer tout de suite, uniquement renseigner comment vous souhaitez régler votre commande).",
|
"choose payment method": "choisissez votre méthode de paiement (vous n'avez pas à payer tout de suite, uniquement renseigner comment vous souhaitez régler votre commande).",
|
||||||
"the product unit will be assigned to the quantity requested in the form": "l'unité de vente du produit définit l'unité associée à la quantité demandée dans le formulaire des amapiens.",
|
"the product unit will be assigned to the quantity requested in the form": "l'unité de vente du produit définit l'unité associée à la quantité demandée dans le formulaire des amapiens.",
|
||||||
|
|||||||
26
frontend/nginx/default.conf
Normal file
26
frontend/nginx/default.conf
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
|
||||||
|
server_name localhost;
|
||||||
|
root /srv/www/frontend;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_pass http://back;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream backend {
|
||||||
|
server back:8000;
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
Group,
|
Group,
|
||||||
Modal,
|
Modal,
|
||||||
MultiSelect,
|
MultiSelect,
|
||||||
Select,
|
|
||||||
TextInput,
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
type ModalBaseProps,
|
type ModalBaseProps,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ActionIcon, Table, Tooltip } from "@mantine/core";
|
import { ActionIcon, Badge, Box, Table, Tooltip } from "@mantine/core";
|
||||||
import { t } from "@/config/i18n";
|
import { t } from "@/config/i18n";
|
||||||
import { IconEdit, IconX } from "@tabler/icons-react";
|
import { IconEdit, IconX } from "@tabler/icons-react";
|
||||||
import { type User } from "@/services/resources/users";
|
import { type User } from "@/services/resources/users";
|
||||||
@@ -18,6 +18,31 @@ export default function UserRow({ user }: UserRowProps) {
|
|||||||
<Table.Tr key={user.id}>
|
<Table.Tr key={user.id}>
|
||||||
<Table.Td>{user.name}</Table.Td>
|
<Table.Td>{user.name}</Table.Td>
|
||||||
<Table.Td>{user.email}</Table.Td>
|
<Table.Td>{user.email}</Table.Td>
|
||||||
|
<Table.Td style={{maxWidth: 200}}>
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 4
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{user.roles.slice(0, 3).map((value) => (
|
||||||
|
<Badge key={value.id} size="xs">
|
||||||
|
{t(value.name, { capfirst: true })}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
{
|
||||||
|
user.roles.length > 3 && (
|
||||||
|
<Tooltip
|
||||||
|
label={user.roles.slice(3).map(role=>`${role.name} `)}
|
||||||
|
>
|
||||||
|
<Badge size="xs" variant="light">
|
||||||
|
+{user.roles.length - 3}
|
||||||
|
</Badge>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Tooltip label={t("edit user", { capfirst: true })}>
|
<Tooltip label={t("edit user", { capfirst: true })}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export default function Contracts() {
|
|||||||
label={t("download recap", { capfirst: true })}
|
label={t("download recap", { capfirst: true })}
|
||||||
>
|
>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
disabled={true}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
navigate(
|
navigate(
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export default function Users() {
|
|||||||
<Table.Tr>
|
<Table.Tr>
|
||||||
<Table.Th>{t("name", { capfirst: true })}</Table.Th>
|
<Table.Th>{t("name", { capfirst: true })}</Table.Th>
|
||||||
<Table.Th>{t("email", { capfirst: true })}</Table.Th>
|
<Table.Th>{t("email", { capfirst: true })}</Table.Th>
|
||||||
|
<Table.Th>{t("roles", { capfirst: true })}</Table.Th>
|
||||||
<Table.Th>{t("actions", { capfirst: true })}</Table.Th>
|
<Table.Th>{t("actions", { capfirst: true })}</Table.Th>
|
||||||
</Table.Tr>
|
</Table.Tr>
|
||||||
</Table.Thead>
|
</Table.Thead>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type User = {
|
|||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
products: Product[];
|
products: Product[];
|
||||||
roles: string[];
|
roles: Role[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserInputs = {
|
export type UserInputs = {
|
||||||
|
|||||||
Reference in New Issue
Block a user