Compare commits

..

14 Commits

Author SHA1 Message Date
lebaudantoine 302e5a503f wip introduce convert logic 2024-10-21 00:57:08 +02:00
lebaudantoine a1c497ef27 wip install prettier and format 2024-10-21 00:52:31 +02:00
lebaudantoine 89a44e4979 add blocknote micro service to kube 2024-10-20 23:49:25 +02:00
lebaudantoine 993394d91f add the image to the tilt stack 2024-10-20 23:48:05 +02:00
lebaudantoine 8f386402e8 add the image to the compose stack 2024-10-20 23:16:04 +02:00
lebaudantoine c7c252f9a1 wip containerized the app 2024-10-20 23:15:37 +02:00
lebaudantoine 5c0e7b9043 wip bootstrap an express app 2024-10-20 23:15:20 +02:00
Anthony LC e35671c450 📝(docs) add CONTRIBUTING.md doc
Add a CONTRIBUTING.md file to the project root
to help new contributors understand how to
contribute to the project.
2024-10-18 09:33:38 +02:00
Anthony LC 15235a9bc2 🔖(minor) release 1.6.0
Added:
- AI to doc editor
- (backend) allow uploading more types of attachments
- (frontend) add buttons to copy document to clipboard as HTML/Markdown

Changed:
- ♻️(frontend) More multi theme friendly
- ♻️ Bootstrap frontend
- ♻️ Add username in email

Fixed:
- 🛂(backend) do not duplicate user when disabled
- 🐛(frontend) invalidate queries after removing user
- 🐛(backend) Fix dysfunctional permissions on document create
- 🐛(backend) fix nginx docker container
- 🐛(frontend) fix copy paste firefox
2024-10-17 17:50:57 +02:00
Anthony LC b360bd8494 ⬆️(frontend) upgrade blocknote to 0.17.0
Version 0.17.0 of Blocknote fixes the
copy paste issue in the editor with Firefox.
2024-10-17 17:15:22 +02:00
Samuel Paccoud - DINUM 6a95d24441 🛂(backend) do not duplicate user when disabled
When a user is disabled and tries to login, we
don't want the user to be duplicated,
the user should not be able to login.

Fixes #324

Work initially contributed by @qbey on:
https://github.com/numerique-gouv/people/pull/456
2024-10-17 16:54:40 +02:00
Anthony LC e816f0afc8 🚸(frontend) add toast error when AI request fails
When the AI request fails, a toast error is
displayed to the user.
2024-10-17 16:22:13 +02:00
lindenb1 7e8732822b 🐛(docker) update docker-compose.yml to make nginx depend on app-dev
Modified docker-compose.yml to ensure nginx starts only after app-dev.

Signed-off-by: lindenb1 <linden@b1-systems.de>
2024-10-17 14:50:21 +02:00
Anthony LC 9ed6b11bb1 ⬆️(dependencies) update js dependencies 2024-10-17 13:11:01 +02:00
39 changed files with 7914 additions and 2268 deletions
+13 -1
View File
@@ -11,6 +11,13 @@ and this project adheres to
## Added
- 📝Contributing.md #352
## [1.6.0] - 2024-10-17
## Added
- ✨AI to doc editor #250
- ✨(backend) allow uploading more types of attachments #309
- ✨(frontend) add buttons to copy document to clipboard as HTML/Markdown #300
@@ -23,8 +30,12 @@ and this project adheres to
## Fixed
- 🛂(backend) do not duplicate user when disabled
- 🐛(frontend) invalidate queries after removing user #336
- 🐛(backend) Fix dysfunctional permissions on document create #329
- 🐛(backend) fix nginx docker container #340
- 🐛(frontend) fix copy paste firefox #353
## [1.5.1] - 2024-10-10
@@ -204,7 +215,8 @@ and this project adheres to
- 🚀 Impress, project to manage your documents easily and collaboratively.
[unreleased]: https://github.com/numerique-gouv/impress/compare/v1.5.1...main
[unreleased]: https://github.com/numerique-gouv/impress/compare/v1.6.0...main
[v1.6.0]: https://github.com/numerique-gouv/impress/releases/v1.6.0
[1.5.1]: https://github.com/numerique-gouv/impress/releases/v1.5.1
[1.5.0]: https://github.com/numerique-gouv/impress/releases/v1.5.0
[1.4.0]: https://github.com/numerique-gouv/impress/releases/v1.4.0
+54
View File
@@ -0,0 +1,54 @@
# Contributing to the Project
Thank you for taking the time to contribute! Please follow these guidelines to ensure a smooth and productive workflow. 🚀🚀🚀
To get started with the project, please refer to the [README.md](https://github.com/numerique-gouv/impress/blob/main/README.md) for detailed instructions.
## Creating an Issue
When creating an issue, please provide the following details:
1. **Title**: A concise and descriptive title for the issue.
2. **Description**: A detailed explanation of the issue, including relevant context or screenshots if applicable.
3. **Steps to Reproduce**: If the issue is a bug, include the steps needed to reproduce the problem.
4. **Expected vs. Actual Behavior**: Describe what you expected to happen and what actually happened.
5. **Labels**: Add appropriate labels to categorize the issue (e.g., bug, feature request, documentation).
## Commit Message Format
All commit messages must adhere to the following format:
`<gitmoji>(type) title description`
* **<gitmoji>**: Use a gitmoji to represent the purpose of the commit. For example, ✨ for adding a new feature or 🔥 for removing something, see the list here: <https://gitmoji.dev/>.
* **(type)**: Describe the type of change. Common types include `backend`, `frontend`, `CI`, `docker` etc...
* **title**: A short, descriptive title for the change, starting with a lowercase character.
* **description**: Include additional details about what was changed and why.
### Example Commit Message
```
✨(frontend) add user authentication logic
Implemented login and signup features, and integrated OAuth2 for social login.
```
## Pull Requests
It is nice to add information about the purpose of the pull request to help reviewers understand the context and intent of the changes.
Once all the required tests have passed, you can request a review from the project maintainers.
## Code Style
Please maintain consistency in code style. Run any linting tools available to make sure the code is clean and follows the project's conventions.
## Tests
Make sure that all new features or fixes have corresponding tests. Run the test suite before pushing your changes to ensure that nothing is broken.
## Asking for Help
If you need any help while contributing, feel free to open a discussion or ask for guidance in the issue tracker. We are more than happy to assist!
Thank you for your contributions! 👍
+1
View File
@@ -122,6 +122,7 @@ logs: ## display app-dev logs (follow mode)
run: ## start the wsgi (production) and development server
@$(COMPOSE) up --force-recreate -d celery-dev
@$(COMPOSE) up --force-recreate -d nginx
@$(COMPOSE) up --force-recreate -d y-provider
@echo "Wait for postgresql to be up..."
@$(WAIT_DB)
+12
View File
@@ -39,6 +39,18 @@ docker_build(
]
)
docker_build(
'localhost:5001/impress-blocknote:latest',
context='..',
dockerfile='../src/blocknote/Dockerfile',
only=['./src/blocknote', './docker', './.dockerignore'],
target = 'production',
live_update=[
sync('../src/blocknote', '/home/blocknote'),
]
)
k8s_yaml(local('cd ../src/helm && helmfile -n impress -e dev template .'))
migration = '''
+11 -1
View File
@@ -63,7 +63,6 @@ services:
- mailcatcher
- redis
- createbuckets
- nginx
celery-dev:
user: ${DOCKER_USER:-1000}
@@ -118,6 +117,7 @@ services:
- ./docker/files/etc/nginx/conf.d:/etc/nginx/conf.d:ro
depends_on:
- keycloak
- app-dev
frontend-dev:
user: "${DOCKER_USER:-1000}"
@@ -134,6 +134,16 @@ services:
ports:
- "3000:3000"
blocknote-converter:
user: "${DOCKER_USER:-1000}"
build:
context: .
dockerfile: ./src/blocknote/Dockerfile
target: production
image: blocknote:blocknote-production
ports:
- "8081:8081"
dockerize:
image: jwilder/dockerize
+1 -1
Submodule secrets updated: 59590c285c...38594182e8
-31
View File
@@ -8,8 +8,6 @@ from rest_framework import views as drf_views
from rest_framework.decorators import api_view
from rest_framework.response import Response
from ..models import Document, User, RoleChoices, DocumentAccess
def exception_handler(exc, context):
"""Handle Django ValidationError as an accepted exception.
@@ -40,32 +38,3 @@ def get_frontend_configuration(request):
}
frontend_configuration.update(settings.FRONTEND_CONFIGURATION)
return Response(frontend_configuration)
@api_view(["POST"])
def create_summary(request):
"""Wip."""
data = request.data
document = Document(
title="Votre résumé",
link_reach="authenticated",
link_role="reader",
)
document.save()
owner_user = User.objects.get(email=data["owner"])
document_access = DocumentAccess(
user=owner_user,
document=document,
role=RoleChoices.OWNER
)
document_access.save()
document.content = data["content"]
document.save()
return Response({"id": document.id})
+4 -2
View File
@@ -84,6 +84,8 @@ class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
user = self.get_existing_user(sub, email)
if user:
if not user.is_active:
raise SuspiciousOperation(_("User account is disabled"))
self.update_user_if_needed(user, claims)
elif self.get_settings("OIDC_CREATE_USER", True):
user = User.objects.create(sub=sub, password="!", **claims) # noqa: S106
@@ -101,11 +103,11 @@ class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
def get_existing_user(self, sub, email):
"""Fetch existing user by sub or email."""
try:
return User.objects.get(sub=sub, is_active=True)
return User.objects.get(sub=sub)
except User.DoesNotExist:
if email and settings.OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION:
try:
return User.objects.get(email=email, is_active=True)
return User.objects.get(email=email)
except User.DoesNotExist:
pass
return None
@@ -305,3 +305,63 @@ def test_authentication_get_userinfo_invalid_response():
match="Invalid response format or token verification failed",
):
oidc_backend.get_userinfo("fake_access_token", None, None)
def test_authentication_getter_existing_disabled_user_via_sub(
django_assert_num_queries, monkeypatch
):
"""
If an existing user matches the sub but is disabled,
an error should be raised and a user should not be created.
"""
klass = OIDCAuthenticationBackend()
db_user = UserFactory(is_active=False)
def get_userinfo_mocked(*args):
return {
"sub": db_user.sub,
"email": db_user.email,
"first_name": "John",
"last_name": "Doe",
}
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
with (
django_assert_num_queries(1),
pytest.raises(SuspiciousOperation, match="User account is disabled"),
):
klass.get_or_create_user(access_token="test-token", id_token=None, payload=None)
assert models.User.objects.count() == 1
def test_authentication_getter_existing_disabled_user_via_email(
django_assert_num_queries, monkeypatch
):
"""
If an existing user does not matches the sub but matches the email and is disabled,
an error should be raised and a user should not be created.
"""
klass = OIDCAuthenticationBackend()
db_user = UserFactory(is_active=False)
def get_userinfo_mocked(*args):
return {
"sub": "random",
"email": db_user.email,
"first_name": "John",
"last_name": "Doe",
}
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
with (
django_assert_num_queries(2),
pytest.raises(SuspiciousOperation, match="User account is disabled"),
):
klass.get_or_create_user(access_token="test-token", id_token=None, payload=None)
assert models.User.objects.count() == 1
+1 -2
View File
@@ -5,7 +5,7 @@ from django.urls import include, path, re_path
from rest_framework.routers import DefaultRouter
from core.api import viewsets, create_summary
from core.api import viewsets
from core.authentication.urls import urlpatterns as oidc_urls
# - Main endpoints
@@ -44,7 +44,6 @@ urlpatterns = [
[
*router.urls,
*oidc_urls,
path("summary/", create_summary, name="create_summary"),
re_path(
r"^documents/(?P<resource_id>[0-9a-z-]*)/",
include(document_related_router.urls),
+1 -1
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "impress"
version = "1.5.1"
version = "1.6.0"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
+5
View File
@@ -0,0 +1,5 @@
{
"semi": false,
"trailingComma": "es5",
"singleQuote": true
}
+28
View File
@@ -0,0 +1,28 @@
FROM node:20-alpine AS dependencies
WORKDIR /home/blocknote
COPY ./src/blocknote/package*.json ./
RUN npm install
COPY .dockerignore ./.dockerignore
COPY ./src/blocknote/ .
FROM dependencies AS blocknote-builder
WORKDIR /home/blocknote
RUN npm run build
# ---- Blocknote image ----
FROM blocknote-builder AS production
# Un-privileged user running the application
ARG DOCKER_USER
USER ${DOCKER_USER}
COPY ./docker/files/usr/local/bin/entrypoint /usr/local/bin/entrypoint
ENTRYPOINT ["/usr/local/bin/entrypoint"]
CMD ["npm", "run", "start"]
+5
View File
@@ -0,0 +1,5 @@
{
"watch": ["src"],
"ext": "ts",
"exec": "concurrently \"npx tsc --watch\" \"ts-node src/index.ts\""
}
+5084
View File
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
{
"name": "blocknote-server",
"version": "1.0.0",
"license": "MIT",
"main": "dist/index.js",
"keywords": [
"nodejs",
"bootstrap",
"express"
],
"scripts": {
"build": "npx tsc",
"start": "node dist/index.js",
"dev": "nodemon src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1",
"format": "prettier --write ./src",
"check": "prettier --check ./src"
},
"dependencies": {
"@blocknote/server-util": "0.17.1",
"dotenv": "16.4.5",
"express": "4.21.1",
"prettier": "3.3.3",
"yjs": "13.6.20"
},
"devDependencies": {
"@types/express": "5.0.0",
"@types/node": "22.7.7",
"concurrently": "9.0.1",
"nodemon": "3.1.7",
"ts-node": "10.9.2",
"typescript": "5.6.3",
"prettier": "3.3.3"
}
}
+37
View File
@@ -0,0 +1,37 @@
import express, { Express, Request, Response } from 'express'
import { asyncWrapper, convertMarkdown } from './utils'
import dotenv from 'dotenv'
import bodyParser from 'body-parser'
dotenv.config()
const app: Express = express()
const router = express.Router()
const port = process.env.PORT ?? 8081
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
// Logging middleware, logs the request method and path for each incoming request
router.use(async function (req, res, next) {
console.log(`/${req.method}`)
next()
})
// Liveness probe endpoint for Kubernetes health checks
router.get('/__heartbeat__', (req: Request, res: Response) => {
res.status(200).send({ status: 'OK' })
})
// Load balancer heartbeat check, useful to detect app readiness
router.get('/__lbheartbeat__', (req: Request, res: Response) => {
res.status(200).send({ status: 'OK' })
})
router.post('/', asyncWrapper(convertMarkdown))
app.use('/', router)
app.listen(port, () => {
console.log(`[server]: Server listening on port ${port}`)
})
+60
View File
@@ -0,0 +1,60 @@
// Utility functions for handling markdown conversion and related operations
import { NextFunction, Request, Response } from 'express'
import { ServerBlockNoteEditor } from '@blocknote/server-util'
import Y from 'yjs'
const toBase64 = function (str: Uint8Array) {
return Buffer.from(str).toString('base64')
}
export const asyncWrapper = (
asyncFn: (req: Request, res: Response) => Promise<Response>
) => {
return function (req: Request, res: Response, next: NextFunction) {
asyncFn(req, res).catch(next)
}
}
const validateContent = (content: string | undefined): string => {
if (!content) {
throw new Error('Content is required')
}
return content
}
const parseMarkdownToBlocks = async (
blockNoteEditor: ServerBlockNoteEditor,
content: string
) => {
try {
const blocks = await blockNoteEditor.tryParseMarkdownToBlocks(content)
if (!blocks || blocks.length === 0) {
throw new Error('No valid blocks generated')
}
return blocks
} catch (error) {
throw new Error('Failed to parse markdown content')
}
}
const processContentBlocks = (server: ServerBlockNoteEditor, blocks: any[]) => {
try {
const yDocument = server.blocksToYDoc(blocks, 'document-store')
return toBase64(Y.encodeStateAsUpdate(yDocument))
} catch (error) {
throw new Error('Failed to process content blocks')
}
}
export const convertMarkdown = async (req: Request, res: Response) => {
try {
const content = validateContent(req.body.content)
const editor = ServerBlockNoteEditor.create()
const blocks = await parseMarkdownToBlocks(editor, content)
const encodedContent = processContentBlocks(editor, blocks)
return res.send({ content: encodedContent })
} catch (error) {
return res.status(500).json({ error: (error as Error).message })
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "app-e2e",
"version": "1.5.1",
"version": "1.6.0",
"private": true,
"scripts": {
"lint": "eslint . --ext .ts",
@@ -12,7 +12,7 @@
"test:ui::chromium": "yarn test:ui --project=chromium"
},
"devDependencies": {
"@playwright/test": "1.47.2",
"@playwright/test": "1.48.1",
"@types/node": "*",
"@types/pdf-parse": "1.1.4",
"eslint-config-impress": "*",
+13 -13
View File
@@ -1,6 +1,6 @@
{
"name": "app-impress",
"version": "1.5.1",
"version": "1.6.0",
"private": true,
"scripts": {
"dev": "next dev",
@@ -19,36 +19,36 @@
"@blocknote/mantine": "*",
"@blocknote/react": "*",
"@gouvfr-lasuite/integration": "1.0.2",
"@hocuspocus/provider": "2.13.6",
"@hocuspocus/provider": "2.13.7",
"@openfun/cunningham-react": "2.9.4",
"@tanstack/react-query": "5.56.2",
"i18next": "23.15.1",
"@tanstack/react-query": "5.59.15",
"i18next": "23.16.0",
"idb": "8.0.0",
"lodash": "4.17.21",
"luxon": "3.5.0",
"next": "14.2.13",
"next": "14.2.15",
"react": "*",
"react-aria-components": "1.3.3",
"react-aria-components": "1.4.1",
"react-dom": "*",
"react-i18next": "15.0.2",
"react-i18next": "15.0.3",
"react-select": "5.8.1",
"styled-components": "6.1.13",
"y-protocols": "1.0.6",
"yjs": "*",
"zustand": "4.5.5"
"zustand": "5.0.0"
},
"devDependencies": {
"@svgr/webpack": "8.1.0",
"@tanstack/react-query-devtools": "5.58.0",
"@tanstack/react-query-devtools": "5.59.15",
"@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.5.0",
"@testing-library/jest-dom": "6.6.1",
"@testing-library/react": "16.0.1",
"@testing-library/user-event": "14.5.2",
"@types/jest": "29.5.13",
"@types/lodash": "4.17.9",
"@types/lodash": "4.17.10",
"@types/luxon": "3.4.2",
"@types/node": "*",
"@types/react": "18.3.10",
"@types/react": "18.3.11",
"@types/react-dom": "*",
"cross-env": "*",
"dotenv": "16.4.5",
@@ -58,7 +58,7 @@
"jest-environment-jsdom": "29.7.0",
"node-fetch": "2.7.0",
"prettier": "3.3.3",
"stylelint": "16.9.0",
"stylelint": "16.10.0",
"stylelint-config-standard": "36.0.1",
"stylelint-prettier": "5.0.2",
"typescript": "*",
@@ -374,6 +374,7 @@ const useHandleAIError = () => {
});
}
toast(t('AI seems busy! Please try again.'), VariantType.ERROR);
console.error(error);
},
[toast, t],
@@ -5,6 +5,7 @@
"\"{{email}}\" is already invited to the document.": "\"{{email}}\" est déjà invité à accéder au document.",
"\"{{email}}\" is already member of the document.": "\"{{email}}\" est déjà membre du document.",
"AI Actions": "Actions IA",
"AI seems busy! Please try again.": "L'IA semble occupée ! Veuillez réessayer.",
"Accessibility": "Accessibilité",
"Accessibility statement": "Déclaration d'accessibilité",
"Address:": "Adresse :",
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "impress",
"version": "1.5.1",
"version": "1.6.0",
"private": true,
"workspaces": {
"packages": [
@@ -25,18 +25,18 @@
"i18n:test": "yarn I18N run test"
},
"resolutions": {
"@blocknote/core": "0.16.0",
"@blocknote/mantine": "0.16.0",
"@blocknote/react": "0.16.0",
"@types/node": "20.16.10",
"@types/react-dom": "18.3.0",
"@typescript-eslint/eslint-plugin": "8.7.0",
"@typescript-eslint/parser": "8.7.0",
"@blocknote/core": "0.17.0",
"@blocknote/mantine": "0.17.0",
"@blocknote/react": "0.17.0",
"@types/node": "20.16.12",
"@types/react-dom": "18.3.1",
"@typescript-eslint/eslint-plugin": "8.9.0",
"@typescript-eslint/parser": "8.9.0",
"cross-env": "7.0.3",
"eslint": "8.57.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"typescript": "5.6.2",
"yjs": "13.6.19"
"typescript": "5.6.3",
"yjs": "13.6.20"
}
}
@@ -1,24 +1,24 @@
{
"name": "eslint-config-impress",
"version": "1.5.1",
"version": "1.6.0",
"license": "MIT",
"scripts": {
"lint": "eslint --ext .js ."
},
"dependencies": {
"@next/eslint-plugin-next": "14.2.13",
"@tanstack/eslint-plugin-query": "5.58.1",
"@next/eslint-plugin-next": "14.2.15",
"@tanstack/eslint-plugin-query": "5.59.7",
"@typescript-eslint/eslint-plugin": "*",
"@typescript-eslint/parser": "*",
"eslint": "*",
"eslint-config-next": "14.2.13",
"eslint-config-next": "14.2.15",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "2.30.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jest": "28.8.3",
"eslint-plugin-jsx-a11y": "6.10.0",
"eslint-plugin-playwright": "1.6.2",
"eslint-plugin-playwright": "1.7.0",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-react": "7.37.0",
"eslint-plugin-react": "7.37.1",
"eslint-plugin-testing-library": "6.3.0",
"prettier": "3.3.3"
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "packages-i18n",
"version": "1.5.1",
"version": "1.6.0",
"private": true,
"scripts": {
"extract-translation": "yarn extract-translation:impress",
@@ -15,7 +15,7 @@
"@types/jest": "29.5.13",
"@types/node": "*",
"eslint-config-impress": "*",
"eslint-plugin-import": "2.30.0",
"eslint-plugin-import": "2.31.0",
"i18next-parser": "9.0.2",
"jest": "29.7.0",
"ts-jest": "29.2.5",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "server-y-provider",
"version": "1.5.1",
"version": "1.6.0",
"description": "Y.js provider for docs",
"repository": "https://github.com/numerique-gouv/impress",
"license": "MIT",
@@ -15,7 +15,7 @@
"node": ">=18"
},
"dependencies": {
"@hocuspocus/server": "2.13.6",
"@hocuspocus/server": "2.13.7",
"y-protocols": "1.0.6"
},
"devDependencies": {
+2182 -1995
View File
File diff suppressed because it is too large Load Diff
@@ -93,6 +93,14 @@ yProvider:
pullPolicy: Always
tag: "latest"
blocknote:
replicas: 1
image:
repository: localhost:5001/impress-blocknote
pullPolicy: Always
tag: "latest"
ingress:
enabled: true
host: impress.127.0.0.1.nip.io
-1
View File
@@ -1 +0,0 @@
../../../../secrets/numerique-gouv/impress/env/docs-ia/secrets.enc.yaml
@@ -1,184 +0,0 @@
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "v-demo"
backend:
migrateJobAnnotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
envVars:
AI_API_KEY:
secretKeyRef:
name: backend
key: AI_API_KEY
AI_BASE_URL:
secretKeyRef:
name: backend
key: AI_BASE_URL
AI_MODEL: meta-llama/Meta-Llama-3.1-70B-Instruct
DJANGO_CSRF_TRUSTED_ORIGINS: http://docs-ia.beta.numerique.gouv.fr,https://docs-ia.beta.numerique.gouv.fr
DJANGO_CONFIGURATION: Production
DJANGO_ALLOWED_HOSTS: "*"
DJANGO_SECRET_KEY:
secretKeyRef:
name: backend
key: DJANGO_SECRET_KEY
DJANGO_SETTINGS_MODULE: impress.settings
DJANGO_SUPERUSER_EMAIL:
secretKeyRef:
name: backend
key: DJANGO_SUPERUSER_EMAIL
DJANGO_SUPERUSER_PASSWORD:
secretKeyRef:
name: backend
key: DJANGO_SUPERUSER_PASSWORD
DJANGO_EMAIL_HOST: "smtp.tem.scw.cloud"
DJANGO_EMAIL_PORT: 587
DJANGO_EMAIL_USE_TLS: True
DJANGO_EMAIL_FROM: "noreply@docs.beta.numerique.gouv.fr"
DJANGO_EMAIL_HOST_USER:
secretKeyRef:
name: backend
key: DJANGO_EMAIL_HOST_USER
DJANGO_EMAIL_HOST_PASSWORD:
secretKeyRef:
name: backend
key: DJANGO_EMAIL_HOST_PASSWORD
DJANGO_SILENCED_SYSTEM_CHECKS: security.W008,security.W004
OIDC_OP_JWKS_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/jwks
OIDC_OP_AUTHORIZATION_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/authorize
OIDC_OP_TOKEN_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/token
OIDC_OP_USER_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/userinfo
OIDC_OP_LOGOUT_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/session/end
OIDC_RP_CLIENT_ID:
secretKeyRef:
name: backend
key: OIDC_RP_CLIENT_ID
OIDC_RP_CLIENT_SECRET:
secretKeyRef:
name: backend
key: OIDC_RP_CLIENT_SECRET
OIDC_RP_SIGN_ALGO: RS256
OIDC_RP_SCOPES: "openid email"
OIDC_REDIRECT_ALLOWED_HOSTS: https://docs-ia.beta.numerique.gouv.fr
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
LOGIN_REDIRECT_URL: https://docs-ia.beta.numerique.gouv.fr
LOGIN_REDIRECT_URL_FAILURE: https://docs-ia.beta.numerique.gouv.fr
LOGOUT_REDIRECT_URL: https://docs-ia.beta.numerique.gouv.fr
DB_HOST:
secretKeyRef:
name: postgresql.postgres.libre.sh
key: host
DB_NAME:
secretKeyRef:
name: postgresql.postgres.libre.sh
key: database
DB_USER:
secretKeyRef:
name: postgresql.postgres.libre.sh
key: username
DB_PASSWORD:
secretKeyRef:
name: postgresql.postgres.libre.sh
key: password
DB_PORT:
secretKeyRef:
name: postgresql.postgres.libre.sh
key: port
POSTGRES_USER:
secretKeyRef:
name: postgresql.postgres.libre.sh
key: username
POSTGRES_DB:
secretKeyRef:
name: postgresql.postgres.libre.sh
key: database
POSTGRES_PASSWORD:
secretKeyRef:
name: postgresql.postgres.libre.sh
key: password
REDIS_URL:
secretKeyRef:
name: redis.redis.libre.sh
key: url
AWS_S3_ENDPOINT_URL:
secretKeyRef:
name: impress-media-storage.bucket.libre.sh
key: url
AWS_S3_ACCESS_KEY_ID:
secretKeyRef:
name: impress-media-storage.bucket.libre.sh
key: accessKey
AWS_S3_SECRET_ACCESS_KEY:
secretKeyRef:
name: impress-media-storage.bucket.libre.sh
key: secretKey
AWS_STORAGE_BUCKET_NAME:
secretKeyRef:
name: impress-media-storage.bucket.libre.sh
key: bucket
AWS_S3_REGION_NAME: local
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
createsuperuser:
command:
- "/bin/sh"
- "-c"
- |
python manage.py createsuperuser --email $DJANGO_SUPERUSER_EMAIL --password $DJANGO_SUPERUSER_PASSWORD
restartPolicy: Never
frontend:
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "v-demo"
yProvider:
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "v-demo"
ingress:
enabled: true
host: docs-ia.beta.numerique.gouv.fr
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
ingressWS:
enabled: true
host: docs-ia.beta.numerique.gouv.fr
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
ingressAdmin:
enabled: true
host: docs-ia.beta.numerique.gouv.fr
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy-preprod.beta.numerique.gouv.fr/oauth2/start
nginx.ingress.kubernetes.io/auth-url: https://oauth2-proxy-preprod.beta.numerique.gouv.fr/oauth2/auth
ingressMedia:
enabled: true
host: docs-ia.beta.numerique.gouv.fr
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
nginx.ingress.kubernetes.io/auth-url: https://docs-ia.beta.numerique.gouv.fr/api/v1.0/documents/retrieve-auth/
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /docs-ia-impress-media-storage/$1
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/upstream-vhost: s3.margaret-hamilton.indiehosters.net
serviceMedia:
host: s3.margaret-hamilton.indiehosters.net
port: 443
@@ -1,7 +1,7 @@
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "v1.5.1-preprod"
tag: "v1.6.0-preprod"
backend:
migrateJobAnnotations:
@@ -133,13 +133,13 @@ frontend:
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "v1.5.1-preprod"
tag: "v1.6.0-preprod"
yProvider:
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "v1.5.1-preprod"
tag: "v1.6.0-preprod"
ingress:
enabled: true
@@ -1,7 +1,7 @@
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "v1.5.1"
tag: "v1.6.0"
backend:
migrateJobAnnotations:
@@ -133,13 +133,13 @@ frontend:
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "v1.5.1"
tag: "v1.6.0"
yProvider:
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "v1.5.1"
tag: "v1.6.0"
ingress:
enabled: true
-5
View File
@@ -70,11 +70,6 @@ environments:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
docs-ia:
values:
- version: 0.0.1
secrets:
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
preprod:
values:
- version: 0.0.1
+9
View File
@@ -157,6 +157,15 @@ Requires top level scope
{{ include "impress.fullname" . }}-y-provider
{{- end }}
{{/*
Full name for the blocknote
Requires top level scope
*/}}
{{- define "impress.blocknote.fullname" -}}
{{ include "impress.fullname" . }}-blocknote
{{- end }}
{{/*
Usage : {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" .Values.path.to.the.image1) }}
*/}}
@@ -0,0 +1,136 @@
{{- $envVars := include "impress.common.env" (list . .Values.blocknote) -}}
{{- $fullName := include "impress.blocknote.fullname" . -}}
{{- $component := "blocknote" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
spec:
replicas: {{ .Values.blocknote.replicas }}
selector:
matchLabels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 6 }}
template:
metadata:
annotations:
{{- with .Values.blocknote.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 8 }}
spec:
{{- if $.Values.image.credentials }}
imagePullSecrets:
- name: {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end}}
shareProcessNamespace: {{ .Values.blocknote.shareProcessNamespace }}
containers:
{{- with .Values.blocknote.sidecars }}
{{- toYaml . | nindent 8 }}
{{- end }}
- name: {{ .Chart.Name }}
image: "{{ (.Values.blocknote.image | default dict).repository | default .Values.image.repository }}:{{ (.Values.blocknote.image | default dict).tag | default .Values.image.tag }}"
imagePullPolicy: {{ (.Values.blocknote.image | default dict).pullPolicy | default .Values.image.pullPolicy }}
{{- with .Values.blocknote.command }}
command:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.blocknote.args }}
args:
{{- toYaml . | nindent 12 }}
{{- end }}
env:
{{- if $envVars}}
{{- $envVars | indent 12 }}
{{- end }}
{{- with .Values.blocknote.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.blocknote.service.targetPort }}
protocol: TCP
{{- if .Values.blocknote.probes.liveness }}
livenessProbe:
{{- include "impress.probes.abstract" (merge .Values.blocknote.probes.liveness (dict "targetPort" .Values.blocknote.service.targetPort )) | nindent 12 }}
{{- end }}
{{- if .Values.blocknote.probes.readiness }}
readinessProbe:
{{- include "impress.probes.abstract" (merge .Values.blocknote.probes.readiness (dict "targetPort" .Values.blocknote.service.targetPort )) | nindent 12 }}
{{- end }}
{{- if .Values.blocknote.probes.startup }}
startupProbe:
{{- include "impress.probes.abstract" (merge .Values.blocknote.probes.startup (dict "targetPort" .Values.blocknote.service.targetPort )) | nindent 12 }}
{{- end }}
{{- with .Values.blocknote.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
subPath: content
{{- end }}
{{- range $name, $volume := .Values.blocknote.persistence }}
- name: "{{ $name }}"
mountPath: "{{ $volume.mountPath }}"
{{- end }}
{{- range .Values.blocknote.extraVolumeMounts }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
subPath: {{ .subPath | default "" }}
readOnly: {{ .readOnly }}
{{- end }}
{{- with .Values.blocknote.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.blocknote.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.blocknote.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
name: "{{ include "impress.fullname" $ }}-files-{{ $index }}"
{{- end }}
{{- range $name, $volume := .Values.blocknote.persistence }}
- name: "{{ $name }}"
{{- if eq $volume.type "emptyDir" }}
emptyDir: {}
{{- else }}
persistentVolumeClaim:
claimName: "{{ $fullName }}-{{ $name }}"
{{- end }}
{{- end }}
{{- range .Values.blocknote.extraVolumes }}
- name: {{ .name }}
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}
{{- else if .csi }}
csi:
{{- toYaml .csi | nindent 12 }}
{{- else if .configMap }}
configMap:
{{- toYaml .configMap | nindent 12 }}
{{- else if .emptyDir }}
emptyDir:
{{- toYaml .emptyDir | nindent 12 }}
{{- else }}
emptyDir: {}
{{- end }}
{{- end }}
@@ -0,0 +1,21 @@
{{- $envVars := include "impress.common.env" (list . .Values.blocknote) -}}
{{- $fullName := include "impress.blocknote.fullname" . -}}
{{- $component := "blocknote" -}}
apiVersion: v1
kind: Service
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
annotations:
{{- toYaml $.Values.blocknote.service.annotations | nindent 4 }}
spec:
type: {{ .Values.blocknote.service.type }}
ports:
- port: {{ .Values.blocknote.service.port }}
targetPort: {{ .Values.blocknote.service.targetPort }}
protocol: TCP
name: http
selector:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 4 }}
+90 -1
View File
@@ -412,4 +412,93 @@ yProvider:
extraVolumeMounts: []
## @param yProvider.extraVolumes Additional volumes to mount on the yProvider.
extraVolumes: []
extraVolumes: []
## @section blocknote
blocknote:
## @param blocknote.command Override the blocknote container command
command: []
## @param blocknote.args Override the blocknote container args
args: []
## @param blocknote.replicas Amount of blocknote replicas
replicas: 3
## @param blocknote.shareProcessNamespace Enable share process namespace between containers
shareProcessNamespace: false
## @param blocknote.sidecars Add sidecars containers to blocknote deployment
sidecars: []
## @param blocknote.securityContext Configure blocknote Pod security context
securityContext: null
## @param blocknote.envVars Configure blocknote container environment variables
## @extra blocknote.envVars.BY_VALUE Example environment variable by setting value directly
## @extra blocknote.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
## @extra blocknote.envVars.FROM_CONFIGMAP.configMapKeyRef.key Key within a ConfigMap when configuring env vars from a ConfigMap
## @extra blocknote.envVars.FROM_SECRET.secretKeyRef.name Name of a Secret when configuring env vars from a Secret
## @extra blocknote.envVars.FROM_SECRET.secretKeyRef.key Key within a Secret when configuring env vars from a Secret
## @skip blocknote.envVars
envVars:
<<: *commonEnvVars
## @param blocknote.podAnnotations Annotations to add to the blocknote Pod
podAnnotations: {}
## @param blocknote.service.type blocknote Service type
## @param blocknote.service.port blocknote Service listening port
## @param blocknote.service.targetPort blocknote container listening port
## @param blocknote.service.annotations Annotations to add to the blocknote Service
service:
type: ClusterIP
port: 80
targetPort: 8081
annotations: {}
## @param blocknote.probes.liveness.path [nullable] Configure path for blocknote HTTP liveness probe
## @param blocknote.probes.liveness.targetPort [nullable] Configure port for blocknote HTTP liveness probe
## @param blocknote.probes.liveness.initialDelaySeconds [nullable] Configure initial delay for blocknote liveness probe
## @param blocknote.probes.liveness.initialDelaySeconds [nullable] Configure timeout for blocknote liveness probe
## @param blocknote.probes.startup.path [nullable] Configure path for blocknote HTTP startup probe
## @param blocknote.probes.startup.targetPort [nullable] Configure port for blocknote HTTP startup probe
## @param blocknote.probes.startup.initialDelaySeconds [nullable] Configure initial delay for blocknote startup probe
## @param blocknote.probes.startup.initialDelaySeconds [nullable] Configure timeout for blocknote startup probe
## @param blocknote.probes.readiness.path [nullable] Configure path for blocknote HTTP readiness probe
## @param blocknote.probes.readiness.targetPort [nullable] Configure port for blocknote HTTP readiness probe
## @param blocknote.probes.readiness.initialDelaySeconds [nullable] Configure initial delay for blocknote readiness probe
## @param blocknote.probes.readiness.initialDelaySeconds [nullable] Configure timeout for blocknote readiness probe
probes:
liveness:
path: /__heartbeat__
initialDelaySeconds: 10
readiness:
path: /__lbheartbeat__
initialDelaySeconds: 10
## @param blocknote.resources Resource requirements for the blocknote container
resources: {}
## @param blocknote.nodeSelector Node selector for the blocknote Pod
nodeSelector: {}
## @param blocknote.tolerations Tolerations for the blocknote Pod
tolerations: []
## @param blocknote.affinity Affinity for the blocknote Pod
affinity: {}
## @param blocknote.persistence Additional volumes to create and mount on the blocknote. Used for debugging purposes
## @extra blocknote.persistence.volume-name.size Size of the additional volume
## @extra blocknote.persistence.volume-name.type Type of the additional volume, persistentVolumeClaim or emptyDir
## @extra blocknote.persistence.volume-name.mountPath Path where the volume should be mounted to
persistence: {}
## @param blocknote.extraVolumeMounts Additional volumes to mount on the blocknote.
extraVolumeMounts: []
## @param blocknote.extraVolumes Additional volumes to mount on the blocknote.
extraVolumes: []
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "1.5.1",
"version": "1.6.0",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {