Compare commits

..

916 Commits

Author SHA1 Message Date
Anthony LC
bbe17156be 🔖(minor) release 2.5.0
Added:
- 📝(doc) Added GNU Make link to README
- (frontend) add pinning on doc detail
- 🚩(frontend) feature flag analytic on copy as html
- (frontend) Custom block divider with export
- 🌐(i18n) activate dutch language

Changed:
- 🧑‍💻(frontend) change literal section open source
- ♻️(frontend) replace cors proxy for export
- 🚨(gitlint) Allow uppercase in commit messages

Fixed:
- 🐛(frontend) SVG export
- 🐛(frontend) remove scroll listener table content
- 🔒️(back) restrict access to favorite_list endpoint
- 🐛(backend) refactor to fix filtering on children
    and descendants views
- 🐛(action) fix notify-argocd workflow
- 🚨(helm) fix helmfile lint
- 🚚(frontend) redirect to 401 page when 401 error
2025-03-19 14:11:47 +01:00
Anthony LC
51cc26b916 🐛(frontend) improve svg export to be less pixelized
Some SVGs were pixelized in the exported files.
We now add the wanted size to the svg conversion to
make sure the images are exported with the correct size
and so less pixelized.
2025-03-19 14:11:47 +01:00
Anthony LC
cab8ef51df 🐛(frontend) unmount components Analytics
`useAnalytics` hooks was dispatching methods that
caused children components to be unmounted.
By declaring the methods out of the hook, we can
prevent the components from being unmounted.
2025-03-18 14:53:09 +01:00
Anthony LC
6627518017 🚚(frontend) redirect to 401 page when 401 error
Users could still be able to edit a document if the
session was expired. It could give the feeling that the
document was not saved.
If during a mutation request (POST, PUT, DELETE),
the server returns a 401 error,
the user is redirected to the 401 page.
2025-03-18 14:53:09 +01:00
Pedro Manse
12c18bc4e9 📝(README) Add link to GNU Make
Just like docker-compose, create link to the software's site on it's
first mention.

📝(Changelog) Added entry

📝(Changelog) Added pull request id
2025-03-18 11:07:22 +01:00
Anthony LC
aff330eb5b 🚨(gitlint) Allow uppercase in commit messages
Many developers use uppercase as the first letter
in their commit messages, it creates an error.
We will allow uppercase in commit messages to
lower frustration when committing.
2025-03-18 10:24:08 +01:00
Cameron King
bcdaedba9b 🐛(backend) add user/db to pg healthchecks
Adds PostgreSQL user and database names to the docker-compose.yaml healthchecks.
This resolves an error that appears in the logs, where 'root' is used by
default.
2025-03-18 09:41:27 +01:00
Manuel Raynaud
799814e3e3 🌐(i18n) activate dutch language
All the dutch translations are complete on crowdin. We activate it in
the django settings and download all translations from crowdin
2025-03-18 09:27:13 +01:00
virgile-dev
02c9b2ea2e 🐛(readme) fix preprod link to redirect to homepage (#747)
The current link redirects to a 404. New link redirect to homepage.
2025-03-17 16:02:45 +00:00
Manuel Raynaud
eb23aefd55 ♻️(back) use same base route path for swagger
Swaggers urls where not using the same base route path /api/v1.0, we
prepend it to have the same path everywhere. Moreover, a double slash
was used for swagger and redoc dashboard.
2025-03-17 15:02:34 +01:00
Manuel Raynaud
0c49019490 🚨(helm) fix helmfile lint
Latest release of helmfile is applying the change related before as a
warning. Environnements must be before releases but not in the same
document of repositories.
2025-03-17 14:40:55 +01:00
Anthony LC
170dbe07bb ⬆️(frontend) bump @babel/runtime /src/frontend
Bumps @babel/runtime from 7.26.7 to 7.26.10.
2025-03-17 13:50:20 +01:00
Manuel Raynaud
70136f2415 🐛(action) fix notify-argocd workflow
The notify-argocd workflow was not working correctly. The html_url sent
to argocd was not the good one anymore.
2025-03-17 12:09:18 +01:00
Anthony LC
2a8fc97f2f ⬆️(frontend) bump @babel/helpers in /src/frontend
Bumps @babel/helpers from 7.26.7 to 7.26.10.
2025-03-17 11:50:22 +01:00
Anthony LC
9570701bc3 ⬆️(frontend) bump @babel/runtime /src/mail
Bumps @babel/runtime from 7.26.0 to 7.26.10.
2025-03-17 11:36:04 +01:00
Anthony LC
4b28b3c23b (frontend) add pinning on doc detail
Add pinning button on doc detail page.
2025-03-17 11:16:50 +01:00
Anthony LC
f26fc43df0 🔥(frontend) remove DocTagPublic component
DocTagPublic component was removed because
it was not used.
2025-03-17 11:16:50 +01:00
Anthony LC
05a6818439 🧑‍💻(e2e) display more information when auth fails
When the auth fails, it was quite obscure to
understand what was going on.
We now take a screenshot of the page and display
the console logs.
2025-03-17 09:30:19 +01:00
Anthony LC
8056fd7d66 🚚(frontend) add import path for features/docs tsconfig
To simplify imports, we add the import path @/docs
to target ./src/features/docs/.
We changed all imports to use this path.
2025-03-17 09:30:19 +01:00
Samuel Paccoud - DINUM
c85224af42 📝(README) remove AGPL mention
This mention was confusing. We are only using min.io for development
purposes and this has nothing to do with the project's licence.
2025-03-16 19:00:14 +01:00
Anthony LC
70f1b6a8e8 🚩(frontend) add feature flag on "Copy as HTML"
As a blue print, we add a feature flag on
"Copy as HTML" button in the doc toolbox.
This feature flag is controlled by the `CopyAsHtml`
feature flag.

Be aware:
- if the feature flag is disabled, the button
will be shown
- if the feature flag is enabled and send true,
the button will be shown
- if the feature flag is enabled and send false,
the button will be hidden
2025-03-14 16:26:12 +01:00
Anthony LC
0f07fdcb65 📈(frontend) get user analytics
We will identify users by their email address.
This will help us understand how users interact
with the platform and improve the user experience.
2025-03-14 16:26:12 +01:00
Anthony LC
2e13dfb9bc 📈(frontend) abstract analytics classes
Add abstract classes for analytics services.
We will be able to add easily any analytic
services.
Our first analytic service usecase is Posthog.
2025-03-14 16:26:12 +01:00
renovate[bot]
a026435eb7 ⬆️(dependencies) update canvg to v4.0.3 [SECURITY] 2025-03-14 15:29:27 +01:00
Anthony LC
7007d56c38 🐛(frontend) fix svg not rendering export dox
The svg was not rendering in the dox export.
We overwrite the default mapping to convert the
svg to png before rendering.
The images could be out of the page as well,
we fixed this issue by adding a maxWidth to the image.
2025-03-14 15:13:51 +01:00
Anthony LC
0405e6a3f6 🐛(frontend) fix svg not rendering export pdf
The svg was not rendering in the pdf export.
We overwrite the default mapping to convert the
svg to png before rendering.
The images could be out of the page as well,
we fixed this issue by adding a maxWidth to the image.
2025-03-14 15:13:51 +01:00
Anthony LC
cb8bd4b937 🐛(frontend) fix flakiness e2e tests title doc
- fix multiple states title
- wait for stabilize network after create
- fix test other chromium browsers
- improve grid delete test
2025-03-14 14:49:17 +01:00
Anthony LC
4316b4e67d (frontend) adapt export to divider block
We have a new block type, the divider block.
We have to adapt the export to handle this
new block type.
2025-03-14 10:39:07 +01:00
Anthony LC
534085439f (frontend) add divider blocks to the editor
Add a custom block to add a divider in the editor.
2025-03-14 10:39:07 +01:00
Anthony LC
da02d3d756 🎨(frontend) use blockquote tag for quote block
Use the blockquote tag for quote block instead of
a paragraph tag.
2025-03-13 16:38:33 +01:00
Anthony LC
87960d3773 (AI) add emojify action to ai transform
The emojify action add emojis to the important
parts of the text.
2025-03-13 16:38:33 +01:00
Anthony LC
e0af6d36e1 (AI) add beautify action to ai transform
The beautify action add emojis to the important
parts of the text and add formatting to the text
to make it more readable.
2025-03-13 16:38:33 +01:00
Anthony LC
cbf9091d1c ️(AI) improve formating of ai translation
The ai translation were quite lossy about formatting.
Colors, background, breaklines, table sizes were
lost in the translation.
We improve the AI translation request to keep
the formatting as close as possible by using
html instead of markdown.
2025-03-13 16:38:33 +01:00
Anthony LC
9176328200 ♻️(frontend) replace cors proxy for export
We were using the cors proxy of Blocknote.js
to export the document. Now we use our own proxy
to avoid CORS issues.
2025-03-13 12:39:32 +01:00
Anthony LC
6efc2377fe (back) create a cors proxy fetching docs external resources
When exporting a document in PDF and if the doc contains external
resources, we want to fetch them using a proxy bypassing CORS
restrictions. To ensure this endpoint is not used for something else
than fetching urls contains in the doc, we use access control and check
if the url really exists in the document.
2025-03-13 12:39:32 +01:00
Anthony LC
1c02b0ad8e 📝(frontend) change literal section open source
Change literal section open source.
2025-03-12 09:53:01 +01:00
Manuel Raynaud
007854a877 ️(back) use redis as session backend in developement
We want to persist the session during development. Otherwise the session
is reset everytime the server is restart. This behavior make developing
bot a front and back feature a nigthmare, we spend our time login again
and again
2025-03-12 08:28:20 +01:00
Anthony LC
57cead448d 🏷️(frontend) adapt new doc type nb_accesses_direct
We renamed the `nb_accesses` field to `nb_accesses_direct`
and added a new `nb_accesses_ancestors` field.
We adapt the frontend to use the new fields.
2025-03-11 09:32:48 +01:00
Samuel Paccoud - DINUM
f20d256cd1 🐛(backend) fix numchild when soft deleting/restoring a document
The numchild attribute must be incremented/decremented manually
when we soft delete a document if we want it to remain accurate,
which is important to display the tree structure in the frontend.
2025-03-11 09:32:48 +01:00
Samuel Paccoud - DINUM
76c01df3ae (backend) add number of direct accesses related to a document
The "nb_accesses" field was displaying the number of access instances
related to a document or any of its ancestors. Some features on the
frontend require to know how many of these access instances are related
to the document directly.
2025-03-11 09:32:48 +01:00
Samuel Paccoud - DINUM
20315e9b60 (backend) limit link reach/role select options depending on ancestors
If a document already gets a link reach/role inheriting from one of its
ancestors, we should not propose setting link reach/role on the
document that would be more restrictive than what we inherited from
ancestors.
2025-03-11 09:32:48 +01:00
Samuel Paccoud - DINUM
2203d49a52 (backend) add new "descendants" action to document API endpoint
We want to be able to make a search query inside a hierchical document.
It's elegant to do it as a document detail action so that we benefit
from access control.
2025-03-11 09:32:48 +01:00
Samuel Paccoud - DINUM
56aa69f56a ♻️(backend) refactor list view to allow filtering other views
the "filter_queryset" method is called in the middle of the
"get_object" method. We use the "get_object" in actions like
"children", "tree", etc. which start by calling "get_object"
but return lists of documents.

We would like to apply filters to these views but the it didn't
work because the "get_object" method was also impacted by the
filters...

In a future PR, we should take control of the "get_object" method
and decouple all this. We need a quick solution to allow releasing
the hierchical documents feature in the frontend.
2025-03-11 09:32:48 +01:00
Samuel Paccoud - DINUM
0aabf26694 (backend) add "tree" action on document API endpoint
We want to display the tree structure to which a document belongs
on the left side panel of its detail view. For this, we need an
endpoint to retrieve the list view of the document's ancestors
opened.

By opened, we mean that when display the document, we also need to
display its siblings. When displaying the parent of the current
document, we also need to display the siblings of the parent...
2025-03-11 09:32:48 +01:00
Samuel Paccoud - DINUM
fcf8b38021 (backend) allow forcing page size within limits
We want to be able to increase the page size with by passing
the query string parameter "page_size".
2025-03-11 09:32:48 +01:00
renovate[bot]
757d7f35cd ⬆️(dependencies) update django to v5.1.7 [SECURITY] 2025-03-10 10:19:56 +01:00
Anthony LC
fdc49dc002 🐛(frontend) remove scroll listener table content
During a useEffect cleaning, the selector was
not the correct one. The debounce was not being
removed correctly neither.
2025-03-10 10:06:59 +01:00
Anthony LC
197ba47f73 ⬆️(frontend) bump cunningham to 3.0.0
Last bump to react 19 was a breaking change with
the previous version of Cunnigham, so we need to
update cunningham to 3.0.0 to be compatible with it.
We can now remove Cunnigham from the list of ignored
dependencies in the renovate.json file.
2025-03-10 09:26:19 +01:00
Anthony LC
d5997ba9d5 ⬆️(frontend) bump to react 19.0.0
Last version of Blocknotes is compatible with
React 19.0.0, it seems even necessary to
bump the version of React to 19.0.0.
We bump the version of React to 19.0.0 and
remove the react packages from renovate
list of ignored dependencies.
2025-03-10 09:26:19 +01:00
Anthony LC
1c6d18fdf3 📌(frontend) pin yjs globally
We had a warning about yjs multiple versions
between dependencies. We pinned yjs globally
to avoid this warning and potential side effects.
2025-03-10 09:26:19 +01:00
Anthony LC
24d126f410 🚨(frontend) fix linter
- fix linter
- remove unnecessary style files
2025-03-10 09:26:19 +01:00
renovate[bot]
a5e1751cf3 ⬆️(dependencies) update js dependencies 2025-03-10 09:26:19 +01:00
Manuel Raynaud
0cabb655ad 🔒️(back) restrict access to favorite_list endpoint
favorite_list endpoint is accessible to anonymous user. This lead to an
error 500. This endpoint should be accessible only to authenticated
users.
2025-03-07 09:04:44 +01:00
Manuel Raynaud
38eb6d45b7 🐛(back) fix ValidationError exception handler
The exception handler for the ValidationError was not testing correctly
the existence of some attributes like `message_dict`.
2025-03-07 09:04:44 +01:00
Anthony LC
5bb7ad643a 🔖(minor) release 2.4.0
Added:
- (frontend) synchronize language-choice

Changed:
- Use sentry tags instead of extra scope

Fixed:
- 🐛(frontend) fix collaboration error
2025-03-06 15:59:34 +01:00
Anthony LC
57b8881fc6 📌(frontend) pin blocknote globally
Blocknote does not pinned the version.
We get bumped version instead of the version we want.
We pin the version of blocknote globally to
avoid this issue.
2025-03-06 15:09:17 +01:00
Anthony LC
89ad610ba6 🐛(frontend) fix collaboration error
We upgrade blocknote to 0.23.2-hotfix.0,
it includes a fix with the collaboration.
2025-03-06 15:09:17 +01:00
rvveber
251787b835 🚨(tests) add language related tests; fix getByRole not working in tests
- adds tests and test-utility for solid language switching in tests
- fixes where ...getByRole(menuitem... would not return a valid object
2025-03-05 14:29:24 +01:00
rvveber
f95173e096 🐛(frontend) allow left panel to update on language change
- fixes a bug where after language-sync the left panel would remain
  in the same language as before.
2025-03-05 14:29:24 +01:00
rvveber
a7944cce80 (app) get language from backend; set browser-detected language if null
- adds useLanguageSynchronizer hook to update the:
  1. frontend-language to the user-preference - if there is one.
  2. user-preference to the (browser-detected) frontend-language - otherwise.
2025-03-05 14:29:24 +01:00
rvveber
7941fc91d5 🐛(frontend) set toolbar-popup to current language
- ensure editor is translated to i18n.resolvedLanguage => en
  as i18n.language could hold more accurate locale => en-GB etc..
2025-03-05 14:29:24 +01:00
rvveber
7fc83a4fcd (frontend) the LanguagePicker now uses config as options
- config endpoint languages are used as available options for LanguagePicker
- updating the language from it, triggers an update on the user via API
2025-03-05 14:29:24 +01:00
rvveber
2bf47b7705 (backend) the LanguagePicker now uses config as options
- config endpoint languages are used as available options for LanguagePicker
- updating the language from it, triggers an update on the user via API
2025-03-05 14:29:24 +01:00
rvveber
23b0214a2a (frontend) add language utility for "locale"
- Adds a helper for working with locales
- More details in their annotations
- Unnecessary, if in the future, the backend uses
  the same locales as the keys in the translations (ISO 639-1)
2025-03-05 14:29:24 +01:00
rvveber
f244509de3 (frontend) add API access for 'language' attribute on User model
- allow the language attribute on the user to be updated via API
- add frontend function to update the user language via API
- extend defaults on the test users, to have fixed language in E2E tests
- extend types and variables using the types with the new field
2025-03-05 14:29:24 +01:00
rvveber
fda5f8f008 (backend) add API access for 'language' attribute on User model
- allow the language attribute on the user to be updated via API
- add frontend function to update the user language via API
- extend defaults on the test users, to have fixed language in E2E tests
- extend types and variables using the types with the new field
2025-03-05 14:29:24 +01:00
rvveber
9a79b09b07 🔨(backend) make the 'language' attribute on the User model nullable
- allow the language on the user to be unset
- set the default language to be unset
- helps us determine that the user has yet to set a language preference
2025-03-05 14:29:24 +01:00
rvveber
b24acd14e2 🔨(frontend) email invitation in invited user's language
- language for invitation emails => language saved on the invited user
- if invited user does not exist yet => language of the sending user
- if for some reason no sending user => system default language
2025-03-05 14:29:24 +01:00
rvveber
1531846115 🔨(backend) email invitation in invited user's language
- language for invitation emails => language saved on the invited user
- if invited user does not exist yet => language of the sending user
- if for some reason no sending user => system default language
2025-03-05 14:29:24 +01:00
Manuel Raynaud
ebf6d46e37 ♻️(front) use sentry tags instead of extra scope
To ease filtering issues on sentry, we want to use tags instead of extra
scope. Tags are indexed and searchable, it's not the case with extra
scope. Moreover using setEtra to add additional data is deprecated.
2025-03-05 10:26:23 +01:00
Manuel Raynaud
b9b5f86cf4 ️(back) restrict documents to restore using only the queryset
To determine the descendant to restore or not, we were looking building
a complex exclude clause. This can be simplify focusing only on data we
already have without making an extra query to fetch the list of
descendant to exclude.
2025-03-04 18:03:18 +01:00
renovate[bot]
56412b0be5 ⬆️(dependencies) update python dependencies 2025-03-04 14:24:58 +01:00
Anthony LC
af052cd06b 🔖(minor) release 2.3.0
Added:
- 💄(frontend) add error pages
- 🔒️ Manage unsafe attachments
- (frontend) Custom block quote with export
- (frontend) add open source section homepage

Changed:
- 🛂(frontend) Restore version visibility
- 📝(doc) minor README.md formatting and wording enhancements
- ♻️Stop setting a default title on doc creation
- ♻️(frontend) misc ui improvements

Fixed:
- 🐛(backend) allow any type of extensions for media download
- ♻️(frontend) improve table pdf rendering
2025-03-04 12:12:57 +01:00
Anthony LC
8927635c5f 💄(frontend) hide Crisp when mobile and modal
The Crisp button was hidding buttons on mobile
when a modal was open. This commit hides the
Crisp button when a modal is open on mobile.
2025-03-04 12:12:57 +01:00
Anthony LC
76bce4313b 🩹(frontend) fine tuning 2.3.0
- improve medium button style when 2 lines
- improve design on Firefox input title
- manage title modal without doc title
- improve redirect when 401
2025-03-04 12:12:57 +01:00
Anthony LC
5ac71bfac1 🐛(service-worker) update sw to create a doc without body
Offline creation of a doc was broken because we
don't add a default title anymore when we create a
doc, leading to POST requests without body.
we need to adapt the service worker to handle this
case.
2025-03-04 12:12:57 +01:00
Anthony LC
cb4e148afc ♻️(email) adapt email when no title
Default title is not set when we create a document
anymore. We need to adapt the email to handle
this case.
2025-03-04 12:12:57 +01:00
AntoLC
2d24825be0 🌐(i18n) update translated strings
Update translated files with new translations
2025-03-04 12:12:57 +01:00
Anthony LC
7b1ddc0e05 🛂(backend) remove svg from unsafe
We added content-security-policy on nginx.
It should be safe to allow svg files now.
We remove the svg file from the unsafe
attachments list. We adapt the tests accordingly.
2025-03-03 13:18:40 +01:00
Manuel Raynaud
22a665e535 🔒️(nginx) manage Content-Security-Policy in nginx config
The media route is managed by nginx. On this route we want to add the
Content-Security-Header to forbid fetching any resources.
See : https://content-security-policy.com/
2025-03-03 13:18:40 +01:00
Manuel Raynaud
a22bf95bce 🔒️(back) set ContentDisposition on media upload
On the media upload endpoint, we want to set the content-disposition
header. Its value is based on the uploaded file mime-type and if flagged
as unsafe. If the file is not an image or is unsafe then the
contentDisposition is set to attachment to force its download.
Otherwise, we set it to inline.
2025-03-03 13:18:40 +01:00
Anthony LC
3ce1826355 🚚(frontend) toolbar components in BlockNoteToolBar folder
We moved the toolbar components in BlockNoteToolBar
folder.
2025-03-03 13:18:40 +01:00
Anthony LC
d099d58f77 🛂(frontend) secure download button
Blocknote download button opens the file in a new
tab, which could be not secure because of XSS attacks.
We replace the download button with a new one that
downloads the file instead of opening it in a new tab.
Some files are flags as unsafe (SVG / js / exe),
for these files we add a confirmation modal before
downloading the file to prevent the user from
downloading a file that could be harmful.
In the future, we could add other security layers
from this model, to analyze the file before
downloading it by example.
2025-03-03 13:18:40 +01:00
Anthony LC
ebd49f05a8 🚸(frontend) block click on unsafe image
We want to prevent the user to open unsafe images
in the browser. We blocked the click on the images.
To download them, the user will have to use the
download button.
2025-03-03 13:18:40 +01:00
Anthony LC
315c2c2c43 🐛(frontend) improve authenticated state
It can happen that the user is authenticated
then the token is expired. The authenticated
state should be updated to false in this case.
2025-03-03 13:18:40 +01:00
Anthony LC
e442908c50 💄(frontend) improve the design of the alert error
Since the new design implementation,
the alert error was not looking good.
This commit improves the design of the alert error.
2025-03-03 13:18:40 +01:00
Anthony LC
6672292d93 🛂(backend) add unsafe in the attachments filename
The frontend cannot access custom headers of a file,
so we need to add a flag in the filename.
We add the `unsafe` flag in the filename to
indicate that the file is unsafe.

Previous filename: "/{UUID4}.{extension}"
New filename: "/{UUID4}-unsafe.{extension}"
2025-03-03 13:18:40 +01:00
Manuel Raynaud
7dda74421f ♻️(back) extract ancestor deleted_at directly from db in restore method
In the restore method, all the ancestors with a deleted_at date set are
extracted from the database and then the oldest value is extracted using
the min python function. This usage of min can be removed by sorting
directly the deleted_at at the databse level and then fetching the first
one. It's faster and easier to maintain.
2025-03-03 13:05:36 +01:00
Anthony LC
9c25b684e3 (frontend) add open source section homepage
We decided to add the open source section on
the homepage of Docs.
2025-03-03 12:42:18 +01:00
Anthony LC
cd5ee3fb7c (frontend) adapt export to quote block
We have a new block type, the quote block.
We have to adapt the export to handle this
new block type.
2025-03-03 12:27:02 +01:00
Anthony LC
942c0f059c 🏗️(frontend) blockMapping refactoring
As made for TablePDF, we separate the block mapping
in separate files. This will allow us to have
a better separation of concerns and to have
a more maintainable codebase.
We improve as well the typing. It will be easier
to add new blocks in the future.
2025-03-03 12:27:02 +01:00
Anthony LC
3acee1e6fa (frontend) create feature doc-export
Create the feature doc-export, it will be
responsible for exporting the document.
2025-03-03 12:27:02 +01:00
Anthony LC
26ea32bd0b 🏷️(frontend) adapt title types
We recently changed the default title behavior.
It can now be undefined, we have to change the
types accordingly.
2025-03-03 12:27:02 +01:00
Anthony LC
7f6ffa0123 (frontend) add quote blocks to the editor
Add a custom block to quote in the editor.
2025-03-03 12:27:02 +01:00
Samuel Paccoud - DINUM
ef2127585c 🐛(backend) allow any type of extensions for media download
The regex to validate media file extensions was too restrictive.
2025-03-03 11:21:41 +01:00
Anthony LC
54a75bc338 ♻️(frontend) update some elements
- add panel information when document is
authenticated
- add a copy link button in the toolbox
on the document
- fix when long title document
- modals fit design
- mobile responsive changes
2025-02-24 10:49:20 +01:00
Anthony LC
50d098c777 💄(frontend) adapt language picker design
Adapt the language picker design to
to fit with the new design of the app.
2025-02-24 10:49:20 +01:00
Anthony LC
757c09b189 ♻️(frontend) adapt other error pages to new design
Adapt the other error pages to the new design.
2025-02-24 10:27:42 +01:00
Anthony LC
30c5cfab62 💄(frontend) add page 401
Add a 401 page when user try to access a
doc that need authentication.
2025-02-24 10:27:42 +01:00
Anthony LC
f069329e18 💄(frontend) add page 403
Add a page and integrate the design for the
403 error page.
2025-02-24 10:27:42 +01:00
Manuel Raynaud
ef8ee67553 ♻️(compose) set compose name in the docker-compose file
The helper bin/compose was using the option -p to set the compose
project name but this option is not used in the Makefile. This can lead
to different way to use the docker compose file definition with
different project name. In order to have a consistent name everywhere
and for everybody, we set the name in the docker compose file itself.
2025-02-20 10:32:37 +01:00
Anthony LC
ad47fc2d60 (e2e) global setup authentication
Because of the parallelism of the tests,
the authentication setup was flaky. Sometimes
the tests would run before the authentication
was complete.
We change to a global setup instead of the
project dependency setup, it should be more
reliable.
We improved the waiting states of the authentication
setup.
2025-02-19 14:57:15 +01:00
Nathan Vasse
009f5d6ed4 ♻️(front) improve table pdf rendering
The previous way of rendering table was causing issues when tables
could not fit on one page. I then came accross this discussion
https://github.com/diegomura/react-pdf/issues/2343. The author
created a lib to improve the rendering of table, it's better, but
still not perfect maybe.
2025-02-19 13:58:29 +01:00
Nathan Vasse
64d0072c8d 🐛(front) improve text rendering in pdf
The rendered text had unwanted line breaks in middle of them.
It was because we were not using the appropriate Text component, the
one to be used in the one from react-pdf.
2025-02-18 17:01:58 +01:00
Anthony LC
aefbc2e0b9 🛂(frontend) hide version restore button when reader
When you are a reader member, you have the right to
see the version but you cannot restore
them. So, we hide the restore button
when you are a reader.
2025-02-18 09:30:37 +01:00
Anthony LC
15dc1e3012 🦺(migration) add back the migration folders to linter
Previous commit add "core/tests/migrations".
The linter could not pass on it because all the
migration folders were excluded from the linter.
We remove this exclusion, tests and migrations can
now be linted and formatted automatically.
2025-02-15 23:39:17 +01:00
Anthony LC
6cc20aeacb 🩹(migration) add migration to update default titles
The frontend was setting a default titles for
documents with empty titles.
This migration updates the document table to set
the title to null instead of the default title.
We add a test to ensure that the migration
works as expected.
2025-02-15 23:39:17 +01:00
Anthony LC
7da7214afb (backend) add django-test-migrations
Add django-test-migrations to the project.
It is a tool that helps to test Django migrations.
2025-02-15 23:39:17 +01:00
Anthony LC
c369419512 ♻️(frontend) stop setting a default title on doc creation
We were setting a default title to our document
during creation, but we should not do that,
it created lot of similar titles, lot of
documents will show up during search.
2025-02-15 23:39:17 +01:00
Anthony LC
d9ad397c94 🩹(frontend) minor fixes
- fix linter warning on one e2e test
- improve logo svg
- improve cursor
- improve grid loader
2025-02-15 23:39:17 +01:00
Manuel Raynaud
3191d890f3 ♻️(docker) rename frontend-dev service in frontend
The frontend-dev service is in fact using the production image. We
rename it in frontend accordingly with what it really does. We also have
to change name rules in Makefile to be consistent.
2025-02-14 19:54:38 +01:00
Manuel Raynaud
68f3387539 ♻️(make) make run command starting everything
The run command is not starting the frontend application. We change the
run commands. The run command is strating everything. The run-backend
command is starting all services needed to use the backend application.
2025-02-14 19:54:38 +01:00
Manuel Raynaud
0dc8b4556c ♻️(docker) remove usage of dockerize
We remove dockerize and use healthcheck on docker compose services
instead.
2025-02-14 19:54:38 +01:00
Manuel Raynaud
e123e91959 🐛(nginx) increase nginx buffer size when proxifying keycloak
Nginx is used to proxify keycloak in our development configuration. When
a new user is created keycloak is send a large amount of headers in its
response and the default nginx config is not enough to handle this
amount of headers. We have to increase the proxy buffer size to handle
them.
2025-02-14 19:54:38 +01:00
Bastien Guerry
2709400773 🔊(changelog) add a changelog entry
Add "📝(doc) minor README.md formatting and wording enhancements" in
the unreleased section.
2025-02-14 17:39:52 +01:00
Bastien Guerry
8281c6159b 📝(doc) minor README.md formatting and wording enhancements
See https://github.com/suitenumerique/docs/issues/622
2025-02-14 17:39:52 +01:00
Anthony LC
296dbb7957 🔖(minor) release 2.2.0
Added:
- 📝(doc) Add security.md and codeofconduct.md
- (frontend) add home page
- (frontend) cursor display on activity
- (frontend) Add export page break

Changed:
- 🔧(backend) make AI feature reach configurable

Fixed:
- 🌐(CI) Fix email partially translated
- 🐛(frontend) fix cursor breakline
- 🐛(frontend) fix style pdf export
2025-02-11 14:16:58 +01:00
Anthony LC
3827f0f799 🩹(frontend) fine tuning v2.2.0
Fix minor issues:
- Add some height to the carret in the editor
- Improve css in mail templates
- Improve images resolution on homepage
2025-02-11 14:16:58 +01:00
Anthony LC
d89e3dc6d4 🛂(frontend) display the AI buttons depend abilities
Anybody with edit right could use the AI.
We changed this behavior, now we have to be
authentified with edit right.
We update the UI to display the AI buttons
only if the user has the correct AI ability.
2025-02-11 10:54:55 +01:00
Samuel Paccoud - DINUM
91cf5f9367 🔧(backend) make AI feature reach configurable
We want to be able to define whether AI features are available to
anonymous users who gained editor access on a document, or if we
demand that they be authenticated or even if we demand that they
gained their editor access via a specific document access.

Being authenticated is now the default value. This will change the
default behavior on your existing instance (see UPGRADE.md)
2025-02-11 10:54:55 +01:00
Anthony LC
5cc4b07cf6 💄(frontend) improve caret style
Improve the caret, to looks more like the
google doc caret.
2025-02-10 15:53:03 +01:00
Anthony LC
0cfc242e09 🚚(frontend) move Blocknote styles
Move Blocknote styles to a separate file.
2025-02-10 15:53:03 +01:00
Anthony LC
a6b3cfdb0c (frontend) add export page break
Blocknotejs introduced the ability to export a
document with page breaks.
This commit adds the page break feature to the
editor and so to our export feature.
2025-02-10 15:53:03 +01:00
Anthony LC
5ead18c94c 💬(frontend) add lacking buttons open source section
Some buttons were lacking in the open source
section of the home page. This commit adds them.
2025-02-10 15:40:23 +01:00
AntoLC
5eeb8cae5c 🌐(i18n) update translated strings
Update translated files with new translations
2025-02-10 15:40:23 +01:00
Jacques ROUSSEL
68bf024005 (helm) add pdbs to deployments
In order to avoid a service interruption during a Kubernetes (k8s)
upgrade, we add a Pod Disruption Budget (PDB) to deployments.
2025-02-10 13:05:54 +01:00
Anthony LC
fdd1068c90 (frontend) fix test copy html
The recent upgrade of Blocknote has caused the
test to fail.
This commit updates the test to reflect
the new html structure.
2025-02-10 10:50:29 +01:00
Anthony LC
ba695bf647 ⬇️(frontend) downgrade to @react-pdf/renderer to 4.1.6
The version 4.2.1 of @react-pdf/renderer
is not compatible @blocknote/xl-pdf-exporter
and @blocknote/xl-docx-exporter.
2025-02-10 10:50:29 +01:00
Anthony LC
27e7aec193 💄(frontend) improve styles export pdf
When exporting a document to PDF, the headings
spacings were too small, the break lines were
not displayed. This commit fixes these issues
by replacing the needed blocks.
2025-02-10 10:50:29 +01:00
Anthony LC
58b712a1de 🐛(frontend) fix cursor breakline
We had breakline issues with the initial
cursor because of some css properties.
We changed the cursor css to not take
any space in the lines,
avoiding the breakline issues.
We keep the new cursor visibility
feature (always, activity).
2025-02-10 10:50:29 +01:00
renovate[bot]
08f9036523 ⬆️(dependencies) update js dependencies 2025-02-10 10:50:29 +01:00
Anthony LC
ebe3efc8f7 💄(frontend) update the favicon
Update the favicon with a better one.
2025-02-07 18:18:22 +01:00
Anthony LC
66fbf27913 🩹(frontend) fix PageLayout
The page layout was rendered behind the header,
which caused the top of the mention legales pages
to be hidden.
This commit fixes this issue.
2025-02-07 18:18:22 +01:00
Anthony LC
20e4a4e42a 🥚(frontend) easter egg in dev tools
Add a easter egg in the browser dev tools.
2025-02-07 18:18:22 +01:00
Anthony LC
1aa4844eeb 🎨(frontend) add dsfr proconnect homepage
If we are with the DSFR theme, we need to add the
proconnect button to the homepage.
We add an option in the cunningham theme to
display the proconnect section instead of the
opensource section.
2025-02-07 18:18:22 +01:00
Nathan Panchout
4bb9c092cb (frontend) add white label homepage
Add white label homepage with new assets and
components. When the user is not logged in,
the homepage will be displayed.
2025-02-07 18:18:22 +01:00
Anthony LC
c493eb8924 ♻️(frontend) use a hook instead of a store for auth
We will use a hook instead of a store for the auth
feature. The hook will be powered by ReactQuery,
it will provide us fine-grained control over the
auth state and will be easier to use.
2025-02-07 18:18:22 +01:00
Anthony LC
40fdf97520 🚚(frontend) move auth to its own feature
We will move auth to its own feature to make it
easier to manage and to make it more modular.
2025-02-07 18:18:22 +01:00
Anthony LC
91b10e75dd 💄(email) fix line height email title
The line height of the email title was not
the correct size. We let the title managing
its own line height.
2025-02-07 17:05:49 +01:00
Anthony LC
7a6da10e1c 🐛(i18n) add back the missing email translations
We changed the way we upload the translations to
Crowdin, some translations were missing for the
email templates. We add them back and improve
the tests to make sure we don't forget them again.
2025-02-07 17:05:49 +01:00
Anthony LC
004e8ec645 🌐(CI) build mails when crowdin_upload workflow
When we were executing the crowdin_upload workflow,
we were not building the mail template to dispatch it
to the backend. It resulted in the mail not being
totally translated. This commit fixes that issue
by adding the build mail step to the crowdin_upload.
To do so, we added it to the dependencies workflow.
"dependencies" workflow is callable by other
workflows that need a specific job.
2025-02-07 17:05:49 +01:00
virgile-deville
a1bca9c436 📝(doc) add security.md and conduct.md policies
We need a safe way for people to report vulnerabilities.
People now can go on SECURITY.md and follow our policy.

We want to have a policy for expected behaviour.
People can check out CODE_OF_CONDUCT.md.
2025-02-02 14:53:29 +01:00
Sylvain Zimmer
d02fa1ddd4 ✏️(readme) fix a few typos
Found a few errors after proof-reading the README
2025-01-30 19:00:56 +01:00
Anthony LC
1fd66d3081 🔖(minor) release 2.1.0
Added:
- (backend) add soft delete and restore API endpoints to documents
- (backend) allow organizing documents in a tree structure
- (backend) add "excerpt" field to document list serializer
- (backend) add github actions to manage Crowdin workflow
- 📈Integrate Posthog
- 🏷️(backend) add content-type to uploaded file
- (frontend) export pdf docx front side7

Changed:
- 💄(frontend) add abilities on doc row
- 💄(frontend) improve DocsGridItem responsive padding
- 🔧(backend) Bump maximum page size to 200
- 📝(doc) Improve Read me

Fixed:
- 🐛Fix invitations

Removed:
- 🔥(backend) remove "content" field from list serializer
2025-01-30 12:50:44 +01:00
AntoLC
bae8c4c563 🌐(i18n) update translated strings
Update translated files with new translations
2025-01-30 11:37:05 +01:00
Anthony LC
0dae35dab1 ✏️(frontend) replace Word / Open Office by Docx
Replace the naming of the select export
options from Word / Open Office to Docx.
2025-01-30 11:37:05 +01:00
Anthony LC
929a50b573 ⬇️(frontend) downgrade cunningham to 2.9.4
The last version of Cunningham has problems.
Better to downgrade to the previous version.
We add cunningham library to renovate.json
to prevent future upgrade with Renovate.
An issue will be open to upgrade to the
last version manually.
2025-01-29 17:45:14 +01:00
Anthony LC
83dfd26d1c ⬇️(frontend) downgrade to react 18
We still have conflict with React 19, better to
downgrade to react 18 for the moment.
We add the react 18 libs to renovate.json
to prevent future upgrade with Renovate.
An issue will be open to upgrade to React 19
manually.
2025-01-29 17:45:14 +01:00
renovate[bot]
addc6a331f ⬆️(dependencies) update js dependencies 2025-01-29 17:45:14 +01:00
Samuel Paccoud - DINUM
5c5763a0ef 🗃️(backend) make document/template creation atomic
When creating a new document/template via the API, we add the
logged-in user as owner of the created object. This should be
done atomically with the object creation to make sure we don't
end-up with an orphan object that the creator can't access
anymore.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
5042f4ca47 ♻️(backend) generalize use of SerializerPerActionMixin in viewsets
We improve the serializer and generalize its use for clarity.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
8c247c8777 🔧(backend) bump maximum page size to 200
The new UI with infinite scroll calls for longer pages which we
are able to produce thanks to the many optimizations on the list
view.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
239342fbbd (backend) add API endpoint action to restore a soft deleted document
Only owners can see and restore deleted documents. They can only do
it during the grace period before the document is considered hard
deleted and hidden from everybody on the API.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
8ccfdb3c6a (backend) add soft delete to documents and refactor db queryset
Now that we have introduced a document tree structure, it is not
possible to allow deleting documents anymore as it impacts the whole
subtree below the deleted document and the consequences are too big.

We introduce soft delete in order to give a second thought to the
document's owner (who is the only one to be allowed to delete a
document). After a document is soft deleted, the owner can still
see it in the trashbin (/api/v1.0/documents/trashbin).
After a grace period (30 days be default) the document disappears
from the trashbin and can't be restored anymore. Note that even
then it is still kept in database. Cleaning the database to erase
deleted documents after the grace period can be done as a maintenance
script.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
4de03d292a (backend) add API endpoint to move a document in the document tree
Only administrators or owners of a document can move it to a target
document for which they are also administrator or owner.

We allow different moving modes:
- first-child: move the document as the first child of the target
- last-child: move the document as the last child of the target
- first-sibling: move the document as the first sibling of the target
- last-sibling: move the document as the last sibling of the target
- left: move the document as sibling ordered just before the target
- right: move the document as sibling ordered just after the target

The whole subtree below the document that is being moved, moves as
well and remains below the document after it is moved.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
2e8a399668 (backend) override defaults.NB_OBJECTS for the demo tests
Otherwise when you increase the number of objects to test how the
application scales, the demo test will take too long...
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
7b39b3f7f6 (backend) improve test error
This test was missing the status code check. Without this check
the error that follows does not make sense because the content
returned is not at all what we expect in the following assert
statement.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
0003f9d0de (backend) add user roles as field in the document API representation
user roles were already computed as an annotation on the query for
performance as we must look at all the document's ancestors to determine
the roles that apply recursively. We can easily expose them as readonly
via the serializer.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
8117866ce7 ♻️(backend) remove content from list serializer and introduce excerpt
Including the content field in the list view is not efficient as we need
to query the object storage to retrieve it. We want to display an excerpt
of the content on the list view so we should store it in database. We
let the frontend compute it and save it for us in the new "excerpt" field
because we are not supposed to have access to the content (E2EE feature coming)
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
1d0386d9b5 (backend) add API endpoint to create children for a document
We add a POST method to the existing children endpoint.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
7ff4bc457f (backend) add API endpoint to list a document's children
This endpoint is nested under a document's detail endpoint.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
4333b46901 (backend) add depth, path and numchild to serialized document
This information is useful for the frontend to display the document
tree structure and is cheap to expose.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
d073a9c9b3 (backend) retrieve & update a document taking into account ancestors
A document should inherit the access rights a user has on any of its
ancestors.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
48662ceecb (backend) list only the first visible parent document for a user
Now that we have a tree structure, we should only include parents of
a visible subtree in list results.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
9a12452c26 🚚(backend) split test files to make place for tests on tree structure
The test_api_documents_list file was getting too long. We can extract
tests on filters and ordering.
2025-01-29 14:39:47 +01:00
Samuel Paccoud - DINUM
276b4f7c1b (backend) add django-treebeard to allow tree structure on documents
We choose to use Django-treebeard for its quality, performance and
stability. Adding tree structure to documents is as simple as
inheriting from the MP_Node class.
2025-01-29 14:39:47 +01:00
Anthony LC
0189078917 (CI) fix test-e2e-other-browser job
A recent update to the impress-frontend workflow
caused the test-e2e-other-browser job to fail.
This commit fixes the issue.
2025-01-29 14:23:46 +01:00
Anthony LC
6569f61fc4 (e2e) fix flakiness
Some flakiness appeared in the e2e tests. It
started to impact many pull requests. Time to
fix them.
2025-01-29 14:23:46 +01:00
Nathan Panchout
7880391648 ️(frontend) rollback test changes
Changes were pushed by mistake. We go back. We reorder the tests
correctly and return the environment variables to their original state.
2025-01-29 12:16:10 +01:00
Nathan Panchout
9b95a9c551 🚸(frontend) prevent duplicate invite user row in DocShareModal
Modify the DocShareModal to avoid showing the invite user row when the
email already exists in the search results, preventing redundant invite
options
2025-01-29 12:16:10 +01:00
Manuel Raynaud
3b151cf580 🍱(readme) update europe <3 opensource logo name
The name used for the logo europe<3opensource was causing trouble on
windows systems. We have to rename it with a more "normal" name.
2025-01-29 12:16:10 +01:00
virgile-deville
8b0f4db650 📝(doc) improve readme.md after v2 update
We want to serve as an example of our open source doc best practices.
We want people to find out.
- Which libraries we support
- How they can contribute translations and code
2025-01-28 20:26:40 +01:00
Nathan Panchout
a39990d90f 🚸(frontend) simplify invite user row logic in DocShareModal
Refactor the endActions logic to show invite user row when searching by
email, removing the unnecessary length check for users
2025-01-28 18:15:18 +01:00
Samuel Paccoud - DINUM
609ff91894 🚸(backend) on user search match emails by Levenstein distance
When the query looks like an email (includes @) we search by
Levenstein distance because we are just trying to prevent typing
errors, not searching anymore.

It is important to still propose results with a short Levenstein
distance because it is frequent to forget a double letter in
someone's name for example "Pacoud" or even "pacou" instead of
"Paccoud" and we want to prevent duplicates or failing on
invitation.

We consider the query string to be an email as soon as it contains
a "@" character. Trying harder to identify a string that is really
an email would lead to weird behaviors like toto@example.gouv looking
like and email but if we continue typing toto@example.gouv.f not
looking like an email... before toto@example.gouv.fr finally looking
like an email. The result would be jumping from one type of search
to the other. As soon as there is a "@" in the query, we can be
sure that the user is not looking for a name anymore and we can
switch to matching by Levenstein distance.
2025-01-28 18:15:18 +01:00
Manuel Raynaud
265a24fe7e 🚨(back) update code linting with ruff
Apply new rules provided with ruff version v0.9.3
2025-01-28 16:23:07 +01:00
renovate[bot]
9bc2b4877f ⬆️(dependencies) update python dependencies 2025-01-28 16:23:07 +01:00
Nathan Panchout
b93b43abe8 💄(frontend) improve DocsGridItem responsive padding
- Adjusted padding and alignment for desktop and mobile views
- Conditionally applied CSS styles based on screen size
2025-01-28 13:59:22 +01:00
Anthony LC
dd8bb18f69 🔊(changelog) add some changelog entries
Add some changelog entries that can be useful to
display in the release notes.
2025-01-28 13:36:03 +01:00
Anthony LC
545e8b2a3c 🔥(backend) remove code related to export pdf docx
The export is managed by the frontend, so we
don't need the code related to the export
in the backend side anymore.
2025-01-28 13:36:03 +01:00
Anthony LC
81837aff2b (frontend) export pdf docx front side
We have added the export to pdf and docx feature
to the front side. Thanks to that, the images are now
correctly exported even when the doc is private.
To be able to export the doc, the data must be
in blocknote format, for legacy purpose, we have
to convert the template to blocknote format before
exporting it.
2025-01-28 13:36:03 +01:00
lunika
40c1107959 🌐(i18n) update translated strings
Update translated files with new translations
2025-01-28 12:59:54 +01:00
Manuel Raynaud
0d7d42254b (helm) add a job allowing to run arbitrary management command
For a specific deployment we may need to run a specific management
command, like the one added previously updating all files content-type.
A template is added responsible to manage this case. The job will be
created only if the backend.job.command is set.
2025-01-28 10:33:30 +01:00
Anthony LC
67dc7feb98 🚑️(backend) command to update attachment content-type
The uploaded files in the system are missing
the content-type.
We add a command to update the content-type of
the existing uploaded files.
This command will run one time when we will deploy
to the environments.
2025-01-28 10:33:30 +01:00
Anthony LC
5b4b100e90 🏷️(backend) add content-type to uploaded files
All the uploaded files had the content-type set
to `application/octet-stream`. It create issues
when the file is downloaded from the frontend
because the browser doesn't know how to handle
the file.
We now determine the content-type of the file
and set it to the file object.
2025-01-28 10:33:30 +01:00
Anthony LC
b8be010389 🚚(helm) add posthog proxy
To contourn ads blocker, we add a proxy to the
posthog service. This way, we can access the
service from the same domain as the frontend.
2025-01-28 10:05:37 +01:00
Anthony LC
97cfa2c1ad (frontend) integrate posthog analytics
We integrate posthog, it will help us to track
user behavior and improve the product.
We get the configuration from the backend config
endpoint.
2025-01-28 10:05:37 +01:00
Anthony LC
c018c6fcf5 🔧(backend) add posthog configuration
We add the posthog configuration to the project.
We will expose the posthog configuration to the
frontend.
2025-01-28 10:05:37 +01:00
Nathan Panchout
70048328d1 (frontend) update document sharing tests
- Added Share button interactions in various document visibility
scenarios
- Updated test assertions for share and copy link functionality
- Improved test coverage for document sharing features
2025-01-28 08:59:28 +01:00
Nathan Panchout
55ddfe9181 💄(frontend) refactor document sharing and grid components
Improvements:
- Added disabled state for dropdown menus in share settings
- Updated document grid layout and responsiveness
- Simplified sharing and access count logic
- Improved tooltips and visibility of shared documents
- Created a new responsive doc grid hook
2025-01-28 08:59:28 +01:00
renovate[bot]
ee41d156c7 ⬆️(dependencies) update django to v5.1.5 [SECURITY] 2025-01-27 10:06:16 +01:00
Manuel Raynaud
5be2bc7360 ♻️(actions) create a reusable workflow to install front dependencies
In more than one workflow we need to install frontend dependencies and
this 3 workflows we are copy/pasting the same code. We want to refactor
this by creating a reusable workflow
https://docs.github.com/en/actions/sharing-automations/reusing-workflows
2025-01-24 12:22:48 +01:00
Manuel Raynaud
e46ba4f506 🌐(back) update source translations
In order to have a repo correctly confiogured to managed translation
with crowdin, source translations for the backend app are updated
2025-01-24 12:22:48 +01:00
Manuel Raynaud
7c8b969fa9 🔥(back) remove compiled translation file
The compiles translation file should not be track with git.
2025-01-24 12:22:48 +01:00
Manuel Raynaud
95515fd460 🌐(action) create a workflow to download translation
A new workflow is added responsible todownload new translated strings
and then create a pull request to update the code.
2025-01-24 12:22:48 +01:00
Manuel Raynaud
ce6cfc22ef 🌐(action) upload sources translation on crowdin
Crowdin has released its own github action to automatize translation
workflow. We want to use to upload sources when a PR is merged.
2025-01-24 12:22:48 +01:00
virgile-dev
4b3b441fc3 📝(project) update readme following upgrade to v2
Remove old mentions to "impress" following the repository renaming.
Improve and update descriptions to better reflect the status of the
project after release version 2.
2025-01-21 23:39:22 +01:00
Anthony LC
9194bf5a90 🔖(patch) release 2.0.1
Fixed:
🐛(frontend) title copy break app
2025-01-17 11:58:55 +01:00
Anthony LC
dc63a5839e ⬇️(frontend) downgraded blocknote to 0.21.0
The last version of Blocknote (0.22.0) has a bug,
when we copy paste a title, the app sometimes crashes.
Better to downgrade to 0.21.0 until the bug is fixed.
2025-01-17 11:33:47 +01:00
Anthony LC
d406846986 🎨(frontend) format css blocknote editor
We use the "css" function of style components
to format correctly the blocknote editor css.
2025-01-17 11:33:47 +01:00
Nathan Panchout
e85b07021e 🐛(frontend) fix collaboration cursor
- The collaboration slider is not fully shown when a user is at the very
top of the document
2025-01-17 11:02:41 +01:00
Nathan Panchout
282200ac3d 🐛(frontend) hide the sharing method when you don't have the rights
- Added a new hook `useCopyDocLink` to handle copying document links to
the clipboard with success/error notifications.
- Updated the `DocToolBox`, `DocsGridActions`, and `DocShareModal`
components to utilize the new copy link feature.
- Enhanced tests to verify the functionality of the copy link button in
various scenarios.
- Adjusted visibility checks for sharing options based on user access
rights.
2025-01-17 11:02:41 +01:00
Anthony LC
de8dea20d5 🔖(major) release 2.0.0
Added:
- 🔧(backend) add option to configure list of
essential OIDC claims
- 🔧(helm) add option to disable default tls
setting by @dominikkaminski
- 💄(frontend) Add left panel
- 💄(frontend) add filtering to left panel
- (frontend) new share modal ui
- (frontend) add favorite feature

Changed:
- 🏗️(yjs-server) organize yjs server
- ♻️(frontend) better separation collaboration
process
- 💄(frontend) updating the header and leftpanel
for responsive
- 💄(frontend) update DocsGrid component
- 💄(frontend) update DocsGridOptions component
- 💄(frontend) update DocHeader ui
- 💄(frontend) update doc versioning ui
- 💄(frontend) update doc summary u

Fixed:
- 🐛(backend) fix create document via s2s
if sub unknown but email found
- 🐛(frontend) hide search and create doc
button if not authenticated
- 🐛(backend) race condition creation issue
2025-01-15 12:46:00 +01:00
Anthony LC
342fc2ab59 ✏️(backend) fix read_only_fields is_favorite
is_favorite has a typo error.
This commit fixes it.
2025-01-15 12:13:40 +01:00
Anthony LC
b8132ef393 🐛(backend) creation race condition
3 requests we able to create a document:
- POST document request
- GET collaboration-auth
- GET media-auth

If the 2 last were faster than the first, a
document was created without the necessary
informations.
2025-01-15 12:13:40 +01:00
Nathan Panchout
2ede746d8a (frontend) hide search and create doc button if not logged
- Added visibility checks for 'search' and 'New doc' buttons in the
document visibility tests.
- Updated LeftPanelHeader to conditionally render 'search' and 'New doc'
buttons based on user authentication status, improving user experience
and access control.
2025-01-15 12:00:40 +01:00
Anthony LC
5bd0764bdd 🌐(frontend) add last translations
Add the missing translations FR / DE.
2025-01-14 17:38:19 +01:00
Samuel Paccoud - DINUM
610948cd16 🐛(backend) fix create document for user when sub does not match
When creating a document on behalf of a user via the server-to-server
API, a special edge case was broken that should should never happen
but happens in our OIDC federation because one of the provider modifies
the users "sub" each time they login.

We end-up with existing users for who the email matches but not the sub.
They were not correctly handled.

I made a few additional fixes and improvements to the endpoint.
2025-01-14 16:18:14 +01:00
Samuel Paccoud - DINUM
96bb99d6ec 🐛(compose) fix "port already taken" errors when starting docker compose
We have changed the project's name from "impress" to "docs" but haven't
replaced all occurrences of impress in the project because we want to be
careful of the consequences on deployments.

The name of the docker compose project was different for the "make pylint"
target. This was causing the bug error on ports. Let's rename it without
waiting.
2025-01-14 16:18:14 +01:00
Anthony LC
a090f180f4 💄(frontend) make favicon more visible
In dark mode, the favicon is not correctly visible.
This commit makes it more visible by adding some
white color to the favicon.
2025-01-13 18:15:25 +01:00
Anthony LC
c7e543d459 🐛(frontend) switch to other provider
When we redirect from a doc to another, the components
are not unmounted and states are not reset.
We now destroy the provider if we see that
the provider is not bind to the current doc.
2025-01-13 18:15:25 +01:00
Anthony LC
23e6b508f8 🚚(frontend) harmonize imports
Harmonize the imports in the frontend codebase
to keep the codebase consistent.
2025-01-13 11:02:24 +01:00
Nathan Panchout
49a3989977 💄(frontend) fix minor bugs and enhance DocTitle and DocShareModal
- Fixed minor bugs in the frontend codebase for improved stability.
- Enhanced DocTitle component to update title display dynamically using
useEffect.
- Refactored DocShareModal to improve modal content height calculation
for better responsiveness.
2025-01-13 11:02:24 +01:00
Nathan Panchout
8eb2b60937 (frontend) enhance document grid tests and component interactions
- Updated test cases to replace 'docs-grid-loader' with 'grid-loader'
for improved consistency across document grid tests.
- Refactored document interaction tests to utilize the 'docs-grid'
locator for better readability and maintainability.
- Enhanced document table content tests by refining element selection
methods for improved clarity and performance.
- Cleaned up test code to ensure better structure and maintainability.
2025-01-13 11:02:24 +01:00
Nathan Panchout
f02dcae52a (frontend) enhance DocShareModal and footer components
- Refactored DocShareModal to improve user experience with dynamic list
height and responsive design adjustments.
- Introduced new styling for modal elements using createGlobalStyle for
better visual consistency.
- Updated footer button text from 'Ok' to 'OK' for improved clarity.
- Enhanced user selection handling and search functionality within the
modal for better document sharing experience.
2025-01-13 11:02:24 +01:00
Nathan Panchout
098df5c0b5 (frontend) enhance UI components and improve document management
- Updated DropdownMenu to include index-based styling for better visual
consistency.
- Refactored QuickSearchStyle to remove unnecessary transitions for
smoother performance.
- Adjusted modal styles in cunningham-style.css for improved layout.
- Changed BlockNoteEditor to update block type from 'heading' to
'paragraph' for better content structure.
- Enhanced DocHeader and DocToolBox components with updated color themes
for improved visibility.
- Modified ModalRemoveDoc to change size and clean up unnecessary props
for better usability.
- Improved Heading and TableContent components to handle empty states
more gracefully.
- Updated DocsGrid to conditionally render content based on document
availability, enhancing user experience.
- Refined LeftPanel components for better layout and visual hierarchy,
including adjustments to padding and separators.
2025-01-13 11:02:24 +01:00
Nathan Panchout
684b77cbe6 (frontend) enhance DocsGrid component and simplify layout
- add scroll inside the doc grid
2025-01-13 11:02:24 +01:00
Nathan Panchout
81e9fc49fe (frontend) enhance document interaction tests
- Updated test cases to improve accessibility by replacing 'more_vert'
with 'more_horiz' for action buttons across various components.
- Refactored document deletion confirmation messages to use consistent
heading roles for better visibility.
- Simplified keyboard interactions in document table content tests for
improved clarity.
- Adjusted visibility checks for the share button to utilize more
descriptive labels.
- Cleaned up test code for maintainability and consistency.
2025-01-13 11:02:24 +01:00
Nathan Panchout
7c696fc1ec (frontend) enhance document sharing components
- Refactored DocShareAddMemberList to simplify button styling and
improve loading state handling.
- Updated DocShareAddMemberListItem and DocShareMemberItem to enhance
spacing and button color for better visual consistency.
- Improved DocShareInvitationItem and SearchUserRow with new theming and
spacing tokens for a more cohesive design.
- Adjusted padding and layout in DocShareModal and DocShareModalFooter
for improved responsiveness.
- Enhanced DocVisibility component with updated padding and text styling
for better readability.
- Cleaned up unused imports and optimized component structures for
maintainability.
2025-01-13 11:02:24 +01:00
Nathan Panchout
fc27043e9e (frontend) enhance document editor and header components
- Improved styling for headings in BlockNoteEditor for better visual
hierarchy.
- Adjusted padding in DocEditor and DocHeader based on device type for
responsive design.
- Updated DocTitle and ModalExport components to enhance typography and
spacing.
- Refactored DocToolBox to improve share button functionality and access
display.
- Enhanced versioning modal with better layout and accessibility
features.
- Cleaned up unused imports and optimized component structures for
maintainability.
2025-01-13 11:02:24 +01:00
Nathan Panchout
6ad1e27acf (frontend) enhance UI components and improve styling
- Updated DropdownMenu and ButtonLogin components for better
accessibility and visual consistency.
- Refactored Header and Title components to utilize new theming and
spacing tokens.
- Enhanced LanguagePicker styles for improved user experience.
- Introduced new utility functions in doc-management for better handling
of ProseMirror nodes and Yjs integration.
- Cleaned up unused imports and adjusted component styles for overall
code maintainability.
2025-01-13 11:02:24 +01:00
Nathan Panchout
899047d9a2 (frontend) enhance QuickSearch components
- Updated QuickSearchItemContent to ensure full width for better layout
consistency.
- Adjusted padding in QuickSearchStyle for improved spacing and visual
hierarchy.
- Refactored DocSearchItem to utilize Box component for consistent
styling and layout.
- Removed unused imports in DocSearchItem to streamline the codebase.
2025-01-13 11:02:24 +01:00
Nathan Panchout
78b5e2c1cc (frontend) enhance document grid
- Updated the layout and styling of the DocsGrid and DocsGridItem
components for improved responsiveness and visual consistency.
- Added a new background prop to the UserAvatar component for
customizable user avatars.
- Enhanced the DocsGridActions component to include a share option,
allowing users to share documents easily.
- Refactored SVG assets for pinned and simple documents to improve their
dimensions and visual representation.
- Improved the SimpleDocItem component to display document update times
and access indicators more effectively.
- Adjusted padding and spacing across various components to enhance
overall user experience.
2025-01-13 11:02:24 +01:00
Nathan Panchout
72f234027c (frontend) enhance left panel components
- Updated padding and radius styles in LeftPanelTargetFilters and
LeftPanelFavorites for improved layout consistency.
- Introduced LeftPanelDocContent component to display document details
when navigating to specific documentation pages.
- Enhanced LeftPanelContent to conditionally render LeftPanelDocContent
based on the current route.
- Adjusted LeftPanelHeader button colors for better visual hierarchy.
- Refactored MainLayout padding for a more responsive design.
2025-01-13 11:02:24 +01:00
Nathan Panchout
730efe7b74 (frontend) update color tokens and styles
- Modified color tokens for danger and info categories to enhance visual
consistency and accessibility.
- Updated button and modal styles, including adjustments to padding and
dimensions for improved layout.
- Replaced font files for Marianne with updated versions to ensure
better typography.
2025-01-13 11:02:24 +01:00
Nathan Panchout
63885117e1 (frontend) implement document favorites feature
- Added functionality to mark documents as favorites, including new
hooks `useMakeFavoriteDoc` and `useRemoveFavoriteDoc` for managing
favorite status.
- Enhanced the document management API to support favorite filtering
with the `is_favorite` parameter.
- Created a new e2e test for the favorite workflow to ensure proper
functionality.
- Updated the UI components to reflect favorite status, including
changes in `DocsGridActions`, `DocsGridItem`, and the new
`LeftPanelFavorites` component for displaying pinned documents.
- Adjusted SVG assets for better visual representation of pinned
documents.
2025-01-13 11:02:24 +01:00
Nathan Panchout
4f4c8905ff (frontend) update tests
- Enhanced test coverage for document sharing, member roles, and
visibility settings
2025-01-13 11:02:24 +01:00
Nathan Panchout
a5f6cb542d 🔥(frontend) remove unused document management components
- Deleted `DocVisibility`, `ModalShare`, `InvitationList`, `MemberList`,
and related components to streamline the document management feature.
- Updated component exports to reflect the removal of these components.
- Cleaned up associated assets and styles to improve code
maintainability.
2025-01-13 11:02:24 +01:00
Nathan Panchout
8456f47260 (frontend) enhance dropdown components and add new LoadMoreText feature
- Updated DropButton and DropdownMenu components to include new props
for accessibility and improved layout.
- Introduced LoadMoreText component for better user experience in
loading additional content.
- Added SearchUserRow and UserAvatar components for improved user search
functionality.
- Cleaned up unused imports and adjusted styles for better consistency
across components.
2025-01-13 11:02:24 +01:00
Nathan Panchout
eb35fdc7a9 (frontend) enhance document sharing features and role management
- Introduced new hooks and components for improved document sharing
functionality, including `useTranslatedShareSettings` and
`DocShareModal`.
- Added role management capabilities with `DocRoleDropdown` and
`DocShareAddMemberList` components, allowing users to manage document
access and roles effectively.
- Implemented user invitation handling with `DocShareInvitationItem` and
`DocShareMemberItem` components, enhancing the user experience for
managing document collaborators.
- Updated translation handling for role and visibility settings to
ensure consistency across the application.
- Refactored existing components to integrate new features and improve
overall code organization.
2025-01-13 11:02:24 +01:00
Nathan Panchout
ceaf1e28f9 (frontend) refactor QuickSearch components
- Simplified QuickSearchProps by removing unused properties and
enhancing type definitions.
- Updated QuickSearch component to utilize children for rendering,
improving flexibility.
- Added separator prop to QuickSearchInput for better control over
layout.
- Removed data prop from DocSearchModal's QuickSearch to streamline the
component's usage.
2025-01-13 11:02:24 +01:00
Nathan Panchout
c3da23f5d3 (frontend) add new color tokens and utility classes
- Introduced a comprehensive set of color tokens for blue, brown, cyan,
gold, green, olive, orange, pink, purple, and yellow shades.
2025-01-13 11:02:24 +01:00
Nathan Panchout
44784b2236 (frontend) implement document search functionality
- Added a new DocSearchModal component for searching documents.
- Introduced DocSearchItem component to display individua
 document results.
- Enhanced the useDocs API to support title-based searching.
- Implemented e2e tests for document search visibility and
functionality.
- Included an empty state illustration for no search results.
- Updated the LeftPanelHeader to open the document search modal.
2025-01-13 11:02:24 +01:00
Nathan Panchout
157f6200f2 (frontend) add Quick Search component suite
- Introduced a new Quick Search feature with multiple components
- Implemented styling for the Quick Search components to
ensure a cohesive look and feel across the application.
2025-01-13 11:02:24 +01:00
Nathan Panchout
2882348547 (frontend) update dependencies and enhance package configurations
- Added new dependencies: `luxon` and its type definitions
to the e2e app
- Introduced `cmdk` and `use-debounce` to the impress
app for enhanced UI components and debouncing functionality.
2025-01-13 11:02:24 +01:00
Nathan Panchout
e016cfab70 🐛(frontend) fix document editor height and update translations
- Adjusted the document editor height in the DocEditor component
- Updated translations for various terms to ensure consistency
cross the application.
- Improved layout and spacing in the DocsGridItem
component for a cleaner presentation.
2025-01-13 11:02:24 +01:00
Nathan Panchout
23b11e4096 💄(frontend) update document summary UI
- Enhanced the document summary UI for better visibility
and interaction.
- Refactored the DocHeader and DocEditor components to
 improve layout and responsiveness.
- Updated tests for the DocTableContent to reflect changes
in heading interactions and visibility checks.
2025-01-13 11:02:24 +01:00
Nathan Panchout
7696872416 (frontend) implement document filtering
- Introduced a new enum for default document filters
to improve code clarity.
- Updated the API call to support filtering documents
based on the creator.
- Enhanced the DocsGrid component to accept a target
filter, allowing dynamic content rendering based on user selection.
- Modified the main layout to include a left panel for improved
navigation and user experience.
- Added a new test suite for document filters, verifying the visibility
and selection states of 'All docs', 'My docs', and 'Shared with me'.
2025-01-13 11:02:24 +01:00
Nathan Panchout
42d9fa70a2 🔧(frontend) remove deprecated routes and update service worker
- Removed the versioning route from the default configuration to
streamline the documentation structure.
- Updated the service worker to eliminate references to the deprecated
 versioning fallback, enhancing the offline experience for users.
2025-01-13 11:02:24 +01:00
Nathan Panchout
a8a89def98 (frontend) enhance document versioning and loading experience
- Updated tests for document member list and versioning to utilize
'Load more' button instead of mouse wheel scrolling.
- Improved UI for document versioning, including visibility
checks and modal interactions.
- Refactored InfiniteScroll component to include a button for
loading more items, enhancing user experience.
- Adjusted DocEditor and DocHeader components to handle
version IDs more effectively.
- Removed deprecated versioning pages to streamline the codebase.
2025-01-13 11:02:24 +01:00
Nathan Panchout
5bcce0c64a 💄(frontend) update doc export modal
In the new interface, the export modal changes a little.

- We put the buttons on the right
- We remove the alert
- We transform the radio into select
2025-01-13 11:02:24 +01:00
Nathan Panchout
3a738fe701 (frontend) adapt all tests related to the new header
Since we no longer use an editable div but an input, we must
modify the tests accordingly
2025-01-13 11:02:24 +01:00
Nathan Panchout
d5670640f5 (frontend) update doc header ui
Modification of the header style to be consistent with the new UI :
- We replace the option menu with the DropdownMenu component
- We add a dowload button
- We put an input in place of an editable div.
2025-01-13 11:02:24 +01:00
Nathan Panchout
1d85eee78f 💄(frontend) add dropdown option for DocGridItem
Implement dropdown menu with functionality to delete a document
from the list
2025-01-13 11:02:24 +01:00
Nathan Panchout
5a46ab0055 (frontend) update tests to align with the new interface changes
- Adjust selectors and assertions to reflect updates in the UI layout and
design.
- Ensure all modified tests maintain compatibility with the updated structure.
- Fix any broken test cases caused by the redesign.
2025-01-13 11:02:24 +01:00
Nathan Panchout
3d5ff93a51 💄(frontend) update DocsGrid component
Implement the new version of  the DocsGrid  component
2025-01-13 11:02:24 +01:00
Nathan Panchout
b9c66c7c2a 🔧(frontend) update cunningham configuration
- update primary colors,and spacing.
- update tertiary button
2025-01-13 11:02:24 +01:00
Nathan Panchout
68a390ef59 (frontend) add react-intersection-observer package
- Install `react-intersection-observer` to manage element visibility detection.
- Enables features like lazy loading, animations on scroll, and triggering
events when elements appear in the viewport.
2025-01-13 11:02:24 +01:00
Nathan Panchout
192ab1121c 🔥(frontend) remove unused components due to new interface
Deleted two components that were no longer needed following the
implementation of the new interface. This cleanup helps streamline
he codebase and avoid unnecessary maintenance.
2025-01-13 11:02:24 +01:00
Nathan Panchout
83eb33d54a 💄(frontend) updating the header and leftpanel for responsive
Previously we added a left panel. We now need to adapt the layout
so that it becomesresponsive.

We therefore add a burger menu on the left on mobile which,
when clicked, deploys the left-panel over all the content.
2025-01-13 11:02:24 +01:00
Nathan Panchout
ee937de2c4 (frontend) update tests
Some minor changes have been integrated into the list of documents.
The tests must therefore be adapted accordingly.
2025-01-13 11:02:24 +01:00
Nathan Panchout
8d514bd571 💄(frontend) add left panel
In the new interface there is a new left panel. We implement it and add it
to the MainLayout
2025-01-13 11:02:24 +01:00
Nathan Panchout
e83c404e21 💄(frontend) add cunningham tokens
In order to use the spaces and grays of the DSFR,
we update the cunningham.ts file
2025-01-13 11:02:24 +01:00
Samuel Paccoud - DINUM
945f55f50d (backend) add test to secure updating user when matched on email
We had doubts that the user was correctly updated in the case where
its identity was matched on the email and not on the sub. I added
a test and confirmed that it was working correctly. I still modified
the backend to update the user based on its "id" instead of its "sub"
because it was confusing, but both actually work the same.
2025-01-10 19:30:17 +01:00
Samuel Paccoud - DINUM
9f83ea7111 ♻️(backend) rename required claims to essential claims as per spec
It was pointed by @lebaudantoine that the OIDC specification uses
the term "essential claims" for what we called required claims.

Further more, the Mozilla OIDC library that we use, validates claims
in a method called "verify_claims". Let's override this method.
2025-01-10 19:30:17 +01:00
Jacques ROUSSEL
f12c06e975 🔧(helm) add option to configure deployment annotations
We need to be abble to add specific annotations on Deployment in order
to use a reloader when external-secret sync new secrets
2025-01-09 07:20:01 +01:00
Jacques ROUSSEL
bbb176e153 🐛(CI) fix ci
The backend secret is managed by external-secret now so we should not
keep it in the chart
2025-01-08 11:55:40 +01:00
Jacques ROUSSEL
02793040fd 🐛(CI) improve helm release
In order to avoid a github release when we build the helm chart, we use
another action
2025-01-06 15:44:16 +01:00
Jacques ROUSSEL
0773e83149 📝(self-hosted) add documentation
Add a documentation to deploy a self-hosted visio instance in a
standalone way (witout AI features)
2025-01-06 15:44:16 +01:00
Jacques ROUSSEL
21205b4d19 🐛(CI) add helm release action
In order to avoird code duplication we have to release a helm chart
2025-01-06 15:44:16 +01:00
Jacques ROUSSEL
60dbf6c11d 💚(ci) fix jobs after migration
The repository migration broke the CI. To fix it, we removed the
dependency on the secrets repository.
2025-01-06 12:17:40 +01:00
Anthony LC
2491ad7142 (e2e) adapt test to new Blocknote release
Copy as html provide a html lightly different than
before, so the test was adapted to the new html
provided.
2025-01-02 15:42:39 +01:00
Anthony LC
3b2834cf6d ⬆️(dependencies) update js dependencies 2025-01-02 15:42:39 +01:00
renovate[bot]
7ed2b23ea3 ⬆️(dependencies) update python dependencies 2024-12-30 11:00:32 +01:00
Samuel Paccoud - DINUM
c879f82114 (backend) add option to configure list of required OIDC claims
We want to be able to refuse connection for users who have missing
claims from a list of required keys.
2024-12-24 17:10:52 +01:00
Anthony LC
02a4740c66 ♻️(frontend) create useProviderStore
We created useProviderStore, a store dedicated
to managing the provider of the document.
We created as well a new hook useCollaboration,
it will be use to interact with the provider store.
This refacto is a first step to implement
the long polling.
2024-12-24 12:29:30 +01:00
Anthony LC
6cb2702e6b ♻️(frontend) update already existing tasks
When a task was already existing, we were not
updating it. This commit fixes that.
2024-12-24 12:29:30 +01:00
Anthony LC
94a9f7a84e 🔒️(y-provider) add cors middlewares
Add cors middlewares to y-provider server.
It will control how clients connect to the server
with http requests.
2024-12-24 12:29:30 +01:00
Anthony LC
e53465ce11 🏗️(y-provider) organize yjs server
Many routes were in the server.ts file, now they
are in their own files in the handlers folder.
The server.ts file is now AppServer that handles
the routes.
We split as well the tests.
2024-12-24 12:29:30 +01:00
Julien Bouquillon
33d1f3c151 ️(y-provider) reduce sentry tracesSampleRate
Reduce `tracesSampleRate` due to +120k daily events.
2024-12-20 09:52:43 +01:00
Julien Bouquillon
fc4eba2497 ️(frontend) reduce sentry tracesSampleRate
Reduce `tracesSampleRate` due to +120k daily events.
2024-12-20 09:52:43 +01:00
Dominik Kaminski
3e5f27c1d5 🔧(helm) add option to disable default tls setting
Sets an option for those who uses impress
with a different secretName in ingress.
2024-12-19 15:16:16 +01:00
Anthony LC
f2f64f7dd6 🔖(minor) release 1.10.0
Added:
- (backend) add server-to-server API endpoint
to create documents
- (email) white brand email
- (y-provider) create a markdown converter endpoint

Changed:
- ️(docker) improve y-provider image

Fixed:
- ️(e2e) reduce flakiness on e2e tests
2024-12-17 17:54:49 +01:00
Anthony LC
d842800df3 ✏️(email) change the quotation marks around role
The quotation marks around role have been changed
to the wrong ones. This commit fixes this issue.
2024-12-17 17:29:05 +01:00
Anthony LC
1af2ad0ec4 🔧(helm) add conf white brand email
Add the demo configuration for the
white brand email.
2024-12-17 17:29:05 +01:00
Anthony LC
67915151aa (e2e) add a test on doc creation server side
We recently added a new feature to the app, which
is the ability to create a document from server to
server.
Server A will send a request to Server B with
a markdown content, and Server B will create a
the document after converting the markdown to
yjs base64 format.
This test will check all the steps of the process
and assert that the document is displayed correctly
on the frontend in the blocknote editor.
2024-12-17 14:49:23 +01:00
Anthony LC
de25b36a01 🐛(CI) add chrome playwright install
In a recent commit we removed Chrome from the
install of playwright in the CI job test-e2e,
but it is needed, we put it back.
2024-12-17 14:12:41 +01:00
Anthony LC
59e74e6eeb 🐛(e2e) fix flaky tests
3 tests were flags are flaky or bringing flakiness.
We improved them.
2024-12-17 12:42:02 +01:00
Anthony LC
4e7f095b0f ️(e2e) set maxFailures with CI
If a test fails (retries included), the test runner
will stop after reaching maxFailures.
We will not have to wait for all tests to
run to see the results.
2024-12-17 12:42:02 +01:00
Anthony LC
cdea75b87f ️(ci) playwright install
Sometimes Playwwright installation fails on CI,
it seems to arrive when we update the dependency cache.
We will do a general install before installing the
playwright browser to be sure everything is in place,
it should be fast since we have the cache.
We move the playwright installation before setting
the docker container, so we will wait less if we have
to retry the test because of the Playwwright installation.
2024-12-17 12:42:02 +01:00
Anthony LC
6a0d2e21b5 🐛(frontend) fix rerendering when doc is saving
When the document is saved, the blocknote toolbar
was rerendering, causing the toolbar to close
some panels.
It was creating flakiness in the e2e tests, plus
it was not a good user experience.
This commit fixes this issue.
2024-12-17 12:42:02 +01:00
Anthony LC
b79d5fccbc ⬆️(dependencies) update js dependencies 2024-12-16 18:28:37 +01:00
Anthony LC
6d77cb1801 ️(docker) improve y-provider image
Improve y-provider image by having the
node_modules as small as possible.
We move split the Dockerfile and
add it to the y-provider folder,
it will be easier to read and maintain.
2024-12-16 17:39:45 +01:00
lebaudantoine
e4a45a556c 🐛(backend) fix bucket access in the tilt stack
S3 username was desynchronized with the helmfile. Leading to error,
when patching object or saving any update to the Minio bucket.

@rouja fixed it.
2024-12-16 17:17:42 +01:00
lebaudantoine
3ca39ceb8a ♻️(yprovider) support multiple API keys to separate responsibilities
Support for two API keys has been added to the YProvider microservice to
decouple responsibilities between the collaboration server and other
endpoints. This improves security by scoping keys to specific purposes and
ensures a clearer separation of concerns for easier management and debugging.
2024-12-16 17:17:42 +01:00
lebaudantoine
8a93122882 (yprovider) add test to prevent silent breaking changes
Per Quentin's request, added a test to ensure developers are warned
if the token format is updated, preventing backend compatibility issues.
2024-12-16 17:17:42 +01:00
lebaudantoine
8eb986591a 💡(backend) warm about the token nature of Yprovider microservice
Note to the future myself, using a raw token format is
not common. It should be refactor
2024-12-16 17:17:42 +01:00
lebaudantoine
c10808b611 ♻️(backend) generalize YProvider API config
Abstracted base URL and API key under 'y-provider' for
reuse in future endpoints, aligning with microservice naming.

Please note the YProvider API here is internal to the cluster.
In facts, we don't want these endpoints to be exposed by any ingress
2024-12-16 17:17:42 +01:00
lebaudantoine
ba63358098 🔧(backend) configure conversion microservice in dev
Update helm values to configure the conversion microservice while
creating document server to server.
2024-12-16 17:17:42 +01:00
lebaudantoine
52534db3e1 🐛(backend) fix issues with conversion microservice integration
Minor adjustments were needed after working in parallel on two PRs.
The microservice now accepts an API key without requiring it as a Bearer token.

A mistake in reading the microservice response was corrected after refactoring
the serializer to delegate logic to the converter microservice.
2024-12-16 17:17:42 +01:00
renovate[bot]
dc9b375ff5 ⬆️(dependencies) update python dependencies 2024-12-16 10:45:49 +01:00
renovate[bot]
65fdf115be ⬆️(dependencies) update django to v5.1.4 [SECURITY] 2024-12-13 21:28:11 +01:00
Anthony LC
ecb2b35ec8 (email) white brand email
The email was branded "La Suite Numérique",
we updated the template to make it generic, we
will use settings env variables to customize the
email for each brand.
2024-12-13 17:58:43 +01:00
renovate[bot]
2d13e0985e ⬆️(dependencies) update python dependencies 2024-12-12 18:56:25 +01:00
lebaudantoine
5014443f80 💩(y-provider) init a markdown converter endpoint
This code is quite poor. Sorry, I don't have much time working
on this feature. However, it should be functional.

I've reused the code we created for the Demo with Kasbarian.
I've not tested it yet with all corner case. Error handling
might be improved for sure, same for logging.

This endpoint is not modular. We could easily introduce options
to modify its behavior based on some options. YAGNI

I've added bearer token authentification, because it's unclear
how this micro service would be exposed. It's totally not required
if the microservice is not exposed through an Ingress.
2024-12-12 14:37:30 +01:00
lebaudantoine
3fef7596b3 (y-provider) create utils function toBase64
Add utility function to convert BitArray to Base64 string.
This is required for creating Base64-encoded documents,
as the frontend do.
2024-12-12 14:37:30 +01:00
lebaudantoine
19042907be (y-provider) add BlockNote server utils and yjs
Needed dependencies to mimic frontend code when generating a
document from a markdown string.

Will be used in the upcoming commits.
2024-12-12 14:37:30 +01:00
Samuel Paccoud - DINUM
5cdd06d432 (backend) add server-to-server API endpoint to create documents
We want trusted external applications to be able to create documents
via the API on behalf of any user. The user may or may not pre-exist
in our database and should be notified of the document creation by
email.
2024-12-12 14:01:46 +01:00
Samuel Paccoud - DINUM
47e23bff90 🔥(emails) remove dead template code
The email footer file is not being used and should be deleted.
2024-12-12 14:01:46 +01:00
Anthony LC
7dfc62b2c5 🔖(minor) release 1.9.0
Added:
- (backend) annotate number of accesses
on documents in list view
- (backend) allow users to mark/unmark
documents as favorite

Changed:
- 🔒️(collaboration) increase collaboration access security
- 🔨(frontend) encapsulated title to its own component
- ️(backend) optimize number of queries on
document list view
- ♻️(frontend) stop to use provider with version
- 🚚(collaboration) change the websocket key name

Fixed:
- 🐛(frontend) fix initial content with collaboration
- 🐛(frontend) Fix hidden menu on Firefox
- 🐛(backend) fix sanitize problem IA
2024-12-12 08:37:10 +01:00
Anthony LC
39c4af0a7c 🐛(frontend) hide cursor for authenticated users
When a authenticated user was in read-only mode,
the cursor was still visible.
This commit hides the cursor for authenticated users
in read-only mode.
2024-12-11 21:18:40 +01:00
Anthony LC
57c5c394f5 🐛(backend) improve sanitizer ai_services
The json response of the AI service is badly formatted.
This commit improves the sanitizer to try
to handle the response correctly.
2024-12-11 21:18:40 +01:00
Anthony LC
be6da38a08 🐛(frontend) only owner will make initial content
In some cases, the sync of the initial content
is not being done correctly.
We will let only the owner of the document
to make the initial content.
2024-12-11 21:18:40 +01:00
Anthony LC
fc36ed08f1 🐛(frontend) fix initial content with collaboration
The way the initial content was created was causing
issues with the collaboration server.
As soon a user started typing, the problem was gone.
This commit fixes that by letting Blocknote
managing the initial content, then we update the
Blocknote initial content with our initial content.
2024-12-11 16:08:13 +01:00
Anthony LC
ed90769081 🐛(backend) fix sanitize problem IA
Albert send us back a malformed IA json, the
sanitize function was not able to handle it correctly.
We add a try catch on it, to not use the sanitizer if
the json.loads fails.
2024-12-11 15:21:56 +01:00
Anthony LC
a8310fa0ff ️(frontend) remove debounce on useHeadings
We remove the debounce on useHeadings, it
decreases the user experience and it's not
necessary a big performance improvement.
2024-12-11 14:54:41 +01:00
Anthony LC
a902e31521 🔧(helm) add ingress collaboration api
We need to keep the stickyness between the
collaboration api and the ws server, to do so,
we will use "upstream-hash-by: $arg_room", meaning
that the stickyness will be based on the room query.
We need to ahve 2 ingress to handle the
"collaboration_auth", only the ws routes has to
use the "collaboration_auth" subrequest.
2024-12-11 14:54:41 +01:00
Anthony LC
932ab13d97 📈(collaboration) add sentry
Add sentry to the collaboration server.
It will be used to log errors and exceptions.
2024-12-11 14:54:41 +01:00
Anthony LC
94a1ba7989 (backend) notify collaboration server
When an access is updated or removed, the
collaboration server is notified to reset the
access connection; by being disconnected, the
accesses will automatically reconnect by passing
by the ngnix subrequest, and so get the good
rights.
We do the same system when the document link is
updated, except here we reset every access
connection.
2024-12-11 14:54:41 +01:00
Anthony LC
bfecdbf83a (y-provider) add tests for y-provider server
We add jest tests for the y-provider server.
The CI will be able to run the tests.
2024-12-11 14:54:41 +01:00
Anthony LC
ba1cfc3c27 (y-provider) endpoint POST /collaboration/api/reset-connections
We want to be able to reset the connections of a document.
To do this, we need to be able to send a
request to the collaboration server.
To do so, we added the endpoint
POST "/collaboration/api/reset-connections"
to the collaboration server thanks to "express".
2024-12-11 14:54:41 +01:00
Samuel Paccoud - DINUM
2cba228a67 🧑‍💻(helm) rename minio root user password
Using "impress" as the name of minio's root user in Tilt's
dev environment, was triggering obfuscation of the logs in Tilt's
console each time the word "impress" was used.
This made the logs hard to read.
2024-12-11 14:54:41 +01:00
Samuel Paccoud - DINUM
66553ee236 (backend) add subrequest auth view for collaboration server
We need to improve security on the access to The collaboration server
We can use the same pattern as for media files leveraging the nginx
subrequest feature.
2024-12-11 14:54:41 +01:00
Samuel Paccoud - DINUM
64674b6a73 ♻️(backend) rename, factorize and improve the subrequest media auth view
We want to use the same pattern for the websocket collaboration service
authorization as what we use for media files.

This addition comes in the next commit but doing it efficiently
required factorizing some code with the media auth view.
2024-12-11 14:54:41 +01:00
Anthony LC
a9def8cb18 ♻️(frontend) create useHeadings hook
- We create the useHeadings hook to manage the
headings of the document and staty DRY.
- We use the headings store in IconOpenPanelEditor
and TableContent, to avoid prop drilling.
- We add a debounce on the onEditorContentChange
to improve a bit the performance.
2024-12-06 15:23:16 +01:00
Anthony LC
69186e9a26 🩺(CI) wait for services to be ready
We add a check to be sure all the services are
ready before starting the e2e tests.
2024-12-06 15:23:16 +01:00
Anthony LC
f606826098 ♻️(frontend) stop to use provider with version
Version are not editable, we don't need to activate
the collaboration provider for them.
Simplify the code by removing the provider
from the version.
2024-12-06 15:23:16 +01:00
Anthony LC
aff036d9fb 🚚(collaboration) change the websocket url name
We will have 2 urls targeting the server, better
to improve the naming to avoid confusion.
2024-12-06 15:23:16 +01:00
Anthony LC
57ed08994b 🔊(changelog) add missing logs
Some logs were missing or not at the good place.
This commit replaces them correctly.
2024-12-06 15:23:16 +01:00
rvveber
131eefa1ac 🔨(frontend) encapsulate title component
in order to modularize in the future
the title component is encapsulated.
2024-12-06 14:16:24 +01:00
Anthony LC
b4e639cc24 ♻️(frontend) adapt Blocknote button
Last upgrade of Blocknote changes the editor
method getSelection, the blocks were not being
selected in certain cases.
We updated the methods to select the blocks
correctly.
2024-12-05 23:34:06 +01:00
Samuel Paccoud - DINUM
ba962af914 ⬆️(backend) bump openai library version as it breaks tests
This looks like an instability in the openai library's definition
of dependencies.
2024-12-05 23:34:06 +01:00
Anthony LC
76514a6e2b 🏷️(frontend) adapt typing with recent upgrade
An upgrade to @sentry/nextjs@8.42.0 changed
some typing. It is not from @sentry/types but
from @sentry/core now.
2024-12-05 23:34:06 +01:00
Anthony LC
b69a5342d9 ⬇️(dependency) downgrade workbox-webpack-plugin to 7.1.0
In the 1.8.0 we experienced issues with the service
worker not updating properly. We suspect that the
workbox-webpack-plugin is the cause of this issue.
Better to downgrade to the last version that worked
until we have time to investigate the issue.
We add workbox-webpack-plugin to the renovate.json
file to avoid future updates.
2024-12-05 23:34:06 +01:00
renovate[bot]
c25682f199 ⬆️(dependencies) update js dependencies 2024-12-05 23:34:06 +01:00
Anthony LC
eec8b4d2c3 ♻️(frontend) adapt frontend with new access types
We don't get the accesses anymore from the backeend,
instead we get the number of accesses.
We remove the list of owners in the doc header because
we don't have easily this informations anymore and
we will have to do a bigger refacto.
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
1af7b797bc (backend) test interference btw documents permissions and filtering
We want to make sure that applying filters on the document view list
does not interfere with permissions.
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
b5c159bf63 (backend) allow filtering on document titles
This is the minimal and fast search feature, while we are working on
a full text search based on opensearch. For the moment we only search
on the title of the document.
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
bfbdfb2b5c (backend) allow filtering documents by link reach
We want to be able to limit document list views to only public documents,
or only restricted or authenticated documents.
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
08bb64ddc1 (backend) allow filtering by documents marked as favorite
We recently allowed authenticated users to mark a document as favorite.
We were lacking the possibility for users to see only the documents
they marked as favorite.
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
23f90156bf (backend) add creator field on document and allow filtering on it
We want to be able to limit the documents displayed on a logged-in user's
list view by the documents they created or by the documents that other
users created.

This is different from having the "owner" role on a document because this
can be acquired and even lost. What we want here is to be able to
identify documents by the user who created them so we add a new field.
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
1899cff572 🐛(backend) fix flaky test by clarifying user ordering
On the user search API by similarity, we had a flaky test because
2 users had the same similarity score. Adding a secondary ordering
field makes ordering deterministic between users who share the same
similarity score.
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
774c2ce248 (backend) annotate number of accesses on documents
The new UI will display the number of accesses on each document.

/!\ Once team accesses will be used, this will not represent the number
    of people with access anymore and will have to be improved by
    computing the number of people in each team.
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
89d9075850 (backend) allow users to mark/unmark documents as favorite
A user can now mark/unmark documents as favorite.
This is done via a new action of the document API endpoint:
/api/v1.0/documents/{document_id}/favorite
POST to mark as favorite / DELETE to unmark
2024-11-28 16:02:27 +01:00
Samuel Paccoud - DINUM
2c915d53f4 ️(backend) optimize number of queries on document list view
I realized most of the database queries made when getting a document
list view were to include nested accesses. This detailed information
about accesses in only necessary for the document detail view.

I introduced a specific serializer for the document list view with
less fields. For a list of 20 documents with 5 accesses, we go down
from 3x5x20= 300 queries to just 3 queries.
2024-11-28 16:02:27 +01:00
Anthony LC
797d9442ac 🔖(patch) release 1.8.2
Changed:

- ♻️(SW) change strategy html caching
2024-11-28 15:31:03 +01:00
Anthony LC
573d054748 ♻️(SW) change strategy html caching
We will use the network first strategy for the html
files. This will allow us to always have the
latest version of the html files.
2024-11-28 09:30:06 +01:00
Anthony LC
2035a256f5 🔖(patch) release 1.8.1
Fixed:
🐛(frontend) link not clickable and flickering firefox
2024-11-27 17:17:35 +01:00
Anthony LC
c94f26c8b9 ⬇️(SW) workbox-webpack-plugin to 7.1.0
A recent update to the workbox-webpack-plugin
package seems to introduce strange behavior.
Better to downgrade in waiting that it is more stable.
2024-11-27 16:50:11 +01:00
Anthony LC
fc2f14b3f4 🐛(frontend) link not clickable and flickering firefox
The link in the read mode was not clickable anymore,
it was due to a attempt to not display the cursor
of anonymous users.
We changes the way to do it by rendering our own cursor,
when a user is anonymous we don't render the cursor.
By rendering our own cursor we fixed another problem,
the cursor was flickering when the user was typing
at the end of the line on the firefox browser.
2024-11-27 16:50:11 +01:00
Anthony LC
6dd1697915 🐛(frontend) use hook useTranslation
Sentry highlitghted a few errors about the
function "t" not being defined. Better to get
it from the hook useTranslation.
2024-11-27 16:50:11 +01:00
Anthony LC
79e899c301 ♻️(frontend) add hooks useUploadFile
Move upload file logic to hooks useUploadFile.
It will be more readable and easy to reuse.
2024-11-27 16:50:11 +01:00
Anthony LC
2194301716 🔖(minor) release 1.8.0
Added:
- 🌐(backend) add german translation
- 🌐(frontend) Add German translation
- (frontend) Add a broadcast store
- (backend) whitelist pod's IP address
- (backend) config endpoint
- (frontend) config endpoint
- (frontend) add sentry
- (frontend) add crisp chatbot

Changed:
- 🚸(backend) improve users similarity search and
sort results
- ♻️(frontend) simplify stores
- (frontend) update $css Box props type to add
styled components RuleSet
- (CI) trivy continue on error

Fixed:
- 🔧(backend) fix logging for docker and make it
configurable by envar
- 🦺(backend) add comma to sub regex
- 🐛(editor) collaborative user tag hidden when
read only
- 🐛(frontend) users have view access when revoked
- 🐛(frontend) fix placeholder editable when double clicks
2024-11-27 09:47:42 +01:00
Anthony LC
0348894ab8 🐛(frontend) fix rerender title with broadcasting
The title was not rerendering on other clients
when the title was updated by one client.
This commit fixes the issue.
We set a min width for the title as well, it
will fix the issue with strange behavior when
people were double clicking.
2024-11-26 18:15:18 +01:00
Anthony LC
9b17d8bea1 🚨(frontend) remove Crisp warning
Remove the Crisp warning that was being displayed
on the console in our environments.
2024-11-26 18:15:18 +01:00
Anthony LC
69d6b6f934 (CI) trivy continue on error
Trivy is extremly flaky,
we need to continue on error to avoid
blocking the pipeline.
We still keep the check, to see if there are any
vulnerabilities, but we don't want to block
the pipeline.
2024-11-26 11:53:11 +01:00
Anthony LC
6c106374fa (frontend) add crisp chatbot
Integrate Crisp chatbot for immediate user support access.

This enables real-time interaction, enhancing user experience
by providing quick assistance.
2024-11-25 17:06:02 +01:00
Anthony LC
af039d045d 🔧(backend) add CRISP_WEBSITE_ID setting
Add setting CRISP_WEBSITE_ID. This setting is
used to configure the Crisp chat widget.
It will be available to the conf endpoint, to
be used by the frontend.
2024-11-25 17:06:02 +01:00
Anthony LC
4c9caf09ba ⬆️(CI) upgrade upload-artifact@v3 to v4
Upload artifact v3 is deprecated soon, so we need
to upgrade it to v4.
2024-11-25 13:16:06 +01:00
Anthony LC
3fd02adbec 💄(frontend) remove Blocknote fix
A recent upgrade of Blocknote to 0.19.2 fixed
a issue that we were solving. We removed our
fix as it is no longer needed.
2024-11-25 13:16:06 +01:00
Anthony LC
90dac3cd15 🏷️(frontend) update typescript types
We updated typescript to 5.7.2.
Some types were deprecated and we had to update them.
2024-11-25 13:16:06 +01:00
Anthony LC
d0307ee6d9 ⬆️(dependencies) update js dependencies 2024-11-25 13:16:06 +01:00
Anthony LC
09d02b7ced 🚚(frontend) move conf api urls to api folder
Previous refacto let only the api urls in the conf
file, so better to move it to the api folder.
2024-11-25 09:46:14 +01:00
Anthony LC
56a26d9663 🧪(CI) pass trivy security
The trivy security blocked the deploiement.
It says that we have a vulnerability because
we are using the cross-spawn@7.0.3 package, but
we are not, we are using the cross-spawn@7.0.6 package.
We will bypass this security check in the docker-hub.yml
file in waiting for another solution.
2024-11-25 09:46:14 +01:00
Anthony LC
42f809f6d4 ♻️(frontend) get collaboration server url from config endpoint
We centralized the configuration on the backend
side, it is easier to manage and we can change
the configuration without having to rebuild the
frontend.
We now use the config endpoint to get the collaboration
server url, we refacto to remove the frontend env
occurences and to adapt with the new way to get the
collaboration server url.
2024-11-25 09:46:14 +01:00
Anthony LC
7d64c82987 ♻️(frontend) get media url from config endpoint
We centralized the configuration on the backend
side, it is easier to manage and we can change
the configuration without having to rebuild the
frontend.
We now use the config endpoint to get the media url,
we refacto to remove the frontend env occurences
and to adapt with the new way to get the media url.
2024-11-25 09:46:14 +01:00
Anthony LC
6252227bb6 ♻️(frontend) get theme from config endpoint
We centralized the configuration on the backend
side, it is easier to manage and we can change
the configuration without having to rebuild the
frontend.
We now use the config endpoint to get the theme,
we refacto to remove the frontend env occurences
and to adapt with the new way to get the theme.
2024-11-25 09:46:14 +01:00
Anthony LC
e9ac393a8f (frontend) add sentry
In order to monitor the frontend, we are adding
sentry.
2024-11-25 09:46:14 +01:00
Anthony LC
5b1745f991 (frontend) add config provider
Add a ConfigProvider to the frontend to provide
configuration to the app.
The configuration is loaded from the config
endpoint, we will use react-query cache capabilities
to store the configuration.
2024-11-25 09:46:14 +01:00
Anthony LC
0e55bf5c43 🔒️(helm) allow server host and whitelist pod IP for health checks
In a Kubernetes environment, we need to whitelist the pod's IP address
to allow health checks to pass. This ensures that Kubernetes liveness and
readiness probes can access the application to verify its health.
2024-11-22 13:01:55 +01:00
Samuel Paccoud - DINUM
9f66f73501 🔧(backend) fix logging for docker and make it configurable by envar
Logs were not made to the console so it was hard to debug in k8s.
We propose a ready made logging configuration that sends everything
to the console and allow adjusting log levels with environment
variables.
2024-11-20 11:51:20 +01:00
Samuel Paccoud - DINUM
c3da28b07f ️(helm) bring back helm chart
This is a revert of 1da5a removing actual deployments and keeping
only the dev environment in Tilt.

The clean-up was a bit heavy handed. We should keep the Helm
chart to the development repository and move away only the
deployment configuration.
2024-11-20 11:51:20 +01:00
Anthony LC
b035b96dec ⬆️(CI) bump python version in backend test
We were testing the backend with python 3.10.0, but
actually the backend was running with python 3.12.6.
We bump the python version in the backend test to match
the running version of the backend.
2024-11-20 09:51:08 +01:00
Anthony LC
9623ac4141 🩹(backend) get current release from pyproject.toml
"get_release" was returning NA, we fixed it by
getting the version from pyproject.toml, to do so we
use tomllib
Since tomllib is a native library from Python 3.11,
we bump the required version to 3.11 on the pyproject.toml.
2024-11-20 09:51:08 +01:00
Anthony LC
c8edbd285b 🔧(backend) add FRONTEND_THEME setting
The frontend need to know the theme to be used,
so we need to add a new setting to the backend,
in order to expose this value to the frontend.
2024-11-20 09:51:08 +01:00
Anthony LC
016597d5a2 🔧(backend) add COLLABORATION_SERVER_URL setting
The frontend need to know the collab server url,
so we need to add a new setting to the backend,
in order to expose this value to the frontend.
If the setting is not defined, the frontend current
domain will be used as the base url.
In production this setting do not need to be defined
since we have nginx capturing the ws requests,
but in development we need to define it to target
the collaboration server.
2024-11-20 09:51:08 +01:00
Anthony LC
52dea8fa2f 🔧(backend) add MEDIA_BASE_URL setting
The frontend need to know the base url for the
media files, so we need to add a new setting
to the backend, in order to expose this value
to the frontend.
If the setting is not defined, the frontend current
domain will be used as the base url.
In production this setting do not need to be defined
since we have nginx capturing the media requests,
but in development we need to define it to target
the nginx server.
2024-11-20 09:51:08 +01:00
Anthony LC
0a37a8ea6d (backend) add public endpoint /api/v1.0/config/
Add public endpoint /api/v1.0/config/ to
share some public configuration values.
2024-11-20 09:51:08 +01:00
Anthony LC
c1404ef904 ⬆️(dependencies) bump cross-spawn from 7.0.3 to 7.0.6
Bumps cross-spawn from 7.0.3 to 7.0.6.

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-20 09:24:34 +01:00
renovate[bot]
2c0fce61df ⬆️(dependencies) update js dependencies 2024-11-18 17:25:16 +01:00
Nathan Panchout
bbe9b6b6cf (frontend) add styledCss props to Box component
In order to facilitate DX and not to use a string in the code for the css.
We add the $styledCss props to the Box component.
This object comes from Styled component
2024-11-15 10:33:56 +01:00
Anthony LC
23231563c9 💄(frontend) text color on Blocknote code block options
The options for the code block in the Blocknote
editor was not visible. We changed the text color
to make it visible.
A fix will be made to the code block options in the
next blocknote release.
2024-11-14 17:36:11 +01:00
Anthony LC
d75c8668c5 🚨(frontend) blocknote cast to Dictionnary
The last Blocknote upgrade (0.19.0) gives us
a warning with the dictionnary typing.
We cast it to the correct type to remove the warning.
2024-11-14 17:36:11 +01:00
Anthony LC
f266232b5a ♻️(frontend) use next/router instead of next/navigation
The last upgrade of next.js gives a warning
when we were using next/navigation with the
pages router.
This commit fixes this issue.
2024-11-14 17:36:11 +01:00
Anthony LC
a8362e8e88 ⬆️(dependencies) update js dependencies 2024-11-14 17:36:11 +01:00
Anthony LC
e4dfae1905 ♻️(frontend) simplify useDocStore
We moved the editor store to its own store in the previous
commit. This change allow us to simplify useDocStore.
2024-11-13 15:25:29 +01:00
Anthony LC
a09e740648 ♻️(frontend) move editor store to useEditorStore
Previous changes migrated the editor store to
doc-management, we move it back doc-editor and
simplify it.
2024-11-13 15:25:29 +01:00
Anthony LC
5ee6a43f08 (frontend) add useBroadcastStore
Add the useBroadcastStore.
It will give us the ability to easily
broadcast actions to all connected clients.

In this case, we requery the doc to everyone
when a change relative to the doc rights is made.
2024-11-09 10:21:24 +01:00
Anthony LC
8bd83cbfcd 🚚(frontend) move useDocStore to doc-management
We want to make more accessible the doc store
to every feature, so we move it to the
doc-management folder.
2024-11-09 10:21:24 +01:00
Anthony LC
bc14d1d0f8 🐛(editor) collaborative user tag hidden when read only
When the user was in read-only mode, the user
tag could be displayed when they were touching the
doc. This commit fixes this issue.
We add the full name instead of the email in the
cursor tag.
2024-11-08 12:01:23 +01:00
Anthony LC
526e649f06 🦺(backend) add comma to sub regex
Some sub have comma, the regex was a bit too strict
and didn't allow it, this commit fixes that.
2024-11-08 10:53:53 +01:00
Anthony LC
ac40eb8f7c 🌐(frontend) add German translation
- Add the german translation to Docs
- Add the german language to the frontend
language picker
2024-11-07 15:58:49 +01:00
lindenb1
c750cf10a8 🌐(backend) adding de_DE translation for the backend
This adds German translation to the backend and
adjusts the .po file sequence by priority.

Signed-off-by: lindenb1 <linden@b1-systems.de>
2024-11-07 11:49:41 +01:00
Samuel Paccoud - DINUM
4f4951cdcd 🚸(backend) improve users similarity search and sort results
In some edge cases, the domain part the email addresse is
longer than the name part. Users searches by email similarity
then return a lot of unsorted results.

We can improve this by being more demanding on similarity when
the query looks like an email. Sorting results by the similarity
score is also an obvious improvement.

At the moment, we still think it is good to propose results with
a weak similarity on the name part because we want to avoid
as much as possible creating duplicate users by inviting one of
is many emails, a user who is already in our database.

Fixes 399
2024-11-06 08:27:18 +01:00
Anthony LC
50891afd05 🔖(minor) release 1.7.0
Added:
- 📝Contributing.md
- 🌐(frontend) add localization to editor
- Public and restricted doc editable
- (frontend) Add full name if available
- (backend) Add view accesses ability

Changed:
- ♻️(frontend) avoid documents indexing in search engine
- ♻️(frontend) list accesses if user has abilities
- 👔(backend) doc restricted by default

Fixed:
- 🐛(backend) require right to manage document
  accesses to see invitations
- 🐛(i18n) same frontend and backend language using
  shared cookies
- 🐛(frontend) add default toolbar buttons
- 🐛(frontend) throttle error correctly display

Removed:
- 🔥(helm) remove infra related codes
2024-10-25 14:41:48 +02:00
Anthony LC
cbb6fc740a 👔(backend) doc restricted by default
By default a created document was in "authenticated"
mode, we switch to "restricted" by default.
2024-10-25 14:25:48 +02:00
Anthony LC
31c3dd6119 🛂(frontend) show member list depend ability
We integrate the new ability "accesses_view" that
tells if a user can view the accesses of a document.
2024-10-24 17:31:34 +02:00
Samuel Paccoud - DINUM
15700ddd8d (backend) add new ability on document "accesses_view"
We need this ability in the frontend to know whether we should try
to display the list of users who have document accesses. If this
ability is False (e.g for anonymous users), we should only show
the link reach and link role when clicking on the "Share" button.
2024-10-24 17:31:34 +02:00
Anthony LC
d8673a8cf7 (frontend) display full name if available
We can get the full name from the OIDC, so we should
display it if available.
2024-10-24 10:52:58 +02:00
NathanPanchout
a5af9f0776 🐛(frontend) avoid documents indexing in search engine
Some documents are available publicly (without being logged) and may thus end-up
being indexed by search engine.
2024-10-24 10:43:13 +02:00
Anthony LC
d715e7b3b6 🌐(frontend) translate last features
Translate:
- Mardown Buttons
- doc public editable
2024-10-24 10:15:28 +02:00
Jacques ROUSSEL
1da5a6a411 🗑️(ci) clean old deployment and ci
We move deployment stuff to a new repository. we don't need this
codeanymore
2024-10-24 09:50:18 +02:00
Anthony LC
af5ffc22ac (e2e) fix flaky tests
Fix a flaky tests on the e2e test:
- "it renders correctly when we switch from one doc
to another"
- "it saves the doc when we change pages"
2024-10-23 18:11:08 +02:00
Anthony LC
3434029654 ♻️(frontend) improve handleAIError
To display the throttle error messages,
we are doing a condition on the error message
that we get from the backend.
It is error prone because the backend error
message are internationalized.
This commit fixes this issue.
It DRY the component as well.
2024-10-23 18:11:08 +02:00
virgile-deville
6baa06bd3f 📝(Documentation) add an issue selection section
Added a link to the github project so that contributors know what to prioritize.
2024-10-23 17:08:48 +02:00
Anthony LC
8107d4f531 📝(contributing) add changelog part in contributing
We add a new section in the CONTRIBUTING.md file
to explain how to update the CHANGELOG.md file.
We improve the pull request section as well.
2024-10-23 12:46:49 +02:00
Anthony LC
f8c8044605 🧑‍💻(makefile) add frontend-lint cmd
Add the command frontend-lint to the makefile.
2024-10-23 12:46:49 +02:00
rvveber
a84f4de02c 🔨(i18n) disable key separation for translations
Improves on commit bfde526
2024-10-23 12:35:48 +02:00
rvveber
3c374e3cc7 🐛(i18n) same frontend and backend language using shared cookies
frontend: switch to cookie-based language selection
backend: use cookie for language
2024-10-23 12:35:48 +02:00
Anthony LC
ff364f8b3d (frontend) increase doc visibility options
We now have 3 visibility options for docs:
- public
- restricted
- authenticated

We also have 2 editability options:
- readonly
- editable

The editability options are only available
for public and authenticated docs.
2024-10-23 11:20:33 +02:00
Anthony LC
c0cb12f002 ♻️(frontend) minor components update
- change flex property of Box component
- Forward the ref of Text component
- globalize tooltip padding
2024-10-23 11:20:33 +02:00
Samuel Paccoud - DINUM
0f0f812059 🐛(backend) fix invitations API endpoint access rights
Only users who have the rights to manage accesses on the document should
be allowed to see and manipulate invitations. Other users can see access
rights on the document but only when the corresponding user/team has
actually been granted access.

We added a parameter in document abilities so the frontend knows when
the logged-in user can invite another user with the owner role or not.
2024-10-22 19:39:59 +02:00
NathanPanchout
7fc59ed497 🌐(frontend) add localization to editor
Currently, when you change language the editor does not change. So we add this
functionality
2024-10-22 13:54:20 +02:00
renovate[bot]
60120852f5 ⬆️(dependencies) update js dependencies 2024-10-21 09:55:17 +02:00
Anthony LC
f2c389e2b3 🐛(frontend) add default toolbar buttons
We are overriding the default toolbar to add the
markdown and ai buttons. By doing that we were
missing some default buttons that are useful depend
on the block type. This commit adds the default
buttons to the toolbar.
2024-10-21 09:45:47 +02:00
renovate[bot]
305359ae15 ⬆️(dependencies) update python dependencies 2024-10-21 09:20:33 +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
Anthony LC
62124ae475 🌐(frontend) translate last features
Translate:
- IA Buttons
- doc clipboard markdown / html
2024-10-17 10:11:37 +02:00
Anthony LC
c327928921 🐛(frontend) fix flaky e2e test
A test on e2e was flaky, this commit fixes it.
2024-10-16 22:58:52 +02:00
Anthony LC
be26a9457f 🔧(helm) add ai setting to environments
Add the ai setting to the environments.
2024-10-16 22:58:52 +02:00
Anthony LC
5dc43cbc8b (frontend) add ai blocknote feature
Add AI button to the editor toolbar.
We can use AI to generate content with our editor.
A list of predefined actions are available to use.
2024-10-16 22:58:52 +02:00
Anthony LC
9abf6888aa 🎨(frontend) reduce prop drilling thanks to doc store
We start to have a deep prop drilling with doc,
time to use the doc store to reduce that.
We still prefer to pass the doc as a prop to
keep our component as "pure" as possible, but if
the drilling is too deep, better
to use the doc store.
2024-10-16 22:58:52 +02:00
Anthony LC
aff3b43c9d (backend) create ai endpoint
We created 2 new action endpoints on the document
to perform AI operations:
- POST /api/v1.0/documents/{uuid}/ai-transform
- POST /api/v1.0/documents/{uuid}/ai-translate
2024-10-16 22:58:52 +02:00
Samuel Paccoud - DINUM
e8d95facdf (backend) allow uploading more types of attachments
We want to allow users to upload files to a document, not just images.
We try to enforce coherence between the file extension and the real
mime type of its content. If a file is deemed unsafe, it is still accepted
during upload and the information is stored as metadata on the object
for display to readers.
2024-10-16 19:40:28 +02:00
Samuel Paccoud - DINUM
a9f08df566 (backend) move freezegun to dev dependencies
Freezegun is for testing and should not be installed in the
production image.
2024-10-16 19:40:28 +02:00
Samuel Paccoud - DINUM
2fecbc1162 🚚(backend) split test file for api template accesses
The number of lines in this file had exceeded 1000 lines.
2024-10-16 19:16:50 +02:00
Samuel Paccoud - DINUM
1fc3029d12 🐛(backend) fix dysfunctional permissions on document create
When creating a document access, users were benefitting on the targeted
document from the highest access right they have among all documents.
This is because we forgot to filter on the document ID when retrieving
the role of the user. We improved all tests to secure this issue.
2024-10-16 19:16:50 +02:00
rvveber
bbcb5e0cf1 (frontend) added copy-as buttons for HTML and Markdown
Add buttons to copy editor content as HTML or Markdown. Closes #300
2024-10-16 17:57:10 +02:00
renovate[bot]
e4a7ac0f3c ⬆️(dependencies) update python dependencies 2024-10-16 10:41:25 +02:00
Anthony LC
24630791d8 ♻️(email) use full name instead of email
If the full name is available,
we will use it to identify the user in the email
instead of the email address.
2024-10-16 09:36:33 +02:00
Anthony LC
97d00b678f 🚨(docker) fix docker warning about casing
When we build the docker image, we get a warning
about the casing in the Dockerfile. This commit
fixes the casing in the Dockerfile.
2024-10-14 22:20:54 +02:00
Anthony LC
52eb973164 (CI) refecto test-e2e
With the new container available, we can simplify
the workflow by removing the build step
and using the container directly.
2024-10-14 22:20:54 +02:00
Anthony LC
789879a9cc 🧑‍💻(project) improve frontend bootstrap
We were providing a frontend development container
to the developers, but it was not working properly.
Problem of hot reload was present for Windows and
Linux users.
We stop to provide this development container and
we will provide a container connected to the build
of the frontend.
You can still access the frontend after bootstrap
on the "localhost:3000", but if you want to develop
you will have to install the frontend dependencies
localy and run the frontend in development mode.
This will be more efficient and will avoid the
problem of hot reload, and right on folder access.
2024-10-14 22:20:54 +02:00
Anthony LC
52c52d53b7 🔧(docker) add missing frontent env
The env MEDIA_URL was missing in the frontend
Dockerfile. It is not necessary in our
running environment (staging / preprod ...) but it
is necessary if we want to run the frontend with
a different media url.
SW_DEACTIVATED was missing as well, we need to
deactivate the service worker in the frontend when
we test with Playwright.
2024-10-14 22:20:54 +02:00
Anthony LC
54fe6a2319 🐛(frontend) invalidate queries after removing user
When we remove a user from the list of members,
we need to invalidate the user query for the
user to be found again.
We improve the error message when a user is
already a member of the document.
2024-10-14 19:58:41 +02:00
Anthony LC
bc5dcb0ed5 ️(frontend) use Marianne woff2 if compatible
Woff2 is a more modern format for web fonts,
and it is supported by all modern browsers.
We still keep the woff format for
compatibility with older browsers.
2024-10-11 15:26:18 +02:00
Anthony LC
6c3f3f6a77 💄(frontend) components more multi theme friendly
We adapt a bit the tokens of some components to be
more multi theme friendly.
When we will add another theme, it will be
easier to adapt to the new theme.
2024-10-11 15:26:18 +02:00
Anthony LC
6e64bad1e2 🔖(patch) release 1.5.1
Fixed:
- 🐛(db) fix users duplicate
2024-10-10 16:46:27 +02:00
Anthony LC
0d5b2382ab 🐛(db) fix users duplicate
Some OIDC identity providers provide a random
value in the "sub" field instead of an
identifying ID.
It created duplicate users in the database.
This migration fixes the issue by removing the
duplicate users after having updated all
the references to the old users.
2024-10-10 16:23:46 +02:00
Anthony LC
39d0211593 🔖(minor) release 1.5.0
Added:
- (backend) add name fields to the user synchronized with OIDC
- (ci) add security scan
- (frontend) Activate versions feature
- (frontend) one-click document creation
- (frontend) edit title inline
- 📱(frontend) mobile responsive
- 🌐(frontend) Update translation

Changed:
- 💄(frontend) error alert closeable on editor
- ♻️(backend) Change email content
- 🛂(frontend) viewers and editors can access share modal
- ♻️(frontend) remove footer on doc editor

Fixed:
- 🛂(frontend) match email if no existing user
matches the sub
- 🐛(backend) gitlab oicd userinfo endpoint
- 🛂(frontend) redirect to the OIDC when private doc
and unauthentified
- ♻️(backend) getting list of document versions
available for a user
- 🔧(backend) fix configuration to avoid different
ssl warning
- 🐛(frontend) fix editor break line not working
2024-10-09 16:48:12 +02:00
Anthony LC
86085f87a1 ♻️(frontend) remove share button when not logged in
We remove the share button if the user is not
logged in. Most of the elements in the share modal
nececessitate the user to be logged in.
2024-10-09 16:17:03 +02:00
Anthony LC
ebdcb4b2f0 (frontend) add back the footer and cgu pages
We need to add back the footer and cgu pages,
but we will not display the footer on the doc
editor pages.
2024-10-09 16:17:03 +02:00
Anthony LC
3a0dff5b0e 🐛(service-worker) fix circular import problem
When we were installing the service-worker, errors
were thrown because of circular imports.
This commit fixes the problem by being more explicit
about the imports.
2024-10-09 11:56:35 +02:00
Anthony LC
c682bce6f6 📱(frontend) docs mobile friendly
We adapt the docs component to be
mobile friendly.
2024-10-08 17:25:52 +02:00
Anthony LC
8dd7671d1f 🌐(frontend) add language name to LanguagePicker
The language picker were only showing the language
code, now it shows the language name.
2024-10-08 17:25:52 +02:00
Anthony LC
fe391523c8 📱(frontend) header small mobile friendly
We adapt the header to be small mobile friendly.
We added a burger menu to display the dropdown
menu on small mobile.
2024-10-08 17:25:52 +02:00
Anthony LC
399cf893ad 📱(frontend) add hook store useResponsiveStore
useResponsiveStore is a hook store that provides
the current screen size and the current device type.
2024-10-08 17:25:52 +02:00
Anthony LC
f081f7826a 🔥(frontend) remove footer and legal pages
With the new ui, the footer and legal pages
are no longer needed.
This commit removes them.
2024-10-08 17:25:52 +02:00
Anthony LC
638e1aedb7 🏷️(service-worker) retype the doc creation in SW
Recent changes made the doc creation in SW outdated.
This commit retype the doc creation in SW.
We adapt the main type to fit the new doc type.
2024-10-08 16:30:50 +02:00
Anthony LC
dcbef9630e (e2e) reduce e2e flakiness
We identified some tests causing flakiness
in the e2e tests.
2024-10-08 16:30:50 +02:00
Anthony LC
a745cb7498 🌐(frontend) translate last features
Translate:
- doc visibility
- doc versions
- doc inline title editing
2024-10-08 16:30:50 +02:00
Anthony LC
d701195ae5 🧑‍💻(i18n) rebuild translations for crowdin
For some unexpected reasons it can happen that the
translations in Crowdin are lost.
If that happens, we can rebuild the Crowdin
translations file from our translated json file.
"translations-skeleton.json" is the downloaded
source file from Crowdin.
It will generate "translations-rebuild.json",
which can be uploaded directly to Crowdin.
2024-10-08 16:30:50 +02:00
renovate[bot]
ac18d23fbc ⬆️(dependencies) update python dependencies 2024-10-07 17:32:27 +02:00
Samuel Paccoud - DINUM
ff7914f6d3 🛂(backend) match email if no existing user matches the sub
Some OIDC identity providers may provide a random value in the "sub"
field instead of an identifying ID. In this case, it may be a good
idea to fallback to matching the user on its email field.
2024-10-04 22:08:39 +02:00
Anthony LC
647e6c1cf5 ⬆️(frontend) upgrade blocknote to 0.16.0
Version 0.16.0 of Blocknote fixes the breakline issue.
2024-10-04 11:04:41 +02:00
Anthony LC
98b60ebe93 🐛(frontend) fix infinity scroll on invitation list
The infinity scroll had some difficulties to
load the next page of invitations because the ref
could be not init.
This commit fixes the issue.
2024-10-04 11:04:41 +02:00
Anthony LC
0b15ebba71 🛂(frontend) readers and editors can access share modal
Readers and editors of a document can access the share
modal and see the list of members and their roles.
2024-10-04 11:04:41 +02:00
Samuel Paccoud - DINUM
eee20033ae (backend) add full_name and short_name to user model and API
The full_name and short_name field are synchronized with the OIDC
token upon each login.
2024-10-03 23:39:56 +02:00
Anthony LC
e642506675 🚚(frontend) move Markdown button to is own file
To keep the codebase clean and organized,
we moved the Markdown button to its own file.
2024-10-02 15:24:29 +02:00
Anthony LC
883055b5fb (frontend) first heading is the title of the document
When the title of the doc is not set, the first heading
is used as the title of the document.
2024-10-02 15:24:29 +02:00
Anthony LC
968a1383f7 (frontend) initial editor content is now a heading
When we create a new document,
the initial content is now a heading instead of a
paragraph.
This is to make it easier to set the title
of the document.
2024-10-02 15:24:29 +02:00
Anthony LC
6a2030e235 ♻️(frontend) change useHeading to useHeadingStore
We need to get the headings in multiple places.
To not have multiple listeners to compute the same
thing, we will use a store to store the editor
headings.
2024-10-02 15:24:29 +02:00
Anthony LC
4d2a73556a 🔥(frontend) remove useless update title codes
We can now update the title directly in the header,
so we don't need the update title modal anymore.
We remove the buttons to trigger the modal
and the modal itself.
2024-10-02 15:24:29 +02:00
Anthony LC
90027d3a5a (frontend) edit title inline
We can now edit the title of the document inline.
This is a feature that is very useful for users
who want to change the title of the document
without having to go to the document
management page.
2024-10-02 15:24:29 +02:00
Anthony LC
61593bd807 ♻️(frontend) one click create doc
We can now create a doc in one click.
The doc will be created with a default name,
the user will be able to edit the name inline.
2024-10-02 15:24:29 +02:00
Anthony LC
99ebc9fc9c 🚚(frontend) rename useTransRole to useTrans
We rename useTransRole to useTrans to make
it more general and reusable.
It will be used for all reusable doc translation.
2024-10-02 15:24:29 +02:00
Anthony LC
a5e798164c 🚚(frontend) add utils userAgent
In order to stay DRY, we moved the function
to know if a user is from a firefox browser
from useSaveDoc to utils userAgent.
2024-10-02 15:24:29 +02:00
Anthony LC
002b9340e3 🛂(frontend) redirect to the OIDC when private doc
We now redirect to the OIDC when a user is on
a private doc and get a 401 error.
2024-10-02 15:24:29 +02:00
Anthony LC
f00f833ee2 🐛(frontend) fix sticky panel editor
the sticky panel editor is not working properly,
the panel editor should be sticky until the bottom
when the user scrolls the page.
2024-10-02 15:24:29 +02:00
Jacques ROUSSEL
3a6bc8c0f7 🔧(backend) fix configuration to avoid different ssl warning
Fix following warning messages :
- You have not set a value for the SECURE_HSTS_SECONDS setting.
- Your SECURE_SSL_REDIRECT setting is not set to True.
2024-10-01 09:27:37 +02:00
virgile-dev
76368f1ae9 📝(project) update README.md
Minimal update to the readme file to better reflect the project.
2024-09-30 17:52:14 +02:00
Anthony LC
fab86f7f87 ⬆️(dependencies) bump js dependencies
We bump the js dependencies to their latest version.
2024-09-30 17:39:28 +02:00
Anthony LC
ac74db2fde ♻️(frontend) add versions in the panel editor
We add the features version to the panel editor.
We had to refactor the panel to be able to
have the version with the table of content in
the same panel.
2024-09-30 17:26:23 +02:00
Anthony LC
b2480eea74 ♻️(frontend) minor components refacto
Improve some props in different components.
2024-09-30 17:26:23 +02:00
Anthony LC
20a898c978 👔(frontend) adapt versions api with new types
We updated the backend recently, the types of the
versions list has changed.
This commit adapts the frontend to the new types.
2024-09-30 17:26:23 +02:00
renovate[bot]
589d3abd8d ⬆️(dependencies) update python dependencies 2024-09-30 12:39:51 +02:00
renovate[bot]
1ba588d416 ⬆️(dependencies) update js dependencies 2024-09-30 11:57:15 +02:00
Anthony LC
b1f37495d6 🚑️(backend) fix CVEs in backend image
Use alpine version for production image instead of
debian in order to have less CVEs.
2024-09-30 10:59:52 +02:00
Jacques ROUSSEL
8c9cb43097 🚑️(frontend) fixe CVEs in frontend image
Use alpine version for production image instead of debian in order to
have less CVEs.
2024-09-30 10:59:52 +02:00
Jacques ROUSSEL
aeeed8feb5 (ci) add security scan
Add a security scan for CVE with trivy
2024-09-30 10:59:52 +02:00
Anthony LC
1e89eb1a21 🛂(frontend) redirect to the OIDC when private doc
We now redirect to the OIDC when a user is on
a private doc and is not authentified.
2024-09-27 16:04:31 +02:00
Anthony LC
413e0bebad 🐛(frontend) fix redirection after login
The redirection after login was not working properly.
The user was redirected to the home page
instead of the page he was trying to access.
2024-09-27 16:04:31 +02:00
Samuel Paccoud - DINUM
a2a184bb93 ♻️(api) refactor getting versions to expose pagination
Getting versions was not working properly. Some versions returned
were not accessible by the user requesting the list of available
versions.

We refactor the code to make it simpler and let the frontend handle
pagination (load more style).
2024-09-27 14:59:32 +02:00
Anthony LC
827d8cc8e1 ♻️(backend) change email invitation content
Change the email invitation content. More
document related variables are added.
To benefit of the document inheritance, we moved
the function email_invitation to the document model.
2024-09-26 09:58:11 +02:00
Anthony LC
833c53f5aa 💄(frontend) error alert closeable on editor
When we were uploading a file that was not allowed,
an error alert was shown. This alert was not closeable.
This commit makes the alert closeable.
2024-09-24 16:38:25 +02:00
Jacques ROUSSEL
2775a74bdb (ci) add helmfile linter and fix issue in argocd sync
Add a github job to run helmfile linter on PR
Add argocd annotation to fix job syncing issue
2024-09-24 14:09:26 +02:00
Anthony LC
450790366d (CI) fix flaky test on MinIO initialized
MinIO server need to be initialized before
running the job to configure MinIO.
We add a delay to wait for MinIO server to be ready.
2024-09-24 09:45:09 +02:00
Anthony LC
7b04f664cd (backend) fix flaky test on tmp file
It seems to have a race condition, sometimes the
tmp file is not deleted before the test assertion.
We let the test sleep for 0.5 second before
the assertion.
2024-09-24 09:45:09 +02:00
renovate[bot]
358508ffa3 ⬆️(dependencies) update python dependencies 2024-09-23 11:32:22 +02:00
Anthony LC
9388c8f8f4 🛂(backend) oidc userinfo endpoint json format
The userinfo endpoint can return 2 content types:
- application/json
- application/jwt

Gitlab oidc returns a json object, while
Agent Connect oidc returns a jwt token.
We are adapting the authentication to handle both cases.
2024-09-23 10:57:57 +02:00
renovate[bot]
40d8c949d9 ⬆️(dependencies) update js dependencies 2024-09-23 10:43:10 +02:00
Jacques ROUSSEL
6b0b052d78 🔒️(helm) fix secret sync precedence
When new secret is added to backend secret, it's not sync at the
beginning of argocd synchronisation and jobs are blocked. Theses new
annotations fix this issue.
2024-09-20 18:29:10 +02:00
Anthony LC
ac86a4e7f7 🔖(minor) release 1.4.0
Added:
- (backend) Add link public/authenticated/restricted
access with read/editor roles
- (frontend) add copy link button
- 🛂(frontend) access public docs without being logged

Changed:
- ♻️(backend) Allow null titles on documents
for easier creation
- 🛂(backend) stop to list public doc to everyone
- 🚚(frontend) change visibility in share modal
- ️(frontend) Improve summary

Fixed:
- 🐛(backend) Fix forcing ID when creating a
document via API endpoint
- 🐛 Rebuild frontend dev container from makefile
2024-09-18 12:01:52 +02:00
Anthony LC
bbe5501297 🌐(frontend) translate last features
Translate:
- doc visibility
- doc table of contents
2024-09-18 11:18:29 +02:00
Anthony LC
b37acf3138 💄(frontend) improve ui table of contents
- keep correctly the text on the left side
- improve accuracy highlightment heading when scrolling
- display full heading text when text transform is applied
- fix typo
2024-09-18 11:18:29 +02:00
Anthony LC
5bd78b8068 🚚(frontend) rename feature summary to table of content
We rename the feature summary to table of content
to better reflect the feature purpose.
2024-09-17 15:06:37 +02:00
Anthony LC
ed39c01608 ♻️(frontent) improve summary feature
- Change Summary to Table of content
- No dash before the title
- Change font-size depend the type of heading
- If more than 2 headings the panel is open
by default
- improve sticky
- highligth the title where you are in the page
2024-09-17 15:06:37 +02:00
Anthony LC
748ebc8f26 🔧(helm) change conf helm dev
Some frontend env vars were added on the frontend
side, we need to add them to the dev helm chart.
2024-09-17 15:06:37 +02:00
renovate[bot]
03262878c4 ⬆️(dependencies) update js dependencies 2024-09-16 14:30:29 +02:00
Anthony LC
97fa5b8532 🧑‍💻(makefile) add build frontend dev
docker build frontend dev was lacking.
If dependencies were updated, the change were
not reflected in the frontend container.
2024-09-12 08:06:17 +02:00
Anthony LC
a092c2915b ♻️(frontend) adapt doc visibility to new api
We updated the way we handle the visibility of a doc
in the backend. Now we use a new api to update
the visibility (documents/{id}/link-configuration/)
of a doc. We adapted the frontend to use this new api.
We changed the types to reflect the new api and
to keep the same logic.
2024-09-11 22:31:30 +02:00
Samuel Paccoud - DINUM
9b44e021fd ♻️(models) allow null titles on documents
We want to make it as fast as possible to create a new document.
We should not have any modal asking the title before creating the
document but rather show an "untitle document" title and let the
owner set it on the already created document.
2024-09-11 22:31:30 +02:00
Samuel Paccoud - DINUM
2c3eef4dd9 (api) allow forcing ID when creating a document via API endpoint
We need to be able to force the ID when creating a document via
the API endpoint. This is usefull for documents that are created
offline as synchronization is achieved by replaying stacked requests.

We do it via the serializer, making sure that we don't override an
existing document.
2024-09-11 22:31:30 +02:00
Samuel Paccoud - DINUM
dec1a1a870 🔥(api) remove possibility to force document id on creation
This feature poses security issues in the way it is implemented.
We decide to remove it while clarifying the use case.
2024-09-11 22:31:30 +02:00
Samuel Paccoud - DINUM
1e432cfdc2 (api) allow updating link configuration for a document
We open a specific endpoint to update documents link configuration
because it makes it more secure and simple to limit access rights
to administrators/owners whereas other document fields like title
and content can be edited by anonymous or authenticated users with
much less access rights.
2024-09-11 22:31:30 +02:00
Samuel Paccoud - DINUM
f5c4106547 🐛(api) fix randomly failing test on document list ordering via API
The test was randomly failing because postgresql and python sorting
was not 100% consistent e.g "treatment" vs "treat them" were not
ordered the same.

Comparing each field value insteat of relying on "sort" solves the
issue and makes the test simpler.
2024-09-11 22:31:30 +02:00
Samuel Paccoud - DINUM
494638d306 (models/api) add link access reach and role
Link access was either public or private and was only allowing readers.

This commit makes link access more powerful:
- link reach can be private (users need to obtain specific access by
  document's administrators), restricted (any authenticated user) or
  public (anybody including anonymous users)
- link role can be reader or editor.

It is thus now possible to give editor access to an anonymous user or
any authenticated user.
2024-09-11 22:31:30 +02:00
Samuel Paccoud - DINUM
41260de1c3 🔥(compose) remove docker compose version
The version is now automatically guessed by Docker Compose. This
commit will fix the warning raised about the uselessness of this
setting.
2024-09-11 22:31:30 +02:00
Anthony LC
140a630a6e 🛂(backend) stop to list public doc to everyone
Everybody could see the full list of public docs.
Now only members can see their public docs.
They can still access to any specific public doc.
2024-09-11 22:31:30 +02:00
Anthony LC
b716881d50 📌(frontend) add lacking peerdependencies
- Add some lacking peerdependencies to the packages.
- Resolve conflicts eslint dependencies.
2024-09-11 11:39:03 +02:00
renovate[bot]
fa8466d44d ⬆️(dependencies) update js dependencies 2024-09-11 11:39:03 +02:00
Anthony LC
4ba34f6c80 (CI) add ngnix for the frontend
Because of the Next.js bug with the 404 on the
dynamic routes, we are not able to assert some
behaviors from the e2e tests and the CI.
So we are adding a ngnix to the CI e2e tests
to be able to route correctly our frontend.
2024-09-10 15:51:28 +02:00
Anthony LC
e4712831f2 🎨(frontend) standalone component DocTagPublic
We want to rerender the public tag when we update
the visibility of a document. The problem is that
the public tag is not a standalone component, so
to have it rerender we needed to rerender the whole
document, it is not visually nice.
We created a standalone component for
the public tag, so when we update the visibility
of a document, only the public tag will be rerender.
2024-09-10 15:51:28 +02:00
Anthony LC
37db31a8d5 (frontend) add copy link button
Add a copy link button to the doc
visibility component. This button will
copy the link of the doc to the clipboard.
2024-09-10 15:51:28 +02:00
Anthony LC
4321511631 🚚(frontend) change visibility in share modal
We stop to propose to make the document public
from the doc creation modal.
We now propose to change the visibility of
the document from the share modal.
2024-09-10 15:51:28 +02:00
Anthony LC
459cb5e2e2 🛂(frontend) access public docs without being logged
We can now access public docs without being logged.
2024-09-10 15:51:28 +02:00
Anthony LC
2a7e3116bd 🔖(minor) release 1.3.0
Added:
- Add image attachments with access control
- (frontend) Upload image to a document
- (frontend) Summary
- (frontend) update meta title for docs page

Changed:
- 💄(frontend) code background darkened on editor
- 🔥(frontend) hide markdown button if not text

Fixed:
- 🐛 Fix emoticon in pdf export
- 🐛 Fix collaboration on document
- 🐛 (docker) Fix compatibility with mac

Removed:
- 🔥(frontend) remove saving modal
2024-09-10 09:04:54 +02:00
Anthony LC
b9046a2d9b 🐛(frontend) meta title rerender issue
The meta title is not displayed when we come back to
a page from the dynamic router. The code seems to
compute to quickly so we need to add a delay to the
meta title computation.
2024-09-09 17:47:24 +02:00
Anthony LC
d249ed0c71 🔧(helm) change production media storage name
The set the correct media storage name
for production environment.
2024-09-09 16:36:24 +02:00
Anthony LC
48d3738ec2 (frontend) update meta title for docs page
We update the meta title for the docs page
with the title of the document.
It will be easier for the user
to identify the document in their browser tab,
in their bookmarks and history.
2024-09-05 13:26:49 +02:00
Anthony LC
92102e4a36 🔧(compose) stop forcing platform for Keycloak PostgreSQL image
Forcing `platform: linux/amd64` for the PostgreSQL
image causes compatibility issues and performance
degradation on Mac ARM chips (M1/M2).
Removing the platform specification allows Docker
to select the appropriate architecture automatically,
ensuring better performance and compatibility.
2024-09-05 12:09:15 +02:00
Anthony LC
dd1b271b71 🌐(frontend) translate last features
Translate:
- doc versions
- doc summary
- export docx
2024-09-05 09:30:56 +02:00
Anthony LC
7cfc1d8036 ⬆️(i18n) i18next-parser to 9.0.2
i18next-parser had a compatibility issue with
a dependency (cheerio). The last version
fixed this issue, plus fixed another issue
about a configuration problem.
We can now remove it from the renovate ignore list.
2024-09-05 09:30:56 +02:00
Anthony LC
86fdbeacaa 🔥(frontend) do not display feature version
A bug was found in the version feature.
A 404 error appears sometimes, probably because
of Minio that does not keep enough versions.
We want to do a realease, so we will remove the
version feature for now.
2024-09-05 09:30:56 +02:00
Anthony LC
520d511f59 🔧(project) replace webrtc by yProvider
Replace webrtc by yProvider the project
(docker, helm chart, etc).
2024-09-04 21:10:24 +02:00
Anthony LC
9c512fae69 ♻️(y-provider) replace y-webrtc-signaling by server-y-provider
We replace the y-webrtc-signaling app by
the server-y-provider server.
The server-y-provider server uses @hocuspocus to
do collaborative editing on docs.
2024-09-04 21:10:24 +02:00
Anthony LC
1139c0abea ♻️(frontend) replace y-webrtc by @hocuspocus
y-webrtc had some issues, users had difficulties
to connect with each others.
We replace it by @hocuspocus/provider.
2024-09-04 21:10:24 +02:00
Anthony LC
9e1979f637 🐛(docker) add emoji font
In order to have the emoji font available in
the container, we need to install it.
The font will be then available in the
PDF export.
2024-09-03 17:37:56 +02:00
Anthony LC
ddd93ab0c5 🐛(frontend) close panel when unmount
When the panel is unmounted, the summary and
version panel should be closed.
2024-09-03 17:37:56 +02:00
Anthony LC
85044fd665 (frontend) summary feature
Add the summary feature to the doc.
We will be able to access part of the doc quickly
from the summary.
2024-09-03 15:55:25 +02:00
Anthony LC
b83875fc97 ♻️(frontend) move Panel component
We will have multiple Panel components in the future,
so we move it to the root of the components folder.
We refacto the Version Panel to use the new
Panel component.
2024-09-03 15:55:25 +02:00
Anthony LC
7a8caf5475 🐛(backend) compatibility issue with django and easy_thumbnails
There is a compatibility issue between django 5.1
and easy_thumbnails 2.9.
This commit fixes the issue.
2024-09-03 11:36:50 +02:00
renovate[bot]
e927f2c004 ⬆️(dependencies) update python dependencies 2024-09-03 11:36:50 +02:00
Anthony LC
7f25b05474 ⚗️(frontend) add button Restore this version near title
When a user is on a page version, we will display
a button "Restore this version" near the title of
the page. It gives an obvious way to restore the
version of the doc.
2024-09-02 17:02:23 +02:00
Anthony LC
296b5dbf59 ♻️(frontend) add modal confirmation restore version
Add modal confirmation restore version explaining
that the current version will be replaced
by the selected version, and that some data
may be lost.
2024-09-02 17:02:23 +02:00
Anthony LC
accbda44e2 ♻️(frontend) open version panel from docs options
Versions panel is a feature that will not be used
by all users, so it should be hidden
by default. The user can open it from the docs
options.
2024-09-02 17:02:23 +02:00
Anthony LC
f2a78ada47 🔧(helm) replace storage url in ingressMedia
There is no mechanism to have the media storage
URL from a secret from the ingress.
The media storage URL has to be hardcoded.
We replace the media storage URL in the ingress,
if we change the cluster, we will have to update
these urls.
2024-09-02 12:17:40 +02:00
renovate[bot]
4cb0423511 ⬆️(dependencies) update js dependencies 2024-09-02 09:50:21 +02:00
Anthony LC
766aee6a92 💄(frontend) code background darkened on editor
The "code" was not visible on the editor
because the background was too light.
The background color was darkened to make the
"code" more visible.
2024-08-30 15:58:25 +02:00
Anthony LC
3d19893091 🔥(frontend) remove saving modal
The saving toast are removed.
Users were complaining about the toast
that was shown when saving a document.
2024-08-30 15:43:48 +02:00
Anthony LC
00b223f648 🔥(frontend) hide markdown button if not text
If we are selected a block that is not a text block,
we hide the markdown button.
2024-08-30 15:43:48 +02:00
Anthony LC
38b32c1227 ️(tilt) stop kind-registry to restart when stopped
To save resources, it is nive to be able to stop
the kind-registry when it is not needed.
This commit allow us to stop it.
2024-08-29 18:31:26 +02:00
Anthony LC
1ff3d9c54e 🧑‍💻(ngnix) add conf ngnix to proxy media url
In development mode with docker-compose, we need to
configure Nginx to proxy requests to the Minio server.
Before to proxy to Minio, we need to
authenticate the request, so we proxy to the
Django server first to fill the request with the
necessary headers, then we proxy to Minio.
2024-08-29 18:31:26 +02:00
Anthony LC
6eff21f51e (frontend) add upload to the doc editor
We can now upload images to the doc editor.
The image is uploaded to the server
and the URL is inserted into the editor.
2024-08-29 18:31:26 +02:00
Anthony LC
3eb8f88b5c 👔(frontend) integrate attachment-upload endpoint
Integrate the `documents/${docId}/attachment-upload/`
endpoint. This endpoint is used to upload attachments
to a document.
To have automatically the good content-type form-data,
the `fetchApi` function has been updated to remove the
prefill `Content-Type` header.
2024-08-29 18:31:26 +02:00
renovate[bot]
3a3483b776 ⬆️(dependencies) update js dependencies 2024-08-29 14:11:11 +02:00
Samuel Paccoud - DINUM
67a20f249e (backend) add url to download media attachments with access rights
We make use of nginx subrequests to block media file downloads while
we check for access rights. The request is then proxied to the object
storage engine and authorization is added via the "Authorization"
header. This way the media urls are static and can be stored in the
document's json content without compromising on security: access
control is done on all requests based on the user cookie session.
2024-08-27 15:59:44 +02:00
Samuel Paccoud - DINUM
c9f1356d3e (backend) allow uploading images as attachments to a document
We only rely on S3 to store attachments for a document. Nothing
is persisted in the database as the image media urls will be
stored in the document json.
2024-08-27 15:59:44 +02:00
Samuel Paccoud - DINUM
f12708acee ⬆️(backend) upgrade boto3 to 1.14.4 for unsigned urls
For media urls, we want to compute authorization as a header
instead of computing signed urls.

The url of a media file can then be computed without the
querystring authorization part. This requires upgrading
django-storages to the 1.14 version to benefit from the
"unsigned connection" in the S3Storage backend.
2024-08-27 15:59:44 +02:00
Anthony LC
58eaea000c 🔖(patch) release 1.2.1
Changed:
- ♻️ Change ordering docs datagrid
- 🔥(helm) use scaleway email
2024-08-23 16:27:52 +02:00
Anthony LC
7d97a037f6 🧑‍💻(makefile) bump-packages-version
Create the command bump-packages-version in Makefile.
This command will bump the version of
all the javascript packages in the project.
2024-08-23 16:27:52 +02:00
Anthony LC
c830b4dae6 ♻️(email) replace base64 image with a link
The emails were too big, gmail by example was not
able to display them correctly.
It was caused by base64 image, so they are
replaced with a link to the image.

We fixed the link to the website, it will improve
the score of the email.
2024-08-23 15:37:01 +02:00
Anthony LC
a0fe98e156 🔥(helm) configure staging to use scaleway email
Change staging configuration to use scaleway transactionnal email
2024-08-23 15:37:01 +02:00
Anthony LC
fa105e5b54 📌(e2e) pin pdf-parse to 1.1.1
pdf-parse was not pinned to a specific version.
This could lead to unexpected behavior
if the package is updated.
This change pins pdf-parse to version 1.1.1.
2024-08-23 14:29:52 +02:00
Anthony LC
ced850aecf ♻️(frontend) datagrid ordered by updated_at desc
The datagrid is now ordered by updated_at desc.
2024-08-23 14:29:52 +02:00
Anthony LC
3a420c0416 ♻️(backend) document list order by updated_at desc
Document list is now ordered by updated_at in
descending order.
Test cases were improved as well.
2024-08-23 14:29:52 +02:00
Anthony LC
b5a67df88b 🔖(minor) minor release to 1.2.0
Added:
- 🎨(frontend) better conversion editor to pdf
- Export docx (word)
- 🌐Internationalize invitation email
- (frontend) White branding
- Email invitation when add user to doc
- Invitation management

Fixed:
- 🐛(y-webrtc) fix prob connection
- ️(frontend) improve select share stability
- 🐛(backend) enable SSL when sending email

Changed:
- 🎨(frontend) stop limit layout height to screen size
- ️(CI) only e2e chrome mandatory

Removed:
- 🔥(helm) remove htaccess
2024-08-22 13:39:18 +02:00
Anthony LC
6683821eaf 🔥(frontend) do not display feature version
We want to improve the version feature before
releasing it. We will not display it for now.
2024-08-22 12:33:52 +02:00
Anthony LC
9c67c9e4d4 🔥(helm) remove htaccess
We will not use htaccess anymore,
so we can remove it from the project.
2024-08-21 16:53:38 +02:00
Anthony LC
543770ae63 📝(project) update release document
Update release document.
2024-08-21 16:53:38 +02:00
Anthony LC
f0e2a2b710 ♻️(backend) automatic delete temporary files
To leverage the automatic deletion of temporary
files, we do the conversion inside the with context.
Even if the conversion fails, the temporary file
will be deleted.
2024-08-21 15:27:31 +02:00
Anthony LC
67625dff7a ⬇️(backend) downgrade django-storages[s3] to 1.14.2
Downgrade django-storages[s3] from 1.14.4 to 1.14.2.
It seems to have an issue with our setup.
"default_storage.exists(file_key)" is returning
False when we save a document even if the file
exists in the S3 bucket.
2024-08-20 18:06:02 +02:00
Anthony LC
4ed964240f (frontend) add @typescript-eslint/parser
In order to work correctly
@typescript-eslint/eslint-plugin requires
@typescript-eslint/parser to be installed as well.
We added @typescript-eslint/parser, and
upgraded @typescript-eslint/eslint-plugin to 8.1.0.
We fixed the linting issues related to the
upgrade.
2024-08-20 18:06:02 +02:00
Anthony LC
a970a83229 🚨(backend) fix linting issues after upgrading
The last upgrades introduced some linting issues.
This commit fixes them.
2024-08-20 18:06:02 +02:00
Anthony LC
7babc46261 ⬇️(backend) downgrade django to 5.0.8
Downgrade django from 5.1 to 5.0.8.
There is a compatibility issue with easy_thumbnails,
which is not yet compatible with Django 5.1.
2024-08-20 18:06:02 +02:00
renovate[bot]
2af88c5a4d ⬆️(dependencies) update python dependencies 2024-08-20 18:06:02 +02:00
Anthony LC
07d9e290fa (backend) adapt test to djangorestframework 3.15.2
A recent update of the djangorestframework changes
the detail message of the 404 error.
We update the tests to match the new message.
2024-08-19 17:20:52 +02:00
renovate[bot]
29c5199b72 ⬆️(dependencies) update djangorestframework to v3.15.2 [SECURITY] 2024-08-19 17:20:52 +02:00
Anthony LC
98b3fc4a1b ⬇️(frontend) downgrade @typescript-eslint/eslint-plugin to 7.13.1
@typescript-eslint/eslint-plugin released the
version 8, but it is causing some issues
(@typescript-eslint/no-duplicate-enum-values).
We downgrade it to 7.13.1 in waiting for a fix.
2024-08-19 16:53:17 +02:00
renovate[bot]
754c5d06f4 ⬆️(dependencies) update js dependencies 2024-08-19 16:53:17 +02:00
Anthony LC
fe00d65500 🗑️(frontend) clean member feature
Clean a bit member feature, remove unused files
and refactor some code.
2024-08-19 16:32:46 +02:00
Anthony LC
5ef0f825e0 (frontend) invitation list
- Display the list of invitations for a document
in the share modal.
- We can now cancel an invitation.
- We can now update the role of a invited user.
2024-08-19 16:32:46 +02:00
Anthony LC
3e5dae4ff1 🛂(backend) can update role invitation
Allow to update role invitation if owner or admin.
2024-08-19 16:32:46 +02:00
Anthony LC
affe3be937 ️(CI) only e2e chrome mandatory
To speed up pull request flow, put e2e tests
only mandatory for Chrome.
We still have tests for Firefox and Webkit,
but they are not mandatory.
They will still have to be checked regularly,
particularly during the deployment phase.
2024-08-19 15:51:44 +02:00
Anthony LC
0512af273c 🛂(frontend) redirect to correct url after login
If a user wanted to access a doc but was not logged in,
they would be redirected to the login page.
After logging in, they would be redirected to the home page.
This change makes it so that they are
redirected to the doc they originally wanted to access.

Usefull from the mail sent to the user to access the doc
they were invited to.
2024-08-16 15:17:27 +02:00
Anthony LC
a925b0bedf 🚨(backend) fix linter warning too many lines
The linter was complaining about too many lines
in test_api_document_accesses.py. We split the
test file into two files to fix the warning.
We move as well the test_api_document tests to
the documents folder.
2024-08-16 15:17:27 +02:00
Anthony LC
143f1a02eb 🌐(frontend) add Content-Language on doc access endpoint
We send an internationalized email from
the POST /api/docs/:docId/access endpoint.
We add the language of the website with the
Content-Language header.
2024-08-16 15:17:27 +02:00
Anthony LC
1abbf0539f (backend) send email invitation when add user to doc
We send as well an email invitation to the user
when we add him to a document.
2024-08-16 15:17:27 +02:00
Anthony LC
2f8c5637f4 ♻️(backend) refacto email invitation
Remove email invitation from Invitation model
to be able to use it in other context.
We add it in utils.py instead, and it will be called
from the viewset.
We add the document_id to link to the document from
the mail.
2024-08-16 15:17:27 +02:00
Anthony LC
2391098aba 🔧(docker) frontend env build
Make the environment variables configurable
for the frontend app in the Dockerfile by setting
the arg variables.
2024-08-15 15:25:09 +02:00
Anthony LC
6fdf912d5d 🎨(frontend) get the brand logo from the theme
We want to get the brand logo from the theme.
This way, we will be able to change the
logo easily just by switching to another theme.
2024-08-15 15:25:09 +02:00
Anthony LC
c62a0afdf6 🔧(frontend) theme from env var
We want to be able to brand our website (theme and logo)
at build time, we use the dsfr theme but
the german will use their own theme with their
own logo.
We will use a env var to set the theme,
depend the environment we will be able to
use different theme at build time.
2024-08-15 15:25:09 +02:00
Anthony LC
d73e42b20b 🔥(crowdin) remove crowdin from CI workflow
Pushing to crowdin from the workflow has some side
effects, if 2 branches are pushing to crowdin it
can cause conflicts and delete translations on
Crowdin side.
Better to push to crowdin manually to keep good
control over the translations.
2024-08-15 12:06:17 +02:00
Anthony LC
5d98986402 🌐(i18n) translate the invitation email
Translate the invitation email.
Generate the .po and .mo files thanks to Crowdin.
2024-08-15 12:06:17 +02:00
Anthony LC
c7f7b0f7ad 🌐(frontend) add Content-Language on invitation endpoint
We want to adapt the email language depend the website
choosen language. We sent the language in the request
header, the backend will use this information to send
the email in the correct language.
2024-08-15 12:06:17 +02:00
Anthony LC
41a6ef9dfc 🌐(backend) user language from request Content-Language
We want to adapt the email language depend the website
choosen language. We get the website language
from the request Content-Language header.
We adapt the serializer to set the user language
from the request Content-Language header.
Thanks to that our email will be in the right language.
2024-08-15 12:06:17 +02:00
Anthony LC
ac58341984 (e2e) increase timeout doc export test
We have a test asserting lot of things on the doc
export, and it's failing on CI because of the
timeout.
We increase the timeout to make it pass.
2024-08-14 11:45:21 +02:00
Anthony LC
990de437ba 🚚(project) move scripts to bin
bin and scripts were similar folders,
we will move all scripts to bin folder
and remove scripts folder.
2024-08-14 11:45:21 +02:00
Anthony LC
b4d3f94ada 📝(project) add release document
Add a release document to the project.
It indicates the steps to follow to release
a new version of the project.
2024-08-14 11:45:21 +02:00
Anthony LC
c724ae0253 🔥(project) remove tsclient
We don't need the tsclient for the moment.
Better to remove it.
2024-08-14 11:45:21 +02:00
Anthony LC
e9796404cf 💬(helm) change EMAIL_FROM settings
Change EMAIL_FROM settings to fit our usecase.
2024-08-13 21:42:41 +02:00
Anthony LC
6481ce311d 🐛(backend) enable SSL when sending email
Email settings were wrongly configured. It leed to
unsent email and timeout response from the backend
server. This commit fixes the issue by enabling SSL
when sending email.
2024-08-13 21:42:41 +02:00
Anthony LC
af7e480d52 💬(mail) improve email text
- replace occurences of Impress with Docs in the email
- replace occurences of Impress with Docs in the email
subject
2024-08-13 21:42:41 +02:00
Anthony LC
c1566d98fe ♻️(frontend) export to docx
We can now export the document to docx format.
We adapted the frontend to be able to choose
between pdf or docx export.
2024-08-12 15:46:01 +02:00
Anthony LC
4280f0779e 🗃️(backend) export to docx
We can now export our document to a docx file.
This is done by converting the html to a docx
file using the pypandoc and pandoc library.
We added the "format" param to the
generate-document endpoint, "format" accept
"pdf" or "docx" as value.
2024-08-12 15:46:01 +02:00
Anthony LC
ffaccad014 🍱(project) add gouv logo img
Add a gouv logo image to the public assets folder.
We will be able to access it via the frontend url.
Usefull to get them from our templates on the
backend side.
2024-08-12 15:46:01 +02:00
renovate[bot]
c077ed8414 ⬆️(dependencies) update django to v5.0.8 [SECURITY] 2024-08-12 11:49:41 +02:00
Jacques ROUSSEL
6a13c5d4e1 🔐(secret) add spaccoud age key
Add spaccoud age key
2024-08-09 13:12:13 +02:00
Anthony LC
c42fd9be9c 🌐(frontend) add translations
Add french translations.
2024-08-06 16:23:07 +02:00
Anthony LC
2b824c0862 ️(service-worker) manage document versioning
- Cache correctly the document version to work
in offline mode.
- Restoring a version is possible even if offline.
- Better sync triggering.
2024-08-06 16:23:07 +02:00
Anthony LC
fb494c8c71 ️(frontend) improve select share stability
- keep email in search input after unfocus
- keep search in memory after unfocus
- fixed width to reduce flickering
- empty states after validation
2024-08-06 16:23:07 +02:00
Anthony LC
8d5648005f 🎨(frontend) stop limit layout height to screen size
The app was limiting the layout height to the screen size,
which was a bit annoying when the content was
bigger than the screen.
We stop doing that, and now the layout
will grow as needed.
2024-08-06 12:29:06 +02:00
Anthony LC
713d9e48c8 🐛(y-webrtc) fix prob connection
Sometimes the connection was not established correctly,
because multiple connections were created at the
same time. This commit fixes this issue by ensuring
that only one connection is created at a time.
2024-08-06 09:28:12 +02:00
Anthony LC
bef541f956 🧑‍💻(y-webrtc) use correctly the conf nodemon
The conf was not used correctly, the nodemon.json
was not used.
This commit fix this issue.
2024-08-06 09:28:12 +02:00
Anthony LC
4b61ffce01 🗃️(frontend) replace main version per another version
We can now replace the main version by another version.
Usefull either to come back to a previous version
or to update the main version with a new one.
2024-08-06 09:28:12 +02:00
Anthony LC
a9383212a3 (frontend) feature versioning
We can now see the version of the document
and navigate to different versions.
Collaboration is possible per version.
2024-08-06 09:28:12 +02:00
Anthony LC
1ed20c3896 🌐(frontend) make empty doc grid message translatable
We personalize the empty doc grid message and
make it translatable.
2024-08-06 09:28:12 +02:00
Anthony LC
b5443676c1 ♻️(frontend) replace setEditor per setStore
setStore is more generic and can be used to set
any store.
2024-08-06 09:28:12 +02:00
Anthony LC
424c100eeb 👔(frontend) create versions api endpoints
Create versions api endpoints:
- Add useDocVersion hook - to retrieve a version
- Add useDocVersions hook - to list versions with
pagination

We add an helper to type more easily the react-query
hooks.
2024-08-06 09:28:12 +02:00
Anthony LC
91be4f5a21 👔(backend) add document version serializer
Add document version serializer to get the pagination
with the document version list.
2024-08-06 09:28:12 +02:00
Anthony LC
6eb0ac99e4 🎨(frontend) better conversion editor to pdf
A recent update from Blocknote provides us the
alignment, the color and the background color of
the different editor texts. We adapt our converter
to adapt these new features to the pdf.
2024-08-02 17:34:02 +02:00
renovate[bot]
e79a74083a ⬆️(dependencies) update sentry-sdk to v2 [SECURITY] 2024-08-02 10:21:13 +02:00
Anthony LC
905000550d ⬇️(frontend) downgrade @typescript-eslint/eslint-plugin to 7.13.1
@typescript-eslint/eslint-plugin released the
version 8, but it is causing some issues
(@typescript-eslint/no-duplicate-enum-values).
We downgrade it to 7.13.1 in waiting for a fix.
2024-08-01 10:52:01 +02:00
renovate[bot]
76d623a89b ⬆️(dependencies) update js dependencies 2024-08-01 10:52:01 +02:00
Anthony LC
61cf8aae74 🔖(minor) minor release to 1.1.0
Added:
- 🤡(demo) generate dummy documents on dev users
- (frontend) create side modal component
- (frontend) Doc grid actions (update / delete)
- (frontend) Doc editor header information

Changed:
- ♻️(frontend) replace docs panel with docs grid
- ♻️(frontend) create a doc from a modal
- ♻️(frontend) manage members from the share modal
2024-07-15 18:13:28 +02:00
renovate[bot]
ccadd9567a ⬆️(dependencies) update django to v5.0.7 [SECURITY] 2024-07-15 17:24:18 +02:00
Anthony LC
2c9758eb30 🌐(frontend) translate last features
Translate:
- doc datagrid
- share feature
- remove translations not used anymore
2024-07-15 17:01:09 +02:00
Anthony LC
0f71a3fcfa 💄(frontend) force light theme on BlockNote editor
The BlockNote editor theme was not being forced to
light mode. Depend the user theme preference,
the blocknote theme could be dark.
We force it to light mode.
2024-07-15 10:58:44 +02:00
Anthony LC
839b78a6d0 📱(frontend) improve responsive share modal
Improved the responsiveness of the share modal
on small devices.
2024-07-15 10:58:44 +02:00
Anthony LC
69f2641159 ♻️(frontend) list members from side modal
We refactorize the members grid to display
it inside the share side modal.
It is not a member grid anymore but a member list
with infinite scroll. We can directly update the
role or delete a member from the each row of the
list.
2024-07-12 15:41:32 +02:00
Anthony LC
e5de5a4345 💄(frontend) create IconBG component
Create IconBG component, a premade Icon component
with a background color.
2024-07-12 15:41:32 +02:00
Anthony LC
c2d6e60ae8 ♻️(frontend) add user from side modal
We move the add user functionality to a side modal.
The side modal is opened from the share button.
2024-07-12 15:41:32 +02:00
Anthony LC
ff832239d3 (frontend) add doc editor panel header
Add doc editor panel header to show
the doc title and other info.
2024-07-10 14:11:27 +02:00
Anthony LC
a60399883b 🏷️(frontend) improve props currentDocRole
props of currentDocRole is now more accurate.
2024-07-10 14:11:27 +02:00
Anthony LC
12d32fe933 🛂(frontend) don't show action buttons
Don't show actions buttons if the user
doesn't have permission to edit or delete
the document.
2024-07-10 14:11:27 +02:00
Anthony LC
038d868d29 (frontend) create hook useDate
Create hook useDate, it is an date helper.
We use it for now to format date.
2024-07-10 14:11:27 +02:00
Anthony LC
bfde526361 🐛(i18n) fix key that contain colon
Keys that contains colon where not being
translated correctly. This was due to the
colon being used as a separator for the
key and the value. This was fixed by
replacing the colon with a different
character that is not used in the key
or the value.
2024-07-10 14:11:27 +02:00
Anthony LC
132da0837f 🐛(e2e) sync doc grid sorting check
The sorting check was not the same between
what django provide and what the e2e test
check, it was giving some flakiness.
Django seems to ignore the punctuation (space)
in its sorting.
We improve other test to be more robust
as well.
2024-07-09 14:14:27 +02:00
Anthony LC
164ed885ca 🎨(frontend) improve interface SideModal
Improve the interface of the SideModal
component.
Set the width and the side by default.
2024-07-09 14:14:27 +02:00
Anthony LC
f5a35c66bb (frontend) add doc grid actions button
Add document action buttons to the document grid:
- update document
- delete document
2024-07-09 14:14:27 +02:00
Anthony LC
8100422776 📝(github-actions) rename workflows
Change name workflows.
2024-07-09 14:14:27 +02:00
Anthony LC
f48e385d3d 🛂(docker) add user minio
Add a user on Minio container in docker-compose.
2024-07-09 14:14:27 +02:00
Anthony LC
b5895bf297 ⬆️(frontend) fix blocknote conflicts
Different versions of blocknote
were installed, we pint the same version
in all packages.json.
2024-07-08 17:10:54 +02:00
Anthony LC
0fb0c71cfa ⬆️(mail) bump mail dependencies
Lot of dependencies were outdated, so
we updated them.
2024-07-08 17:10:54 +02:00
Anthony LC
a0fea64630 (frontend) create side modal component
Create SideModal component for
displaying modal on the side of the screen.
This component is build above the Cunningham
component, so all the cunnighama props are
still available.
2024-07-08 16:52:22 +02:00
Anthony LC
de922e1c04 ♻️(frontend) create a doc from a modal
We refacto the create doc feature to use a modal
instead of a page and a card component.
It is more consistent with the other features.
2024-07-08 15:04:35 +02:00
Anthony LC
8007c45a35 👔(service-worker) add new field to create doc
We now display the creation and modification date
of the document in the document grid, so when we
create a new document in offline mode we need to
set the dates as well.
2024-07-08 13:41:08 +02:00
Anthony LC
3d370e5714 💄(frontend) darken the background as the mockup
Darken the background as the mockup to make the
elements more visible.
2024-07-05 19:02:01 +02:00
Anthony LC
03953f0fe7 ️(frontend) use resetQueries instead of invalidateQueries
With the list of documents, invalidateQueries doesn't
refresh as expected the list of documents. We
will prefer resetQueries, it seems to be more
appropriate for this case.
2024-07-05 19:02:01 +02:00
Anthony LC
67f4ddeef7 (e2e) adapt e2e test without the panel
The datagrid replace the panel. Lot of tests
need to be adapted to this new architecture.
2024-07-05 19:02:01 +02:00
Anthony LC
6f4b4bb7d3 🔥(frontend) remove docs-panel feature
Remove docs-panel feature, we will replace it
with a datagrid.
2024-07-05 19:02:01 +02:00
Anthony LC
c27e6c1b06 (frontend) create docs-grid feature
Create docs-grid feature. It will be used to display
the list of documents in a grid view.
Grid view are more useful to display lot of
information, we can easily sort the information.
2024-07-05 19:02:01 +02:00
Anthony LC
d2888d0b9d (frontend) create hook useTransRole
Create the hook useTransRole to get a translated
role.
2024-07-05 19:02:01 +02:00
Anthony LC
6b8af1f9ec ♻️(backend) add more doc sorting
Update the viewset to be able to sort by:
- created date
- updated date
- title
2024-07-05 19:02:01 +02:00
Anthony LC
35852dff0b ♻️(backend) add more info to doc
Update the serializer to include more info
about the doc:
- created date
- updated date
2024-07-05 19:02:01 +02:00
Anthony LC
be93598b2d 🌱(demo) create dev users and make them doc accesses
To be able to test with dummy data, we need to create
our dev users from the demo and to give them access to
the docs.
The sub is the unicity of the user for our oidc provider,
so we need to know the sub to be able to create
correctly the user, it is why we set the sub
as the email of the user in the realm.json file.
2024-07-05 19:02:01 +02:00
Anthony LC
7d3fd25c61 🤡(demo) demo generate dummy documents
The demo command will generate dummy documents
and dummy accesses.
2024-07-05 19:02:01 +02:00
Anthony LC
e3fe647e5b 🔖(major) major release to 1.0.0
Added:
- 🛂(frontend) Manage the document's right (#75)
- (frontend) Update document (#68)
- (frontend) Remove document (#68)
- 🐳(docker) dockerize dev frontend (#63)
- 👔(backend) list users with email filtering (#79)
- (frontend) add user to a document (#52)
- (frontend) invite user to a document (#52)
- 🛂(frontend) manage members (update role / list / remove) (#81)
- (frontend) offline mode (#88)
- 🌐(frontend) translate cgu (#83)
- (service-worker) offline doc management (#94)
- ⚗️(frontend) Add beta tag on logo (#121)

Changed:
- ♻️(frontend) Change site from Impress to Docs (#76)
- (frontend) Generate PDF from a modal (#68)
- 🔧(helm) sticky session by request_uri for signaling server (#78)
- ♻️(frontend) change logo (#84)
- ♻️(frontend) pdf has title doc (#84)
- ️(e2e) unique login between tests (#80)
- ️(CI) improve e2e job (#86)
- ♻️(frontend) improve the error and message info ui (#93)
- ✏️(frontend) change all occurences of pad to doc (#99)

Fixed:
- 🐛(frontend) Fix the break line when generate PDF (#84)

Delete:
- 💚(CI) Remove trigger workflow on push tags on CI (#68)
- 🔥(frontend) Remove coming soon page (#121)
2024-07-03 17:08:59 +02:00
Anthony LC
8f5c413482 ️(e2e) remove unnecessary page.goto
Multiple page.goto can cause flakiness.
An page.goto is already called in the beforeEach,
so we don't need to call it again in the test.
2024-07-03 14:55:54 +02:00
Anthony LC
a66231d72e 🗃️(service-worker) cache successfull update in doc-item
When we updated the doc successfully then directly
pass offline, the cache was not updated.
It is because a successful update didn't
update the cache of the doc-item.
2024-07-03 11:49:53 +02:00
Anthony LC
2e0b6b2a2a ♻️(service-worker) admin NetworkOnly
We will use the NetworkOnly strategy
for the admin part.
2024-07-03 10:03:15 +02:00
Anthony LC
edffbbaf99 🐛(frontend) fix issue when no template
A bug occured when no template was
available. This commit fixes this issue.
2024-07-03 10:03:15 +02:00
Anthony LC
a393cd4df0 ♻️(service-worker) external url NetworkFirst
We will use the NetworkFirst strategy
for external URLs, if a patch is made from
the external resource, the resource will be
updated directly.
2024-07-03 10:03:15 +02:00
Anthony LC
e26b806965 🐛(service-worker) networkFirst on api request
Some api requests were served by the cache first,
which caused the data to be outdated.
This change makes sure that the api requests
are always served by the network first.
2024-07-03 10:03:15 +02:00
Jacques ROUSSEL
aef19cfddd 🔧(helm) manage htaccess
- add htaccess
- fix issue with kind
2024-07-03 09:22:31 +02:00
Anthony LC
b7bd7b7772 ⚗️(frontend) add Beta badge to header
We add a beta badge to the header to indicate
that the app is in beta.
2024-07-02 10:52:15 +02:00
Anthony LC
43ba36af61 🔥(frontend) remove coming-soon
Remove the coming-soon page and the redirection to it.
2024-07-02 10:52:15 +02:00
Samuel Paccoud - DINUM
b4e4ba3516 ♻️(documents) store document file in object storage in a folder
We will need to store more than a file for a document: multiple languages,
images, etc. For this, the document ID should be a folder and the content
a file in this folder.
2024-07-01 22:32:00 +02:00
Anthony LC
9c19b22a66 ♻️(service-worker) create useSWRegister hook
Create useSWRegister hook, which will be used to
register the service worker in the app.
2024-06-28 11:07:21 +02:00
Anthony LC
d87c278cce 🚚(service-worker) move service worker to features
The service worker is now in the features folder.
It is more a feature than a core part of the app.
2024-06-28 11:07:21 +02:00
Anthony LC
287a5b3bff 🔧(service-worker) add conf file with dev urls
Add a conf files to the service worker to store
the development urls. If someone wants to change
the urls, they can do it in the conf file, it is
more intuitive than changing the urls in the
service worker file.
2024-06-28 11:07:21 +02:00
Anthony LC
cfb979a411 (service-worker) add unit tests
Add unit tests for:
- ApiPlugin
- RequestSerializer
- SyncManager
2024-06-28 11:07:21 +02:00
Anthony LC
d588ae847f 🐛(frontend) change saving behavior firefox
Firefox does not trigger the request everytime the
user leaves the page. Plus the request is not
intercepted by the service worker.
So we prevent the default behavior to have
the popup asking the user if he wants to leave the
page, by adding the popup, we let the time to the
request to be sent, and intercepted by the
service worker (for the offline part).

We also add a toast to inform the user that the
document has been saved, it will make it more obvious
that the document has been saved if a firefox user
come back from the popup to the page.
2024-06-28 11:07:21 +02:00
Anthony LC
08a39c7ebd 🔧(frontend) add a buildId to service worker
We add a buildId to the service worker to force
the browser to update the service worker when
the buildId changes.
Before we were on the tag version, but
the browser was not updating the service worker
easily on the staging environment.
2024-06-28 11:07:21 +02:00
Anthony LC
aef5dd51fc ♻️(backend) override document perform_create
We override the perform_create method of
the DocumentViewSet to save the document with
the id provided if a id is provided in the request.
We do that because in offline mode we will create
the document locally and we will need to save it
with the id created locally to have our next
requests to the server to be able to find the
document with the id provided.
2024-06-28 11:07:21 +02:00
Anthony LC
bf10fbd25f (service-worker) add offline docs delete
Add offline docs delete to the service worker.
We use the Network fisrt strategy, if
the network is down, we will delete the
doc in the indexDB.
When the connection is back, we will remove
the doc to the server.
2024-06-28 11:07:21 +02:00
Anthony LC
f5948959f2 (service-worker) add offline docs create
Add offline docs create to the service worker.
We use the Network fisrt strategy, if
the network is down, we will create the
doc in the indexDB and serve it from there.
When the connection is back, we will send
the doc to the server.
2024-06-28 11:07:21 +02:00
Anthony LC
a0644efb45 ♻️(service-worker) add DocsDB to store docs in indexedDB
Export the storage of docs to a separate
class `DocsDB` that will be responsible for
storing / retrieving / synching docs from indexedDB.
2024-06-28 11:07:21 +02:00
Anthony LC
b4e8db9050 (service-worker) add offline docs update
Add offline docs update to the service worker.
We use the Network fisrt strategy, if
the network is down, we will update the
doc in the indexDB and serve it from there.
When the connection is back, we will send
the doc to the server.
2024-06-28 11:07:21 +02:00
Anthony LC
96bfbfabec (service-worker) add service worker api to impress app
This commit adds the service worker api to the
impress app.
The service worker api will cache the api calls
in the indexdb. We are using the network first
strategy to fetch the data. If the network is
not available, we will fetch the data from the
indexdb.
To do that, we create a custom plugin (ApiPlugin).
2024-06-28 11:07:21 +02:00
Anthony LC
c2aa5be8ff 🧑‍💻(frontend) env var service-worker dev mode
Add a new environment variable to enable or disable
the service worker in development mode.
By default, the service worker is disabled
in development mode, it can cause problems
when developing the application with the hmr.
It can creates useless log in the console
as well.
We can easily enable it by setting
NEXT_PUBLIC_SW_DEACTIVATED to false in the
.env.development file.
We will use this new var to not use the service
worder with the CI as well.
2024-06-28 11:07:21 +02:00
Anthony LC
3b77e1254e 🔥(frontend) remove grapesjs dependencies
Some grapesjs dependencies were still present.
We don't use these anymore, so we can remove them.
2024-06-27 12:42:52 +02:00
Anthony LC
b436a418ff ⬆️(frontend) upgrade react to 18.3.1
A recent upgrade of next.js fixed the compatibility
issue with react 18.3.1.
2024-06-27 12:42:52 +02:00
Anthony LC
9fdf87b100 ⬇️(frontend) downgrading fetch-mock to 9.11.0
fetch-mock released the version 10, but it is
causing some issues with jest because of
esm support.
2024-06-27 12:42:52 +02:00
renovate[bot]
6943f2158a ⬆️(dependencies) update js dependencies 2024-06-27 12:42:52 +02:00
renovate[bot]
872cc8c110 ⬆️(dependencies) update requests to v2.32.2 [SECURITY] 2024-06-27 11:04:34 +02:00
Anthony LC
c03e1ffe8b 💄(frontend) change docs logo
Change the docs logo to a more accurate one.
2024-06-25 15:38:40 +02:00
Anthony LC
5454da5c70 ✏️(frontend) change all occurences of pad to doc
The initial doc was named pad in reference to
the "NotePad de l'Etat", but we renamed it
doc to clearly dissociate the 2 sites.
Doc is also a small reference of google doc.
2024-06-25 15:07:36 +02:00
Anthony LC
5b3c91f6dd 🌐(app-impress) translate impress in french
Add french translation to impress.
2024-06-25 13:47:56 +02:00
Anthony LC
f1ac0954ed 🧑‍💻(i18n) run automatically prettier after import
After an import the translation file is now
automatically formatted with prettier.
2024-06-25 13:47:56 +02:00
Anthony LC
fc80c2ec2a 🌐(pages) translate legal pages to english
They should be translated to French using the i18n tooling and not
directly in the code.
2024-06-25 13:47:56 +02:00
renovate[bot]
8ba64322be ⬆️(dependencies) update ws to v8.17.1 [SECURITY] 2024-06-24 12:29:30 +02:00
Anthony LC
ae9555284d (e2e) remove some flakiness
Service worker added some flakiness, we
activate the sw only if not from the CI.
We add as well the email of the user added in a
doc to the toast message. This way we can check
if the user was added correctly, this will remove
some flakiness in some tests.
2024-06-14 12:26:04 +02:00
Anthony LC
14a91a5698 🐛(frontend) mismatch between yjs dependency
Mismatch between yjs dependency, we set the
dependency globally to not have this problem
anymore.
2024-06-14 12:26:04 +02:00
Anthony LC
9f6dac53d4 (frontend) add offline mode support
Add a offline mode support from a service worker.
The service worker will cache the assets and the
visited pages to be able to work offline.
Created a fallback page for when the user is offline
and tries to access a page that is not cached.
2024-06-14 12:26:04 +02:00
Anthony LC
5ba35dbc1d ♻️(frontend) improve the ui error and message info ui
Improve the ui error and message info ui:
- Can use a icon in TextErrors component
- use mode the Alert component to display message info
2024-06-14 11:50:13 +02:00
Anthony LC
62098ec753 🐛(frontend) fix bug multi ws connection
In collaborative mode, only the first document
connected was being shared. This was due to the
fact that the webrtc server was managing only
one connection at a time. This commit fixes this
bug by managing multiple connections.
2024-06-13 12:20:23 +02:00
Jacques ROUSSEL
9e318f88be 🐛(CI) improve submodule
- remove deplucate declaration
- simplify helmfile
- use symlink
2024-06-11 10:40:39 +02:00
Anthony LC
63b565e720 💄(frontend) max img width with container width
The img could be much bigger than the page width
in a generated pdf. We limit the max img width
to the container width.
2024-06-10 13:28:38 +02:00
Anthony LC
e7fe153a81 🐛(frontend) fix break line on firefox
On firefox with collaborative mode, the last
upgrade of ProseMirror added a new line when
a another user was typing. This commit fixes this
issue.
2024-06-10 13:28:38 +02:00
Anthony LC
8e0850b13e 🐛(app-desk) fix fetchPriority warning
The upgrade to react@18.3.1 has a compatibility
issue with next@14.2.3. It creates a error warning
about the fetchPriority prop. This commit fixes the
issue by downgrading react to 18.2.0 as it was
before the last upgrade.
The next.js team is already aware of the issue and
it will be fixed in the next release.
2024-06-10 13:28:38 +02:00
Anthony LC
8fb18ed186 (frontend) add "@testing-library/dom
Recent update of @testing-library/react requires
@testing-library/dom to be installed as well.
2024-06-10 13:28:38 +02:00
renovate[bot]
bec6ff3d22 ⬆️(dependencies) update js dependencies 2024-06-10 13:28:38 +02:00
Anthony LC
0571aaabce ️(CI) improve docker build cache
Improve the caching by using docker buildx,
it should speed up the build process by using the
cache from the previous build.
2024-06-07 15:01:47 +02:00
Jacques ROUSSEL
e4bed84343 🐛(CI) fix ci issue
- fix get secret in ci
2024-06-07 14:05:51 +02:00
Jacques ROUSSEL
37f02893ed 🐛(CI) purge secret from repository
- Remove *.enc.*
- Adapt helmfile
- Adapt CI
2024-06-07 10:11:19 +02:00
Anthony LC
4e4e2e23e3 ️(e2e) unique login between tests
We will cache the login token in the browser
storage and reuse it between tests.
This will speed up the tests and reduce the
load on the server.
2024-06-06 22:31:43 +02:00
Anthony LC
b68015852c 🔊(changelog) add recents changes
Add some recents changes:
- (frontend) manage members (update role / list / remove)
- (frontend) change logo
- (frontend) pdf has title doc
- Fix the break line when generate PDF
2024-06-06 16:09:07 +02:00
Anthony LC
2f79d03acc ⬇️(frontend) downgrade @testing-library/react@16.0.0
downgrade @testing-library/react@16.0.0 to
@testing-library/react@15.0.7.

Some modules are not available in the latest
version, their documentation is not available yet.
2024-06-06 16:09:07 +02:00
Anthony LC
8762a7bc92 ♻️(frontend) adapt BlockNote to the last version
A recent relase of the BlockNote component
has changed the way it is used. This commit
adapts the BlockNoteEditor and BlockNoteToolbar
components to the new version of the BlockNote.
2024-06-06 16:09:07 +02:00
Anthony LC
589b0eab32 🐛(app-desk) fix fetchPriority warning
The upgrade to react@18.3.1 has a compatibility
issue with next@14.2.3. It creates a error warning
about the fetchPriority prop. This commit fixes the
issue by downgrading react to 18.2.0 as it was
before the last upgrade.
The next.js team is already aware of the issue and
it will be fixed in the next release.
2024-06-06 16:09:07 +02:00
renovate[bot]
44f4a37814 ⬆️(dependencies) update js dependencies 2024-06-06 16:09:07 +02:00
Anthony LC
6c76f7faab ♻️(frontend) improve html for pdf
Blocknote json to html is not perfect, so we need
to improve it to provide a better html for
the pdf generation.
It is the first step to improve the html,
we will continue to improve it in the future.
We have now the break line.
It is e2e tested so if blocknote change the conversion
we will know it.
2024-06-06 12:43:25 +02:00
Anthony LC
201d776d1b ♻️(frontend) doc title as title for pdf export
Our pdf export was named impress-document.pdf,
now it is named after the document title.
We sanitize the title by removing special characters,
replacing spaces with underscores and putting it
in lowercase.
2024-06-06 12:43:25 +02:00
Anthony LC
e7f2317b41 ♻️(backend) simplify the template
For the first version, we will use a simple template.
Only the gouv logo will be displayed.
2024-06-06 12:43:25 +02:00
Anthony LC
206c7f11ae (e2e) flaky saves the doc when we quit pages
Stop to check "saves the doc when we quit pages"
with webkit browser. It is flaky.
2024-06-06 12:43:25 +02:00
Anthony LC
615f92ded3 💄(frontend) change logo with the pad one
Change the logo with the pad one.
2024-06-06 12:43:25 +02:00
Anthony LC
da3ad91283 ♻️(frontend) add api versionning per request
We were using the version of the api from the
.env file, but we could have different versions
of the api in the same app. So we now use the
version from the request.
2024-06-06 12:43:25 +02:00
Anthony LC
197b16c5d0 (frontend) add modal to delete a member
Add modal to delete a member.
2024-06-05 21:50:30 +02:00
Anthony LC
cce0331b68 (frontend) add modal to update role of a member
Add modal to update role of a member.
2024-06-05 21:50:30 +02:00
Anthony LC
380ac0cbcf (frontend) add members grid
Add members grid to display all members
of a doc and their roles.
2024-06-05 21:50:30 +02:00
Anthony LC
1779b7bab4 🏷️(frontend) adapt accesses types
We updated the types of the accesses to
get more information about the accesses.
2024-06-05 21:50:30 +02:00
Anthony LC
7b5698a370 👔(backend) object user on DocumentAccessSerializer
user field was displaying the userid, but we
need to return the user object on the
DocumentAccessSerializer, so we can show the
user email on the frontend.
We add the user_id field in write_only mode, so
we can keep create and update.
2024-06-05 21:50:30 +02:00
Anthony LC
f039b2aa3b 🌐(frontend) legal pages translatable
Makes the legal pages translatable.
2024-06-05 10:37:02 +02:00
Anthony LC
8531e0dd08 🐛(i18n) comma in keys
Having a comma in the keys broke the parser.
This commit allows the comma in keys.
2024-06-05 10:37:02 +02:00
Anthony LC
e2586b1d8e 🚚(frontend) move to members/members-add
Move addUsers to members/members-add.
Rename some of the occurences of user to member.
2024-05-31 11:51:29 +02:00
Anthony LC
327a5f2ed4 🤡(demo) generate dummy user
To search users we need to have some dummy data users.
We add some dummy users when we run the `make demo`
command.
2024-05-31 11:51:29 +02:00
Anthony LC
1e62320803 💚(app-desk) add template mail for e2e
The tests e2e need the mail template to
send the invitation to the user.
We are now caching the mail template instead
of using a artifact, thanks to that we can
build only the mail template if the cache is
invalidated, so we save time and resources.
2024-05-31 11:51:29 +02:00
Anthony LC
ff7341e544 (frontend) improve some tests
Improve some tests to check the states depend
the user's role.
2024-05-31 11:51:29 +02:00
Anthony LC
038c3d6207 🧑‍💻(mail) make commands windows friendly
Make the commands windows friendly.
2024-05-31 11:51:29 +02:00
Anthony LC
2b67d0bd26 (frontend) add or invite users to a doc
Add or invite a user to a doc.
If the user is already in the system,
the user will be added to the doc,
otherwise, the user will be invited to the doc.
2024-05-31 11:51:29 +02:00
Anthony LC
d30ac8ee4e 🐛(CI) fix job check changelog
The job was everytime failing.
This commit fixes the issue.
2024-05-31 10:34:15 +02:00
Anthony LC
c05b98ce0d 🔧(helm) create ingress_ws
The ingress was the same for the frontend, the
backend and the websocket, but the websocket
needs to be handled differently, so we created
a new ingress specifically for the websocket.
2024-05-31 10:34:15 +02:00
Anthony LC
3d594a99d5 🔨(frontend) add room to ws signaling url
Add the room to the ws signaling url so that
the ingress controller can route the request
to the correct pod.
2024-05-31 10:34:15 +02:00
Anthony LC
b47e60d6ec 🔧(helm) sticky session by request_uri
It helps to keep the same pod for the same
request_uri, so that multiple users from the
same room are not dispatched to different pods.
2024-05-31 10:34:15 +02:00
Anthony LC
f40f0d35cb 🧑‍💻(tilt) sync only y-webrtc-signaling app
We were listening all the frontend, but we can
just listen to the y-webrtc-signaling app.
This app doesn't change often moreover.
2024-05-31 10:34:15 +02:00
Anthony LC
f1227ed037 🚨(CI) fix linter backend
The linter in the CI was checking only the
impress directory. We were passing near some lint
issues. Now the linter checks the whole backend
directory.
2024-05-31 10:12:28 +02:00
Anthony LC
dbd4477e9a 👔(backend) change field displayed on users endpoint
Change the field displayed on the users endpoint.
We need the email field to be displayed.
2024-05-31 10:12:28 +02:00
Anthony LC
b4b308bda9 (backend) search users
We need to search users by their email.
For that we will use the trigram similarity algorithm
provided by PostgreSQL. To use it we have to
activate the pg_trgm extension in postgres db.
To query the email we will use the query param
`q`.
We have another query param `document_id`, it is
necessary to exclude the users that have already
access to the document.
2024-05-31 10:12:28 +02:00
Jacques ROUSSEL
2b456c231f 💚(CI) improve changelog ci
Disable changelog when the label is not present
2024-05-30 16:57:45 +02:00
Jacques ROUSSEL
72bb079f10 👷(helm) preprod configuration
This PR adds the preprod configuration
for the helm chart.
2024-05-30 16:57:45 +02:00
Anthony LC
06af320d61 🚀(docker) dockerize frontend dev
Dockerize the frontend development environment
to make it easy to display the frontend
development environment in the browser.

We don't mix it with the command `make run` to
let it kind of optional to run the frontend
in a docker container. We let it optional because
the hot reload doesn't work well in the docker
container. The volume synch make it a bit slower
as well. So, we let the developer decide to run
the frontend in a docker container or not.
2024-05-30 15:42:34 +02:00
Samuel Paccoud - DINUM
926fe37e85 ♻️(models) rename document/template access rights
The "member" access right does not make sense for documents and templates.
What we really need are "editor" and "reader" access rights.
2024-05-29 19:25:46 +02:00
Anthony LC
51325df7d9 💄(frontend) change favicon
Change Next favicon for the Gouvernment one.
2024-05-27 13:51:18 +02:00
Anthony LC
0727fe0273 💬(frontend) change literal occurances of pad to document
- change urls from /pads to /docs
- change part of the litteral occurances of pad to document
2024-05-27 13:51:18 +02:00
Anthony LC
d0d0b44f57 💬(frontend) change site name to Docs
The site name was Impress, we change it to Docs.
2024-05-27 13:51:18 +02:00
Anthony LC
6e46b686ab 💄(frontend) change gouvernement logo
Change the gourvernement logo.
2024-05-27 13:51:18 +02:00
Anthony LC
38eabe2fc8 🛂(frontend) right pad delete
Manage the right on the pad delete.
If a use cannot delete a pad, we will not display
the delete button in the dropdown menu.
2024-05-27 10:20:20 +02:00
Anthony LC
effa77dc10 🛂(frontend) right pad update
Manage the right on the pad update.
If a use cannot update a pad, we will not display
the update button in the dropdown menu.
2024-05-27 10:20:20 +02:00
Anthony LC
d5d374c311 🛂(frontend) right pad editor
Manage the right on the pad editor.
If a use cannot edit a pad, the pad editor will
be read-only. It will not save automatically.
A message will be displayed to the user.
2024-05-27 10:20:20 +02:00
Jacques ROUSSEL
dd33c99532 💚(CI) add git hook
Add git hook to avoid secret leaks
2024-05-24 17:01:25 +02:00
Anthony LC
f4146fbe4e 🚚(frontend) rename pad to pad-editor
Rename the pad feature to pad-editor.
2024-05-24 16:12:45 +02:00
Anthony LC
dce040f4c3 🚚(frontend) move types and pad api hooks
Move types and pad api hooks to pad-management.
2024-05-24 16:12:45 +02:00
Anthony LC
3a68c75feb 🚚(frontend) rename pads-create to pad-management
Rename the pads-create feature to pad-management.
This feature will not only create but gives tools
to update and remove pads as well.
2024-05-24 16:12:45 +02:00
Anthony LC
29577c0419 (frontend) remove document
We can now remove a document from a modal.
2024-05-24 16:12:45 +02:00
Anthony LC
2813b2ca27 (frontend) update document
We can now update a pad from a modal.
2024-05-24 16:12:45 +02:00
Anthony LC
ef2f4d3250 💚(CI) remove trigger workflow on push tags
We were starting the workflow on push tags,
it is needed for the docker-hub workflow,
but the other workflows should not be triggered
on push tags.
2024-05-24 16:12:45 +02:00
Anthony LC
996cea49b4 ♻️(frontend) add PDF generation inside a modal
Refacto the pad tools, we will use modals to handle
the different actions on the pad. We start by
implementing the PDF generation inside a modal.
2024-05-24 16:12:45 +02:00
Anthony LC
eb2936f48b 🔐(helm) add djangoSuperUserEmail secret
To fit with the production environment, we need to add the
djangoSuperUserEmail secret to the staging environment.
2024-05-24 12:47:38 +02:00
Anthony LC
10c53d2a4e 🔖(minor) initial release to 0.1.0
Added
- Coming Soon page
- Impress, project to manage your documents easily
  and collaboratively
2024-05-24 10:49:09 +02:00
Anthony LC
5c9e4ab3e6 🏷️(backend) accept string as saved document
Saved documents has to be a string now.
Before it has to be a json object.
2024-05-24 10:27:34 +02:00
Anthony LC
7aeea18202 🗃️(frontend) add initial pad to webrtcserver
It is the webrtc server that will be responsible
for managing the data of the BlockNote Editor.

So to restore the data of a saved pads, we have to
add the initial pad to the room of the webrtc
server, it will then update the data of BlockNote
Editor.
The webrtc server accept Y.Doc, so we
convert our base64 data from Minio to make it
compatible with the server.

By doing so, we avoid the problem of
data lost when multiple users are connecting
and one user already updated the data of the
BlockNote Editor before saving it.
2024-05-24 10:27:34 +02:00
Anthony LC
64d508c260 (frontend) pad persistance
This commit manage the persistance of the pad.
We save the pad in different ways:
- when the user close the tab or the browser
- when the user leave the page (go to another pad
  by example)
- every 1 minute
----
- We save the pad only if the pad has been modified.
- Pads are collaborative, to not save multiple
  times the same pad, we save the pad only if
  the user is the last to have modified the pad.
----
Because of the collaborative aspect of the pads,
the best way to store our pad is to save the
Y.Doc, to do so the recommended way is to convert
the Y.Doc to a Uint8Array and then to a string
(base64). Our pad are saved as a string in a
object in a Minio bucket.
2024-05-24 10:27:34 +02:00
Anthony LC
d75649d18a 🏷️(frontend) adapt some types
Adapt some pad types.
2024-05-24 10:27:34 +02:00
Anthony LC
a851720441 (frontend) create useUpdatePad react query hook
Create a react query hook to update a pad.
PATCH /documents/:id
2024-05-24 10:27:34 +02:00
Anthony LC
29f13f5f4b 🚨(frontend) stop throwing errors on id pages
We stopped throwing errors on id pages, often
Next.js just need a rerender to get the id
from the router correctly.
2024-05-24 10:27:34 +02:00
Samuel Paccoud - DINUM
515b686795 (models/api) allow inviting external users to a document by their email
We want to be able to share a document with a person even if this person
does not have an account in impress yet.

This code is ported from https://github.com/numerique-gouv/people.
2024-05-24 08:20:28 +02:00
Samuel Paccoud - DINUM
125284456f 🐛(docker-compose) run node container as logged-in user
Running the container as root will install npm modules with root
access rights and generate issues when running the application with
the logged in user as we do it with Docker Compose.
2024-05-24 08:20:28 +02:00
Anthony LC
b7b3c6c058 🧑‍💻(backend) add document in django admin
Add document in django admin to be able
to manage the document in the admin panel.
2024-05-23 10:44:52 +02:00
Anthony LC
644920e732 (frontend) coming soon
Add a coming soon page bind to the
docs.numerique.gouv.fr domain.
2024-05-23 10:44:52 +02:00
Anthony LC
dc319578b6 👷(helm) production configuration
This PR adds the production configuration
for the helm chart.
2024-05-23 10:44:52 +02:00
Lebaud Antoine
abf08fe3b3 🔧(helm) setup logout flow from Agent Connect
Add the relevant environment configurations to make sure the backend
in dev and staging environments log out the user from Agent Connect.
2024-05-22 16:08:41 +02:00
Lebaud Antoine
76e50ac02f (backend) support Agent Connect Logout flow
The default Logout view provided by Mozilla Django OIDC is not suitable
for the Agent Connect Logout flow.

Previously, when a user was logging-out, only its Django session was ended.
However, its session in the OIDC provider was still active.

Agent Connect implements a 'session/end' endpoint, that allows services to
end user session when they logout.

Agent Connect logout triggers cannot work with the default views implemented
by the dependency Mozilla Django OIDC. In their implementation, they decided
to end Django Session before redirecting to the OIDC provider.

The Django session needs to be retained during the logout process.

An OIDC state is saved to the request session, pass to Agent Connect Logout
endpoint, and verified when the backend receives the Logout callback from Agent
Connect. It seems to follow OIDC specifications.

If for any reason, the Logout flow cannot be initiated with Agent Connect,
(missing ID token in cache, unauthenticated user, etc), the user is redirected
to the final URL, without interacting with Agent Connect.
2024-05-22 16:08:41 +02:00
Lebaud Antoine
5f7e3e620a 🚚(backend) create a dedicated authentication package
Prepare adding advanced authentication features. Create a dedicated
authentication Python package within the core app.

This code organization will be more extensible.
2024-05-22 16:08:41 +02:00
Anthony LC
004a4edfe7 (frontend) add logout button
Rework the header based on latest Johann's design, which introduced a
dropdown menu to manage user account.

In this menu, you can find a logout button, which ends up the backend
session by calling the logout endpoint. Please that automatic redirection
when receiving the backend response were disabled. We handle it in our
custom hook, which reload the page.

Has the session cookie have been cleared, on reloading the page, a new
loggin flow is initiated, and the user is redirected to the OIDC provider.
2024-05-22 16:08:41 +02:00
Anthony LC
d221ebe4f9 💚(CI) increase timeout e2e job
It can happen that the e2e job takes longer
than 10 minutes to finish, we increase the
timeout to 15 minutes to avoid the job to fail.
2024-05-14 16:30:49 +02:00
Anthony LC
9903b43b72 ♻️(app-desk) change header html structure
Change the html structure of the header
component to be more aligned.
Remove dummy profil picture.
2024-05-14 16:12:36 +02:00
Anthony LC
d23426679a 🐛(app-desk) fix fetchPriority warning
The upgrade to react@18.3.1 has a compatibility
issue with next@14.2.3. It creates a error warning
about the fetchPriority prop. This commit fixes the
issue by downgrading react to 18.2.0 as it was
before the last upgrade.
2024-05-14 16:12:36 +02:00
Anthony LC
e2e34a011d (app-impress) add La Gaufre
Add La Gaufre.
It is a button to access easily to the
La Suite products.
2024-05-14 16:12:36 +02:00
Anthony LC
c1dd34512b 🚸(app-impress) footer legales pages
- Add legales pages.
- Add links to the footer for the new pages.

Legales pages are based from
https://lasuite.numerique.gouv.fr/
2024-05-14 11:57:19 +02:00
Anthony LC
88d6d8977c (app-impress) add footer
Add footer to the impress app.
2024-05-14 11:57:19 +02:00
Anthony LC
4ffcf164d8 💄(app-impress) lighter border pad editor
Make border pad editor a bit lighter.
2024-05-14 11:04:45 +02:00
Anthony LC
c25814b060 ♻️(app-impress) add transition props Box component
Add transition props to Box component.
2024-05-14 11:04:45 +02:00
Anthony LC
f88d625622 💄(app-impress) move title document to the top
Move teh title document to the top of the pad editor.
We let the white card only for the pad content.
2024-05-14 11:04:45 +02:00
Anthony LC
a26ee3b868 💬(app-impress) replace recent with documents
Replace Recent literal by Documents.
2024-05-14 11:04:45 +02:00
Anthony LC
c0d919ae52 💄(app-impress) re-design icon close panel
We deleted the menu, so we have to retouch
a bit the icon close panel to fit the new design.
We change the icon to have something more
intuitive by using material icons.
2024-05-14 11:04:45 +02:00
Anthony LC
944385c83f 🔥(app-impress) remove menu
For the moment we only have 1 menu item,
so we can remove the menu.
2024-05-14 11:04:45 +02:00
Anthony LC
ccb6d4cb91 (app-impress) add @blocknote/mantine
The new version of blocknote dissociates
the ux from the @blocknote/react package.
It is now in a separate package @blocknote/mantine.
We add this package to the impress app.
We adapt the toolbar to fit the new package.
2024-05-13 17:21:58 +02:00
renovate[bot]
5853adb8eb ⬆️(dependencies) update js dependencies 2024-05-13 17:21:58 +02:00
Anthony LC
70994de914 💚(ci) improve secrets for createsuperuser
Avoid secrets to be visible from running deployments.
2024-05-13 12:12:52 +02:00
Anthony LC
df0ff61faf 👷(CI) configure Minio with the CI
Add the minio container to the CI on the
job test-back. Adapt the conf to have
Minio working with the backend.
2024-05-13 12:12:52 +02:00
Anthony LC
b94f08d275 🧑‍💻(helm) add minio in staging environment
Add minio in staging environment.
Set the env var for the backend.
2024-05-13 12:12:52 +02:00
Anthony LC
a764e5989c 🧑‍💻(helm) add minio in dev environment
Add minio in dev environment.
Set the env var for the backend.
2024-05-13 12:12:52 +02:00
Samuel Paccoud - DINUM
130e7a8c99 (documents) allow retrieving versions (list and detail)
Versions are retrieved directly from object storage and served on API
endpoints. We make sure a user who is given access to a document will
only see versions that were created after s.he gained access.
2024-05-13 12:12:52 +02:00
Samuel Paccoud - DINUM
8e262da8f5 (documents) add content field as an S3 object
The content field is a writable property on the model which is persisted
in object storage. We take advantage of the versioning, robustness and
scalability of S3.
2024-05-13 12:12:52 +02:00
Samuel Paccoud - DINUM
4956beb040 (docker-compose) add minio for object storage
We will need object storage to store our document content which comes
as a big json object. Storing it in object storage will offer us
versioning, robustness and scalability.
2024-05-13 12:12:52 +02:00
Samuel Paccoud - DINUM
397b9efbce (models/api) add document model and API
We do this by making copies of existing Template and TemplateAccess
models and API. A little refactoring is done to try to limit duplicate
code.
2024-05-13 12:12:52 +02:00
Jacques ROUSSEL
7c6e48975d 💚(ci) improve local deployment with tilt
Change the way we start django app in order to be abble to use django
admin on local deployment
2024-05-13 11:15:43 +02:00
Anthony LC
f0a4de361e 👷(helm) command createsuperuser
We sometimes need a superuser in the Django
application, to fix eventual db issues.
This commit adds a superuser on the pods.
2024-05-13 11:15:43 +02:00
Anthony LC
f0b3090a73 ♻️(frontend) frontend environment free
Until now, the front had to know at build time
the url of the backend and the webrtc server
to be able to communicate with them.
It is not optimal because it means that we need
multiple docker image (1 per environment) to have
the app working, it is not very flexible.

This commit will make the frontend "environment free"
by determining these urls at runtime.
2024-05-13 11:04:06 +02:00
Anthony LC
88109623ba ♻️(app-desk) replace classname spacings
Replace the classname spacings with the new
spacing system based on props.
2024-05-03 15:47:36 +02:00
Anthony LC
d8e7f29072 🎨(app-desk) add margin and padding to Box
Add margin and padding system to Box component.
It proposes the autocompletion.
It is bind with the Cunninghams spacing system.
2024-05-03 15:47:36 +02:00
Jacques ROUSSEL
0c2d097d8d 💚(ci) improve secrets for k8s deployment
Avoid secrets to be visible from running deployments
2024-05-02 16:06:58 +02:00
Anthony LC
5fbb5106a9 🔥(backend) remove code_editor field
code_editor field was removed from the backend.
This filed was used to store the code editor to
generate templates.
2024-05-02 15:24:51 +02:00
Anthony LC
c7a5b5f3fb 🤡(backend-demo) remove code_editor from demo
We removed the template code_editor from app, so
we redapt the demo to fit the old template.
The field code_editor is not needed anymore.
2024-05-02 15:24:51 +02:00
Anthony LC
d967896874 🔥(app-impress) remove templates
We will generate the templates internally.
We remove the template editor.
2024-05-02 15:24:51 +02:00
Anthony LC
b98cc01592 🚚(app-impress) move templates api to pad-tools
Before removing the templates feature, we move the
necessary api to the pad feature.
2024-05-02 15:24:51 +02:00
Anthony LC
d9fa75b764 ⬇️(frontend) react 18.3.1 -> 18.2.0
Downgrade react to 18.2.0.
It seems to have a compatibility issue
with @openfun/cunningham-react.
2024-04-29 15:21:25 +02:00
Anthony LC
33b39ce315 ⬇️(eslint-config-people) eslint 9.0.0 -> 8.57.0
Downgrade eslint to 8.57.0.
9.0.0 has breaking changes, the adoption
is still very low, better to wait.
Add it in the renovate.json file.
2024-04-29 15:21:25 +02:00
renovate[bot]
f5ddce6b47 ⬆️(dependencies) update js dependencies 2024-04-29 15:21:25 +02:00
renovate[bot]
8d48ded46b ⬆️(dependencies) update gunicorn to v22 [SECURITY] 2024-04-29 10:53:14 +02:00
Jacques ROUSSEL
f9bec19c7d 🐛(ci) improve docker-hub
Avoid to notify argocd for nothing
2024-04-29 10:29:56 +02:00
Anthony LC
89aa048fc1 🐛(app-desk) force redirect on resources/[id]
When we use the static mode, Next.js want that we
build our resource pages at build time. But we can't
do that because users can create a resource at runtime.

So we redirect thanks to ngnix when we see that
a resource page is not found.
2024-04-19 13:38:28 +02:00
Anthony LC
b2cc789665 ️(docker) separate deps y-webrtc-signaling
We separate the install part of the signaling server
from the frontend-dep stage. It will improve
the build time of both of them.
2024-04-19 13:38:28 +02:00
Anthony LC
9489264555 🔥(eslint) remove uneeded yarn.lock file
The yarn.lock doesn't seem necessary for this
package, so we're removing it.
Update the frontend yarn.lock
2024-04-19 13:38:28 +02:00
Anthony LC
e1b94e3443 🌐(app-impress) translate impress in french
Add french translation to impress.
2024-04-19 13:38:28 +02:00
Anthony LC
90c8338a59 🛂(app-impress) allow writing permission on editor
allow writing permission on editor when we are not
the owner to test the POC.
2024-04-19 13:38:28 +02:00
Anthony LC
895aaff2d3 📝(readme) steps to start frontend
Define the steps to start the frontend in
development mode.
2024-04-19 13:38:28 +02:00
Anthony LC
bbafce64e7 🐛(app-impress) refresh when multiple Blocknote
When multiple blocknote were created, the editor
didn't display the content of the selected blocknote
but the content of the last blocknote with a new
provider.
We were actually storing the editor, so if the
editor is stored already, we serve it instead of
creating a new one.
2024-04-19 13:38:28 +02:00
Anthony LC
e901103c8b 🔨(Tilt) add y-webrtc-signaling
Add docker_build config to build y-webrtc-signaling.
2024-04-19 12:41:06 +02:00
Anthony LC
f3f5f190a5 🚀(helm) webrtc helm chart
Make the webrtc helm chart.
The webrtc server is a simple websocket server
that forwards messages between clients.
The server uses the port 443 and the protocol wss.
local_url: wss://impress.127.0.0.1.nip.io/ws
2024-04-19 12:41:06 +02:00
Anthony LC
2dc2341052 🚚(docker) move frontend docker config
Move frontend docker config to
src/frontend/Dockerfile.
2024-04-19 12:41:06 +02:00
Anthony LC
c2fb55506d 🧱(docker) dockerhub lasuite/impress-y-webrtc-signaling
Add the signaling server to dockerhub.
We create a special image for the signaling server,
it will be easier for scaling and maintenance.
2024-04-19 12:41:06 +02:00
Anthony LC
428cd74366 🚚(k8s) move tilt config to bin
Move tilt config to bin directory
to keep the root directory clean.
Add makefile cmd about k8s.
2024-04-19 12:41:06 +02:00
Anthony LC
0575c0f086 (app-impress) can duplicate template
Template with mode "is_public" are not editable, except
for the owner. We give the possibility to duplicate a
template, so that the user can edit the new one.
2024-04-18 18:13:59 +02:00
Anthony LC
35d500c08b 👔(backend) display css and code in template endpoint
We would like to duplicate a template from the frontend.
For that we need to access the css and code of the template.
So we add the css and code to the template endpoint.
2024-04-18 18:13:59 +02:00
Anthony LC
bf83bf99b3 ♻️(app-impress) create TemplateTools component
A component that will be used to display
the tools for the template editor.
2024-04-18 18:13:59 +02:00
Anthony LC
964efe4c2f 💄(app-impress) simplify the template builder
Remove some features from the template builder
that are not necessary for the MVP.
2024-04-18 18:13:59 +02:00
Anthony LC
75801bf95f 🤡(backend) add template editor json in demo
For the demo to be compatible with the new template
editor, we need to add the json file that setup
correctly the editor.
We are able to read and use it, but not edit it,
because the demo is in is_public mode.
2024-04-18 18:13:59 +02:00
Anthony LC
4de79c2388 💫(ap-impress) improve transition
Improve transition on Box component.
2024-04-18 15:49:42 +02:00
Anthony LC
f61047dd6a 💄(app-impress) keep highlighting menu when sub menu
When sub menu was open, the parent menu was not
highlighted.
This commit fixes this issue.
2024-04-18 15:49:42 +02:00
Anthony LC
d3b4a576c8 🗑️(app-impress) clean the menu
- replace pad and template icon
- remove unused icons
- remove unused pages
- remove menu items
2024-04-18 15:49:42 +02:00
Anthony LC
7107caf50b 🚸(app-impress) warning when resource not editable
When a resource is public, the users can see it
but not edit it. This commit adds a warning message
to the user to tell them that the resource
is not editable.
2024-04-18 14:44:12 +02:00
Anthony LC
82463be8ec 🐛(app-impress) fix all the resources were public
All the resources were public.
We now update correctly depend the user's choice.
We add a test.
2024-04-18 14:44:12 +02:00
Anthony LC
64cc2a9f2b 🐛(app-impress) fix bug rerendering when empty data
If the data is empty, the editor kept the previous
data and didn't rerender correctly the current editor.
This commit fixes this issue.
2024-04-18 12:48:15 +02:00
Anthony LC
b687ad6974 🚚(app-impress) rename PrintToPDFButton to PDFButton
- rename PrintToPDFButton to PDFButton
- rename button "Print the pad" to "Generate PDF"
2024-04-18 12:48:15 +02:00
Anthony LC
5d6ae870fa (app-impress) select template on pad
We add a template select component to the pad toolbox.
This component is a dropdown that allows the user
to select a template for the pad to add in a PDF.
2024-04-18 12:48:15 +02:00
Anthony LC
ef2ebf596a 👔(app-impress) add is_public switch button
Add a switch button to the create pad form to
allow the user to set the pad as public or private.
2024-04-17 17:45:03 +02:00
Anthony LC
7463735a38 👔(backend) is_public in document and template serializer
Add is_public field to document and template serializer.
2024-04-17 17:45:03 +02:00
Anthony LC
d9cdeb5c68 🚸(app-impress) add warning when body tag not in template
In order to work with the pad, the body tag
must be in the template.
This commit adds a warning to the template editor
when the body tag is not in the template.
2024-04-17 17:15:53 +02:00
Anthony LC
5d25aed765 💫(app-desk) add effect prop on Box component
Add the effect prop on Box component.
2 effects are available: 'show' and 'hide'.
2024-04-17 17:15:53 +02:00
Anthony LC
a8248f10d2 👔(app-impress) button to save html and css of the template
We created a button to save the template's html and css.
With the template saved we will be able to use it
in the near future with our pads.
2024-04-17 17:15:53 +02:00
Anthony LC
faeb8d137b 👔(backend) add css and code in template serializer
We want to be able to update the css and code of
a template from the update endpoint.
This commit adds the css and code fields to the
TemplateSerializer.
2024-04-17 17:15:53 +02:00
Anthony LC
5074b271ad 🐛(backend) add partial_update action to the abilities
partial_update action was missing for the permission.
So when we were patching the template,
it was not working.
This commit adds the partial_update action
to the abilities.
2024-04-17 17:15:53 +02:00
Anthony LC
e13ecdd38d 👔(app-impress) save the templates editor automatically
On almost each change in the code editor,
the template is saved automatically.
It will restore as well the editor when we arrive
on the template editor page.
2024-04-17 17:15:53 +02:00
Anthony LC
3aaa3e179d 👔(backend) add code_editor column on Template
To save the template code editor content,
we need to add a new column on the Template model.
It is a JSONField that will store the code editor content.

We could in the future make an implementation to
save the code editor content in Minio.
2024-04-17 17:15:53 +02:00
Anthony LC
7922c702cd 🚀(app-impress) add template builder editor
We add the template builder editor to
the template feature.
It will be used to create and edit templates.
2024-04-17 17:15:53 +02:00
Anthony LC
8d2a78cf8d (app-impress) create Templates feature
We created the features templates.
It will be used to create, edit and
delete templates.
It is a naive copy of pads.
2024-04-17 17:15:53 +02:00
Anthony LC
7fb8a62e63 🔥(app-impress) remove La Gauffre button
The web host of the La Gauffre app is down,
La Gauffre was not rendered correctly.
La Gauffre button is removed from the header
of the Impress app, in waiting for the
web host to be up again.
2024-04-16 15:01:05 +02:00
Anthony LC
45bf99178b (app-impress) button editor convert from markdown
Some people prefer to write in markdown,
or had already written stuff in markdown,
so this commit adds a button to the editor t
hat will convert the markdown to the editor's format.
2024-04-16 15:01:05 +02:00
Anthony LC
8305cfcf99 🏷️(app-impress) add body type on generate-document endpoint
Adapt the generate-document endpoint
to serve body as html as well.
We add a type to the body parameter to
specify the type of the body.
2024-04-16 13:03:25 +02:00
Anthony LC
1df7c43dd3 🏷️(backend) add body type on generate-document endpoint
We were converting from markdown to html, but the
frontend can provide the body in html format, so
wa can avoid the conversion.

Solution:
Add body type on generate-document endpoint
to allow to choose between markdown and html.
2024-04-16 13:03:25 +02:00
Anthony LC
f9705c6ce9 ♻️(backend) api resources list ordering
Give the possibility to order the resources
list by creation date (documents / templates).
By default the list is ordered by
creation date descending.
2024-04-16 11:37:11 +02:00
Anthony LC
db9c9ecfe3 🤡(app-impress) remove mock pad endpoints
The backend was updated to include the
pad endpoints.
The mock pad endpoints were removed
in favor of the real ones.
We adapted the types as well to match
the real ones.
2024-04-16 11:37:11 +02:00
Samuel Paccoud - DINUM
3e0739cd0a (models/api) add document model and API
We do this by making copies of existing Template and TemplateAccess
models and API. A little refactoring is done to try to limit duplicate
code.
2024-04-16 11:37:11 +02:00
Anthony LC
0024cc5814 🚨(backend) fix linting issue
Fix linting issue in the backend codebase.
2024-04-12 16:24:23 +02:00
Anthony LC
5587cf69dd 🔧(helm) add command create_demo
We need to get dummy data in the system
for the moment, it will insert a template
for the PDF endpoint.
We need to run this command after the
migration is made.
2024-04-12 16:24:23 +02:00
Anthony LC
68c130bd84 🔧(backend) activate https on oidc redirection
mozilla-django-oidc didn't add the https://
prefix to the redirect_uri.
We set the option SECURE_PROXY_SSL_HEADER to
('HTTP_X_FORWARDED_PROTO', 'https') in the
settings.py file to force the https prefix.
2024-04-12 16:24:23 +02:00
Anthony LC
22e79e7583 🔧(backend) activate container liveness probes
Enabled Dockerflow Django app by activating liveness probes. The previously
unavailable routes such as `__heartbeat__` and `__lbheartbeat__` are now
accessible. New endpoints include:
* GET /__version__
* GET /__heartbeat__
* GET /__lbheartbeat__
2024-04-12 16:24:23 +02:00
Anthony LC
056bad08a5 🔧(backend) configure RedisCache in production settings
In development, sessions are saved in local memory. It's working well,
however it doesn't adapt to a kubernetized setup. Several pods need
to access the current sessions, which need to be stored in a single
source of truth.

With a local memory cache, pods cannot read session saved in other pods.
We end up returning 401 errors, because we cannot authenticate the user.

I preferred setting up a proper cache than storing sessions in database,
because in the long run it would be a performance bottleneck. Cache will
decrease data access latency when reading current sessions.

I added a Redis cache backend to the production settings. Sessions would
be persisted to Redis. In K8s, a Redis operator will make sure the cached
data are not lost.

Two new dependencies were added, redis and django-redis.

I followed the installation guide of django-redis dependency. These
setting were tested deploying the app to a local K8s cluster.
2024-04-12 16:24:23 +02:00
Anthony LC
2f4dd4ee7a 🔐(impress) update impress oidc secrets
Update oidc secrets in dev and staging environments
to match agent connect with impress settings.
2024-04-12 16:24:23 +02:00
Anthony LC
51babc07af 🚚(project) rename desk occurences to impress
Rename desk occurences to impress.
2024-04-12 16:24:23 +02:00
Anthony LC
dbb592bc9a 🔨(CI) add Tilt
Tilt is a tool for local Kubernetes development.
It makes it easy to see your changes as you
make them, and it rebuilds and redeploys
your app as you change it.
It is a great tool for local development
and testing of Kubernetes applications.
2024-04-12 16:24:23 +02:00
Anthony LC
0dc1be4cc7 (e2e) test the pdf export of the impress app
Add a test to check the pdf export of the
impress app.
It intercept the download of the PDF and
read its content.
We can assert that the template is correctly
rendered, and that the pad text is present as
well.
2024-04-11 09:21:35 +02:00
Anthony LC
a772d8fc29 🤡(backend) demo template
Create the create_demo command.
It creates a demo template for the backend.
We add it in the makefile bootstrap.

We force the template id, this id is the same as
used in the frontend for the moment.
When the template feature will be created in
the frontend side we will not have to force
it anymore.
2024-04-11 09:21:35 +02:00
Anthony LC
c6c0c1355d 🙈(project) add .devcontainer in gitignore
.devcontainer is used to create virtual environment
for development. It is not necessary to be
included in the git repository.
2024-04-11 09:21:35 +02:00
Anthony LC
cc64b8c8b5 🐛(backend) format html when generate document
When we converted from markdown to html,
we didn't format the html, so it was displayed
like a string in our pdf.
We now format_html the html before we generate
the pdf.
2024-04-11 09:21:35 +02:00
Anthony LC
2665b707db (app-impress) print to pdf
Feature to print the pad to pdf.

It display a button in the pad toolbar,
when clicked it will convert the pad to markdown,
send the markdown with a template to the backend
to convert it to pdf and then download the pdf.
2024-04-11 09:21:35 +02:00
Anthony LC
e28a4294fa (app-desk) add useCreatePdfFromMarkdown
Add the hook useCreatePdfFromMarkdown,
it will be used to generate a pdf from a
a template and a markdown.
2024-04-11 09:21:35 +02:00
Anthony LC
9ca63baeeb 🔨(backend) add local CSRF_TRUSTED_ORIGINS
To allow the frontend to make requests to the backend,
we need to add the frontend's origin
to the CSRF_TRUSTED_ORIGINS setting.
2024-04-11 09:21:35 +02:00
Anthony LC
a5a8a64350 ♻️(app-impress) refacto pad store to add the editor
To spread the data editor to different feature
of our app, we add the editor in the pad store.
It will give an easy access to the editor data.
2024-04-11 09:21:35 +02:00
Anthony LC
272b18ae02 (y-webrtc-signaling) test signaling server
Create a e2e testcase to test the signaling server.
2024-04-09 16:56:21 +02:00
Anthony LC
37367f2d22 🎨(app-impress) different color per user
We generate a color for each user.
This color is used to highlight the
user's cursor and selection.
2024-04-09 16:56:21 +02:00
Anthony LC
2070a159e1 ️(app-impress) create pad store
Better to store the WebrtcProvider, we will
reuse the provider if one is already created.
2024-04-09 16:56:21 +02:00
Anthony LC
a4e8bbd26e 🚚(app-impress) rename usePadStore to usePadPanelStore
This commit renames the `usePadStore`
to `usePadPanelStore` to better
reflect the purpose of the store.
2024-04-09 16:56:21 +02:00
Anthony LC
75bb4b36d4 🔨(y-webrtc-signaling) dockerize y-webrtc-signaling
Dockerize y-webrtc-signaling for futur deployment.
Add a docker-compose file to run easily
the signaling server.
2024-04-09 16:56:21 +02:00
Anthony LC
f4e4cbaa56 (y-webrtc-signaling) create signaling server
Create signaling server for WebRTC.
It will be used for the collaborative editor, client
will connect to this server to see their
changes in real-time.
2024-04-09 16:56:21 +02:00
Anthony LC
6b567a0f69 🔧(sops) add secrets
Add secrets to the repository using sops.
2024-04-05 12:42:33 +02:00
Anthony LC
007a9ae4f4 💚(docker) adapt dockerfile with people updates
Adapt the dockerfile to include the new
people updates, plus adapt to impress project.
2024-04-05 12:42:33 +02:00
Anthony LC
ca0571cb21 (CI) copy docker-hub github-actions from people
Copy of docker-hub github-actions
from people repository to impress repository.
Replace occurence of people by impress.
2024-04-05 12:42:33 +02:00
Anthony LC
560dbf1c2e 🔧(helm) replace occurences of desk and people
Replace naively all occurences of
desk and people with impress.
2024-04-05 12:42:33 +02:00
Anthony LC
17729e231b (CI) copy helm config from people
Naive copy of helm config from people repository
to impress repository.
2024-04-05 12:42:33 +02:00
Anthony LC
409691d466 🚨(app-desk) improve linter
The linter was passing near the ts errors in
the tests, we improve the linter by adding
a ts checkup.
2024-04-04 17:40:27 +02:00
Anthony LC
603ece466d 🤡(app-impress) mock BlockNoteEditor
BlockNoteEditor uses many libraries not compatible
with Jest.
This commit mocks the BlockNoteEditor
component to allow Jest to run without errors.
2024-04-04 17:40:27 +02:00
Anthony LC
79831154be (app-impress) add basic blocknotes editor
Create PadEditor, the parent component for the editors.
We integrate the BlockNoteEditor into the PadEditor.
2024-04-04 17:40:27 +02:00
Anthony LC
da273d6b2b (app-markdown) add necessary library for pad editor
Add the necessary library for the pad editor:
- blocknote editor (Notion like editor)
- yjs and y-webrtc for real-time collaboration
2024-04-04 17:40:27 +02:00
Anthony LC
5ce77c2138 🤡(app-desk) mock pad and pads panel
In order to access to the pad page we mocked
the api calls of the pad and pads panel.
2024-04-04 17:40:27 +02:00
renovate[bot]
b7663cca2d ⬆️(dependencies) update js dependencies 2024-04-04 12:38:11 +02:00
Anthony LC
80335ffd15 🔥(markdownPDF) remove versionning app mardownPDF
Remove the app mardownPDF from the versionning,
this app is not used anymore, we don't want
to maintain it.
2024-04-04 12:05:54 +02:00
Anthony LC
6de828c049 🚨(frontend) fix CI probs
The CI highlighted some issues with the tests.
This commit fixes the issues.
2024-04-03 17:36:54 +02:00
Anthony LC
352d4f6d27 🔥(app-impress) remove teams pages
Remove teams pages from the next router.
2024-04-03 17:36:54 +02:00
Anthony LC
3e6f20aedd ️(frontend) clean yarn.lock
The yarn.lock file get full of garbage and old
dependencies after a while. This commit cleans it.
2024-04-03 17:36:54 +02:00
Anthony LC
bf19c50146 💚(CI) add frontend jobs
Add the frontend jobs to the CI pipeline.
2024-04-03 17:36:54 +02:00
Anthony LC
1d3f1f793e (e2e) adapt tests to impress app
Adapt e2e test to works with Impress.
2024-04-03 17:36:54 +02:00
renovate[bot]
cad206cccf ⬆️(dependencies) update django to v5.0.3 [SECURITY] 2024-04-03 13:10:09 +02:00
Anthony LC
3cd1852b93 🔧(sops) add secrets
Add secrets to the repository using sops.
2024-04-03 13:07:05 +02:00
Anthony LC
d19c7f7fd8 👷(project) rename i18n-back to i18n-crowdin
Rename i18n-back to i18n-crowdin.
Change the job to use the 'age' env.
2024-04-03 13:07:05 +02:00
Anthony LC
8afc003004 🐛(docker) switch CMD form from Shell to Exec
`backend-development` and `backend-production` CMD syntaxes were
using a Shell Form. Shell form prevented Unix signals from reaching
our container correctly, such as SIGTERM. Also, the shell process
ends up being the PID 1, instead of our Python scripts.

Docker recommends to use the exec form whenever possible.
2024-04-03 13:07:05 +02:00
Anthony LC
23797f2953 🔧(project) add frontend cmd to Makefile
add frontend cmd to Makefile.
2024-04-03 13:07:05 +02:00
Anthony LC
78bccafb93 🐛(backend) generate po file
Generate the PO file.
It will create the locale folder, and so
init correctly LOCALE_PATHS env.
This should fix the CI.
2024-04-03 13:07:05 +02:00
Anthony LC
42bea9940e 🔥(app-impress) remove addMembers feature
addMembers feature was a part of the People
project, we don't need it in the Impress project.
2024-04-03 11:57:36 +02:00
Anthony LC
55e2a2073d 🔥(app-impress) remove Members feature
Members feature was a part of the People
project, we don't need it in the Impress project.
2024-04-03 11:57:36 +02:00
Anthony LC
1f804ad1f9 🔥(app-impress) remove Teams feature
Teams feature was a part of the People
project, we don't need it in the Impress project.
2024-04-03 11:57:36 +02:00
Anthony LC
3ba388bc49 ⬆️(project) update renovate.json config
Update the renovate config to fit with
what we setup in People project.
2024-04-03 11:57:36 +02:00
Anthony LC
b517b0bc39 (app-impress) create feature pad
Create a new feature pad for the app impress.
It will be used to display a pad in the app.
2024-04-03 11:57:36 +02:00
Anthony LC
1cb138ecb3 (app-impress) create feature pads-create
Create a new feature pads-create for the app impress.
It will be used to create a pad in the app.
2024-04-03 11:57:36 +02:00
Anthony LC
1dd33706c0 (app-impress) create feature pads-panel
Create a new feature pads-panel for the app impress.
It will be used to display the pads in the app.
2024-04-03 11:57:36 +02:00
Anthony LC
6fe5f3ef72 🚚(app-impress) rename icon-desk.svg to icon-impress.svg
Rename icon-desk.svg to icon-impress.svg.
2024-04-03 11:57:36 +02:00
Anthony LC
1bf391f26f (app-impress) add layouts
Add layouts folder to the app-impress,
it will contain all the layouts for the app.
For the moment:
- MainLayout.tsx
- PadLayout.tsx
2024-04-03 11:57:36 +02:00
Anthony LC
dfafd335f4 🌐(i18n) create package i18n
The package i18n will handle the
internationalization of the applications.
It will parse the frontend code and extract
the translations to be send to the crowdin
platform.
2024-04-02 16:39:17 +02:00
Anthony LC
3098b9f4fc 🚀(app-impress) create the e2e app impress
Create the e2e app impress, it will be used
to test the app impress.
2024-04-02 16:39:17 +02:00
Anthony LC
8fda0bc9b9 🚀(app-impress) create the base app impress
Create the base app impress, based on the
people app.
2024-04-02 16:39:17 +02:00
Anthony LC
c64b66c797 🚨(frontend) add lib eslint-config-impress
Create the eslint-config-impress package to share
eslint configuration with all impress projects.
2024-04-02 16:39:17 +02:00
Anthony LC
c3e354d727 🚚(frontend) move previous app to mardownPDF app
We will start an monorepo archi, we move the previous
app to a new folder called mardownPDF.
2024-04-02 16:39:17 +02:00
Samuel Paccoud - DINUM
ab7d9933e0 ♻️(project) rename project from "publish" to "impress"
The repository was renamed to "impress" but the code was still
mentionning "publish".
2024-03-08 08:53:56 +01:00
Samuel Paccoud - DINUM
f581eb8abd (models/api) add RBAC on templates linking accesses to a team name
We want to be able to control who can access a template via roles.
I added this feature on the TeamAccess model assuming that the teams
to which a user belongs can be retrieved via a `get_teams` method on
the user model. The idea is that this method will get the teams either
via a call to an external API or directly from the OIDC token upon
user login. This list of teams will probably have to be cached for
each user.
2024-03-07 21:21:18 +01:00
Lebaud Antoine
a23118bee4 🚨(backend) fix minor linting issues
Lint backend tests and sources.
2024-03-03 08:29:00 +01:00
Lebaud Antoine
47aeab76a5 (backend) test the authentication class
Add tests on get_or_create method.
2024-03-03 08:29:00 +01:00
Lebaud Antoine
551468470f 🐛(project) run production image locally with docker-compose
The local deployment of the Production image through docker-compose was
failing due to issues in the Django configurations, influenced by Joanie.

The bug stemmed from a dependency on a development-specific package
(drf-spectacular-sidecar) while attempting to run the application in
production mode.

Changes Made:
- Introduced new Django settings for local demo environments.
2024-03-03 08:29:00 +01:00
Lebaud Antoine
36d6735798 ✏️(project) fix minor typos
Found typos and fixed them.
2024-03-03 08:29:00 +01:00
Lebaud Antoine
b1892ded17 (backend) drop JWT authentication in API tests
Force login to bypass authorization checks when necessary.

Note: Generating a session cookie through OIDC flow
is not supported while testing our API.
2024-03-03 08:29:00 +01:00
Lebaud Antoine
b9eee3e643 🔧(backend) configure Authorization Code authentication
Integrate 'mozilla-django-oidc' dependency, to support
Authorization Code flow, which is required by Agent Connect.

Thus, we provide a secure back channel OIDC flow, and return
to the client only a session cookie.

Done:
- Replace JWT authentication by Session based authentication in DRF
- Update Django settings to make OIDC configurations easily editable
- Add 'mozilla-django-oidc' routes to our router
- Implement a custom Django Authentication class to adapt
'mozilla-django-oidc' to our needs

'mozilla-django-oidc' routes added are:
- /authenticate
- /callback (the redirect_uri called back by the Idp)
- /logout
2024-03-03 08:29:00 +01:00
Lebaud Antoine
23e92d12fb (project) configure Keycloak server to support OIDC
Create a realm 'impress' and configure it to support
the authorization code flow.
2024-03-03 08:29:00 +01:00
Samuel Paccoud - DINUM
0f9327a1de ♻️(backend) refactor post hackathon to a first working version
This project was copied and hacked to make a POC in a 2-day hackathon.
We need to clean and refactor things in order to get a first version
of the product we want.
2024-02-23 18:41:36 +01:00
Samuel Paccoud
0a3e26486e Merge pull request #1 from numerique-gouv/dependabot/npm_and_yarn/src/mail/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2 in /src/mail
2024-02-16 19:15:23 +01:00
Samuel Paccoud
ae2ba59c82 Merge pull request #3 from numerique-gouv/dependabot/pip/src/backend/django-5.0.2
Bump django from 5.0 to 5.0.2 in /src/backend
2024-02-16 19:14:44 +01:00
dependabot[bot]
0f0b0f0de1 Bump django from 5.0 to 5.0.2 in /src/backend
Bumps [django](https://github.com/django/django) from 5.0 to 5.0.2.
- [Commits](https://github.com/django/django/compare/5.0...5.0.2)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-07 17:37:18 +00:00
Samuel Paccoud - DINUM
44ff62c08f (backend) parse YAML metadata
YAML header is processed, to fill Django
templates with the right parametrized values.

Used an external dependency, frontmatter.
2024-02-05 22:59:14 +01:00
Lebaud Antoine
312a680b66 (frontend) set up Vite-based frontend project
Chose Vite for static output efficiency, aligning with project needs.

All API interactions are currently unauthenticated. SSO support
planned soon, using ProConnect.

UX is minimalistic, and showcases the core idea.

Components introduced:
* AppProvider
* Select and TextArea Rhf inputs

API hooks introduced:
* useGeneratePDF, generates a PDF, and downloads it in the client.
* useTemplates, fetches available templates to populate Select options.
2024-01-16 16:35:11 +01:00
dependabot[bot]
7eab2b61a5 Bump semver from 5.7.1 to 5.7.2 in /src/mail
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 15:30:25 +00:00
Samuel Paccoud - DINUM
62df0524ac (project) first proof of concept printing pdf from markdown
This is a boilerplate inspired from https://github.com/openfun/joanie
2024-01-09 15:30:36 +01:00
850 changed files with 70551 additions and 28569 deletions

View File

@@ -32,5 +32,5 @@ db.sqlite3
.pylint.d
.pytest_cache
# Frontend dependencies
# Frontend
node_modules

View File

@@ -18,7 +18,7 @@ A clear and concise description of what you expected to happen (or code).
3. And then the bug happens!
**Environment**
- People version:
- Impress version:
- Platform:
**Possible Solution**

View File

@@ -9,9 +9,9 @@ We primarily use GitHub as an issue tracker. If however you're encountering an i
---
Please make sure you have read our [main Readme](https://github.com/numerique-gouv/people).
Please make sure you have read our [main Readme](https://github.com/numerique-gouv/impress).
Also make sure it was not already answered in [an open or close issue](https://github.com/numerique-gouv/people/issues).
Also make sure it was not already answered in [an open or close issue](https://github.com/numerique-gouv/impress/issues).
If your question was not covered, and you feel like it should be, fire away! We'd love to improve our docs! 👌

77
.github/workflows/crowdin_download.yml vendored Normal file
View File

@@ -0,0 +1,77 @@
name: Download translations from Crowdin
on:
workflow_dispatch:
push:
branches:
- 'release/**'
jobs:
install-dependencies:
uses: ./.github/workflows/dependencies.yml
with:
node_version: '20.x'
with-front-dependencies-installation: true
synchronize-with-crowdin:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Create empty source files
run: |
touch src/backend/locale/django.pot
mkdir -p src/frontend/packages/i18n/locales/impress/
touch src/frontend/packages/i18n/locales/impress/translations-crowdin.json
# crowdin workflow
- name: crowdin action
uses: crowdin/github-action@v2
with:
config: crowdin/config.yml
upload_sources: false
upload_translations: false
download_translations: true
create_pull_request: false
push_translations: false
push_sources: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# A numeric ID, found at https://crowdin.com/project/<projectName>/tools/api
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
# Visit https://crowdin.com/settings#api-key to create this token
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
CROWDIN_BASE_PATH: "../src/"
# frontend i18n
- name: Restore the frontend cache
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
fail-on-cache-miss: true
- name: generate translations files
working-directory: src/frontend
run: yarn i18n:deploy
# Create a new PR
- name: Create a new Pull Request with new translated strings
uses: peter-evans/create-pull-request@v7
with:
commit-message: |
🌐(i18n) update translated strings
Update translated files with new translations
title: 🌐(i18n) update translated strings
body: |
## Purpose
update translated strings
## Proposal
- [x] update translated strings
branch: i18n/update-translations
labels: i18n

76
.github/workflows/crowdin_upload.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Update crowdin sources
on:
workflow_dispatch:
push:
branches:
- main
jobs:
install-dependencies:
uses: ./.github/workflows/dependencies.yml
with:
node_version: '20.x'
with-front-dependencies-installation: true
with-build_mails: true
synchronize-with-crowdin:
needs: install-dependencies
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# Backend i18n
- name: Install Python
uses: actions/setup-python@v3
with:
python-version: "3.12.6"
- name: Upgrade pip and setuptools
run: pip install --upgrade pip setuptools
- name: Install development dependencies
run: pip install --user .
working-directory: src/backend
- name: Restore the mail templates
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}
fail-on-cache-miss: true
- name: Install gettext
run: |
sudo apt-get update
sudo apt-get install -y gettext pandoc
- name: generate pot files
working-directory: src/backend
run: |
DJANGO_CONFIGURATION=Build python manage.py makemessages -a --keep-pot
# frontend i18n
- name: Restore the frontend cache
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
fail-on-cache-miss: true
- name: generate source translation file
working-directory: src/frontend
run: yarn i18n:extract
# crowdin workflow
- name: crowdin action
uses: crowdin/github-action@v2
with:
config: crowdin/config.yml
upload_sources: true
upload_translations: false
download_translations: false
create_pull_request: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# A numeric ID, found at https://crowdin.com/project/<projectName>/tools/api
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
# Visit https://crowdin.com/settings#api-key to create this token
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
CROWDIN_BASE_PATH: "../src/"

85
.github/workflows/dependencies.yml vendored Normal file
View File

@@ -0,0 +1,85 @@
name: Dependency reusable workflow
on:
workflow_call:
inputs:
node_version:
required: false
default: '20.x'
type: string
with-front-dependencies-installation:
type: boolean
default: false
with-build_mails:
type: boolean
default: false
jobs:
front-dependencies-installation:
if: ${{ inputs.with-front-dependencies-installation == true }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Setup Node.js
if: steps.front-node_modules.outputs.cache-hit != 'true'
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
- name: Install dependencies
if: steps.front-node_modules.outputs.cache-hit != 'true'
run: cd src/frontend/ && yarn install --frozen-lockfile
- name: Cache install frontend
if: steps.front-node_modules.outputs.cache-hit != 'true'
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
build-mails:
if: ${{ inputs.with-build_mails == true }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/mail
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Restore the mail templates
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}
- name: Setup Node.js
if: steps.mail-templates.outputs.cache-hit != 'true'
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
- name: Install yarn
if: steps.mail-templates.outputs.cache-hit != 'true'
run: npm install -g yarn
- name: Install node dependencies
if: steps.mail-templates.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile
- name: Build mails
if: steps.mail-templates.outputs.cache-hit != 'true'
run: yarn build
- name: Cache mail templates
if: steps.mail-templates.outputs.cache-hit != 'true'
uses: actions/cache@v4
with:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}

View File

@@ -1,52 +0,0 @@
name: Deploy
on:
push:
tags:
- 'preprod'
- 'production'
jobs:
notify-argocd:
runs-on: ubuntu-latest
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "people,secrets"
-
name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/people/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
-
name: Call argocd github webhook
run: |
data='{"ref": "'$GITHUB_REF'","repository": {"html_url":"'$GITHUB_SERVER_URL'/'$GITHUB_REPOSITORY'"}}'
sig=$(echo -n ${data} | openssl dgst -sha1 -hmac ''${ARGOCD_WEBHOOK_SECRET}'' | awk '{print "X-Hub-Signature: sha1="$2}')
curl -X POST -H 'X-GitHub-Event:push' -H "Content-Type: application/json" -H "${sig}" --data "${data}" $ARGOCD_WEBHOOK_URL
sig=$(echo -n ${data} | openssl dgst -sha1 -hmac ''${ARGOCD_PRODUCTION_WEBHOOK_SECRET}'' | awk '{print "X-Hub-Signature: sha1="$2}')
curl -X POST -H 'X-GitHub-Event:push' -H "Content-Type: application/json" -H "${sig}" --data "${data}" $ARGOCD_PRODUCTION_WEBHOOK_URL
start-test-on-preprod:
needs:
- notify-argocd
runs-on: ubuntu-latest
if: startsWith(github.event.ref, 'refs/tags/preprod')
steps:
-
name: Debug
run: |
echo "Start test when preprod is ready"

View File

@@ -1,4 +1,5 @@
name: Docker Hub Workflow
run-name: Docker Hub Workflow
on:
workflow_dispatch:
@@ -18,39 +19,29 @@ jobs:
build-and-push-backend:
runs-on: ubuntu-latest
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "people,secrets"
-
name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
uses: actions/checkout@v4
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: lasuite/people-backend
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/people/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
images: lasuite/impress-backend
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
run: echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_USER" --password-stdin
run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USER }}" --password-stdin
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
with:
docker-build-args: '--target backend-production -f Dockerfile'
docker-image-name: 'docker.io/lasuite/impress-backend:${{ github.sha }}'
continue-on-error: true
-
name: Build and push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
target: backend-production
@@ -62,47 +53,73 @@ jobs:
build-and-push-frontend:
runs-on: ubuntu-latest
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "people,secrets"
-
name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
uses: actions/checkout@v4
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: lasuite/people-frontend
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/people/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
images: lasuite/impress-frontend
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
run: echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_USER" --password-stdin
run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USER }}" --password-stdin
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
with:
docker-build-args: '-f src/frontend/Dockerfile --target frontend-production'
docker-image-name: 'docker.io/lasuite/impress-frontend:${{ github.sha }}'
continue-on-error: true
-
name: Build and push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
file: ./src/frontend/Dockerfile
target: frontend-production
build-args: DOCKER_USER=${{ env.DOCKER_USER }}:-1000
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push-y-provider:
runs-on: ubuntu-latest
steps:
-
name: Checkout repository
uses: actions/checkout@v4
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: lasuite/impress-y-provider
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USER }}" --password-stdin
-
name: Run trivy scan
uses: numerique-gouv/action-trivy-cache@main
with:
docker-build-args: '-f src/frontend/servers/y-provider/Dockerfile --target y-provider'
docker-image-name: 'docker.io/lasuite/impress-frontend:${{ github.sha }}'
continue-on-error: true
-
name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: ./src/frontend/servers/y-provider/Dockerfile
target: y-provider
build-args: DOCKER_USER=${{ env.DOCKER_USER }}:-1000
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
notify-argocd:
needs:
- build-and-push-frontend
@@ -110,29 +127,12 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "people,secrets"
-
name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/people/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
uses: actions/checkout@v4
-
name: Call argocd github webhook
run: |
data='{"ref": "'$GITHUB_REF'","repository": {"html_url":"'$GITHUB_SERVER_URL'/'$GITHUB_REPOSITORY'"}}'
sig=$(echo -n ${data} | openssl dgst -sha1 -hmac ''${ARGOCD_WEBHOOK_SECRET}'' | awk '{print "X-Hub-Signature: sha1="$2}')
curl -X POST -H 'X-GitHub-Event:push' -H "Content-Type: application/json" -H "${sig}" --data "${data}" $ARGOCD_WEBHOOK_URL
data='{"ref": "'$GITHUB_REF'","repository": {"html_url":"'$GITHUB_SERVER_URL'/${{ secrets.DEPLOYMENT_REPO_URL }}"}}'
sig=$(echo -n ${data} | openssl dgst -sha1 -hmac "${{ secrets.ARGOCD_PREPROD_WEBHOOK_SECRET }}" | awk '{print "X-Hub-Signature: sha1="$2}')
curl -X POST -H 'X-GitHub-Event:push' -H "Content-Type: application/json" -H "${sig}" --data "${data}" ${{ vars.ARGOCD_PREPROD_WEBHOOK_URL }}

30
.github/workflows/helmfile-linter.yaml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Helmfile lint
run-name: Helmfile lint
on:
push:
pull_request:
branches:
- 'main'
jobs:
helmfile-lint:
runs-on: ubuntu-latest
container:
image: ghcr.io/helmfile/helmfile:v0.171.0
steps:
-
name: Checkout repository
uses: actions/checkout@v4
-
name: Helmfile lint
shell: bash
run: |
set -e
HELMFILE=src/helm/helmfile.yaml
environments=$(awk 'BEGIN {in_env=0} /^environments:/ {in_env=1; next} /^---/ {in_env=0} in_env && /^ [^ ]/ {gsub(/^ /,""); gsub(/:.*$/,""); print}' "$HELMFILE")
for env in $environments; do
echo "################### $env lint ###################"
helmfile -e $env -f $HELMFILE lint || exit 1
echo -e "\n"
done

138
.github/workflows/impress-frontend.yml vendored Normal file
View File

@@ -0,0 +1,138 @@
name: Frontend Workflow
on:
push:
branches:
- main
pull_request:
branches:
- "*"
jobs:
install-dependencies:
uses: ./.github/workflows/dependencies.yml
with:
node_version: '20.x'
with-front-dependencies-installation: true
test-front:
needs: install-dependencies
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.x"
- name: Restore the frontend cache
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
fail-on-cache-miss: true
- name: Test App
run: cd src/frontend/ && yarn test
lint-front:
runs-on: ubuntu-latest
needs: install-dependencies
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.x"
- name: Restore the frontend cache
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
fail-on-cache-miss: true
- name: Check linting
run: cd src/frontend/ && yarn lint
test-e2e-chromium:
runs-on: ubuntu-latest
needs: install-dependencies
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.x"
- name: Restore the frontend cache
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
fail-on-cache-miss: true
- name: Set e2e env variables
run: cat env.d/development/common.e2e.dist >> env.d/development/common.dist
- name: Install Playwright Browsers
run: cd src/frontend/apps/e2e && yarn install --frozen-lockfile && yarn install-playwright chromium
- name: Start Docker services
run: make bootstrap FLUSH_ARGS='--no-input' cache=
- name: Run e2e tests
run: cd src/frontend/ && yarn e2e:test --project='chromium'
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-chromium-report
path: src/frontend/apps/e2e/report/
retention-days: 7
test-e2e-other-browser:
runs-on: ubuntu-latest
needs: test-e2e-chromium
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.x"
- name: Restore the frontend cache
uses: actions/cache@v4
with:
path: "src/frontend/**/node_modules"
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
fail-on-cache-miss: true
- name: Set e2e env variables
run: cat env.d/development/common.e2e.dist >> env.d/development/common.dist
- name: Install Playwright Browsers
run: cd src/frontend/apps/e2e && yarn install --frozen-lockfile && yarn install-playwright firefox webkit chromium
- name: Start Docker services
run: make bootstrap FLUSH_ARGS='--no-input' cache=
- name: Run e2e tests
run: cd src/frontend/ && yarn e2e:test --project=firefox --project=webkit
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-other-report
path: src/frontend/apps/e2e/report/
retention-days: 7

185
.github/workflows/impress.yml vendored Normal file
View File

@@ -0,0 +1,185 @@
name: Main Workflow
on:
push:
branches:
- main
pull_request:
branches:
- "*"
jobs:
install-dependencies:
uses: ./.github/workflows/dependencies.yml
with:
with-build_mails: true
lint-git:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' # Makes sense only for pull requests
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: show
run: git log
- name: Enforce absence of print statements in code
run: |
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude)**/impress.yml' | grep "print("
- name: Check absence of fixup commits
run: |
! git log | grep 'fixup!'
- name: Install gitlint
run: pip install --user requests gitlint
- name: Lint commit messages added to main
run: ~/.local/bin/gitlint --commits origin/${{ github.event.pull_request.base.ref }}..HEAD
check-changelog:
runs-on: ubuntu-latest
if: |
contains(github.event.pull_request.labels.*.name, 'noChangeLog') == false &&
github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 50
- name: Check that the CHANGELOG has been modified in the current branch
run: git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.after }} | grep 'CHANGELOG.md'
lint-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Check CHANGELOG max line length
run: |
max_line_length=$(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com" | wc -L)
if [ $max_line_length -ge 80 ]; then
echo "ERROR: CHANGELOG has lines longer than 80 characters."
exit 1
fi
lint-back:
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/backend
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Python
uses: actions/setup-python@v3
with:
python-version: "3.12.6"
- name: Upgrade pip and setuptools
run: pip install --upgrade pip setuptools
- name: Install development dependencies
run: pip install --user .[dev]
- name: Check code formatting with ruff
run: ~/.local/bin/ruff format . --diff
- name: Lint code with ruff
run: ~/.local/bin/ruff check .
- name: Lint code with pylint
run: ~/.local/bin/pylint .
test-back:
runs-on: ubuntu-latest
needs: install-dependencies
defaults:
run:
working-directory: src/backend
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: impress
POSTGRES_USER: dinum
POSTGRES_PASSWORD: pass
ports:
- 5432:5432
# needed because the postgres container does not provide a healthcheck
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
env:
DJANGO_CONFIGURATION: Test
DJANGO_SETTINGS_MODULE: impress.settings
DJANGO_SECRET_KEY: ThisIsAnExampleKeyForTestPurposeOnly
OIDC_OP_JWKS_ENDPOINT: /endpoint-for-test-purpose-only
DB_HOST: localhost
DB_NAME: impress
DB_USER: dinum
DB_PASSWORD: pass
DB_PORT: 5432
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
AWS_S3_ENDPOINT_URL: http://localhost:9000
AWS_S3_ACCESS_KEY_ID: impress
AWS_S3_SECRET_ACCESS_KEY: password
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create writable /data
run: |
sudo mkdir -p /data/media && \
sudo mkdir -p /data/static
- name: Restore the mail templates
uses: actions/cache@v4
id: mail-templates
with:
path: "src/backend/core/templates/mail"
key: mail-templates-${{ hashFiles('src/mail/mjml') }}
fail-on-cache-miss: true
- name: Start MinIO
run: |
docker pull minio/minio
docker run -d --name minio \
-p 9000:9000 \
-e "MINIO_ACCESS_KEY=impress" \
-e "MINIO_SECRET_KEY=password" \
-v /data/media:/data \
minio/minio server --console-address :9001 /data
# Tool to wait for a service to be ready
- name: Install Dockerize
run: |
curl -sSL https://github.com/jwilder/dockerize/releases/download/v0.8.0/dockerize-linux-amd64-v0.8.0.tar.gz | sudo tar -C /usr/local/bin -xzv
- name: Wait for MinIO to be ready
run: |
dockerize -wait tcp://localhost:9000 -timeout 10s
- name: Configure MinIO
run: |
MINIO=$(docker ps | grep minio/minio | sed -E 's/.*\s+([a-zA-Z0-9_-]+)$/\1/')
docker exec ${MINIO} sh -c \
"mc alias set impress http://localhost:9000 impress password && \
mc alias ls && \
mc mb impress/impress-media-storage && \
mc version enable impress/impress-media-storage"
- name: Install Python
uses: actions/setup-python@v3
with:
python-version: "3.12.6"
- name: Install development dependencies
run: pip install --user .[dev]
- name: Install gettext (required to compile messages) and MIME support
run: |
sudo apt-get update
sudo apt-get install -y gettext pandoc shared-mime-info
sudo wget https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types -O /etc/mime.types
- name: Generate a MO file from strings extracted from the project
run: python manage.py compilemessages
- name: Run tests
run: ~/.local/bin/pytest -n 2

View File

@@ -1,384 +0,0 @@
name: People Workflow
on:
push:
branches:
- main
pull_request:
branches:
- '*'
jobs:
lint-git:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' # Makes sense only for pull requests
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: show
run: git log
- name: Enforce absence of print statements in code
run: |
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude)**/people.yml' | grep "print("
- name: Check absence of fixup commits
run: |
! git log | grep 'fixup!'
- name: Install gitlint
run: pip install --user requests gitlint
- name: Lint commit messages added to main
run: ~/.local/bin/gitlint --commits origin/${{ github.event.pull_request.base.ref }}..HEAD
check-changelog:
runs-on: ubuntu-latest
if: |
contains(github.event.pull_request.labels.*.name, 'noChangeLog') == false &&
github.event_name == 'pull_request'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check that the CHANGELOG has been modified in the current branch
run: git whatchanged --name-only --pretty="" origin/${{ github.event.pull_request.base.ref }}..HEAD | grep CHANGELOG
lint-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check CHANGELOG max line length
run: |
max_line_length=$(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com" | wc -L)
if [ $max_line_length -ge 80 ]; then
echo "ERROR: CHANGELOG has lines longer than 80 characters."
exit 1
fi
install-front:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
with:
path: 'src/frontend/**/node_modules'
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Install dependencies
if: steps.front-node_modules.outputs.cache-hit != 'true'
run: cd src/frontend/ && yarn install --frozen-lockfile
- name: Cache install frontend
if: steps.front-node_modules.outputs.cache-hit != 'true'
uses: actions/cache@v4
with:
path: 'src/frontend/**/node_modules'
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
build-front:
runs-on: ubuntu-latest
needs: install-front
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
with:
path: 'src/frontend/**/node_modules'
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Build CI App
run: cd src/frontend/ && yarn ci:build
- name: Cache build frontend
uses: actions/cache@v4
with:
path: src/frontend/apps/desk/out/
key: build-front-${{ github.run_id }}
test-front:
runs-on: ubuntu-latest
needs: install-front
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
with:
path: 'src/frontend/**/node_modules'
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Test App
run: cd src/frontend/ && yarn app:test
lint-front:
runs-on: ubuntu-latest
needs: install-front
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
with:
path: 'src/frontend/**/node_modules'
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Check linting
run: cd src/frontend/ && yarn lint
test-e2e:
runs-on: ubuntu-latest
needs: [build-mails, build-front]
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set services env variables
run: |
make create-env-files
cat env.d/development/common.e2e.dist >> env.d/development/common
- name: Download mails' templates
uses: actions/download-artifact@v4
with:
name: mails-templates
path: src/backend/core/templates/mail
- name: Restore the frontend cache
uses: actions/cache@v4
id: front-node_modules
with:
path: 'src/frontend/**/node_modules'
key: front-node_modules-${{ hashFiles('src/frontend/**/yarn.lock') }}
- name: Restore the build cache
uses: actions/cache@v4
id: cache-build
with:
path: src/frontend/apps/desk/out/
key: build-front-${{ github.run_id }}
- name: Build and Start Docker Servers
env:
DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1
run: |
docker-compose build --pull --build-arg BUILDKIT_INLINE_CACHE=1
make run
- name: Apply DRF migrations
run: |
make migrate
- name: Add dummy data
run: |
make demo FLUSH_ARGS='--no-input'
- name: Install Playwright Browsers
run: cd src/frontend/apps/e2e && yarn install
- name: Run e2e tests
run: cd src/frontend/ && yarn e2e:test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: src/frontend/apps/e2e/report/
retention-days: 7
build-mails:
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/mail
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install yarn
run: npm install -g yarn
- name: Install node dependencies
run: yarn install --frozen-lockfile
- name: Build mails
run: yarn build
- name: Persist mails' templates
uses: actions/upload-artifact@v4
with:
name: mails-templates
path: src/backend/core/templates/mail
lint-back:
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/backend
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install development dependencies
run: pip install --user .[dev]
- name: Check code formatting with ruff
run: ~/.local/bin/ruff format . --diff
- name: Lint code with ruff
run: ~/.local/bin/ruff check .
- name: Lint code with pylint
run: ~/.local/bin/pylint .
test-back:
runs-on: ubuntu-latest
needs: build-mails
defaults:
run:
working-directory: src/backend
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: people
POSTGRES_USER: dinum
POSTGRES_PASSWORD: pass
ports:
- 5432:5432
# needed because the postgres container does not provide a healthcheck
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
env:
DJANGO_CONFIGURATION: Test
DJANGO_SETTINGS_MODULE: people.settings
DJANGO_SECRET_KEY: ThisIsAnExampleKeyForTestPurposeOnly
OIDC_OP_JWKS_ENDPOINT: /endpoint-for-test-purpose-only
DB_HOST: localhost
DB_NAME: people
DB_USER: dinum
DB_PASSWORD: pass
DB_PORT: 5432
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create writable /data
run: |
sudo mkdir -p /data/media && \
sudo mkdir -p /data/static
- name: Download mails' templates
uses: actions/download-artifact@v4
with:
name: mails-templates
path: src/backend/core/templates/mail
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install development dependencies
run: pip install --user .[dev]
- name: Install gettext (required to compile messages)
run: |
sudo apt-get update
sudo apt-get install -y gettext
- name: Generate a MO file from strings extracted from the project
run: python manage.py compilemessages
- name: Run tests
run: ~/.local/bin/pytest -n 2
i18n-crowdin:
runs-on: ubuntu-latest
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "people,secrets"
-
name: Checkout repository
uses: actions/checkout@v2
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
- name: Install gettext (required to make messages)
run: |
sudo apt-get update
sudo apt-get install -y gettext
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install development dependencies
working-directory: src/backend
run: pip install --user .[dev]
- name: Generate the translation base file
run: ~/.local/bin/django-admin makemessages --keep-pot --all
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/people/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
cache: 'yarn'
cache-dependency-path: src/frontend/yarn.lock
- name: Install dependencies
run: cd src/frontend/ && yarn install --frozen-lockfile
- name: Download sources from Crowdin to stay synchronized
run: |
docker run \
--rm \
-e CROWDIN_API_TOKEN=$CROWDIN_API_TOKEN \
-e CROWDIN_PROJECT_ID=$CROWDIN_PROJECT_ID \
-e CROWDIN_BASE_PATH=$CROWDIN_BASE_PATH \
-v "${{ github.workspace }}:/app" \
crowdin/cli:3.16.0 \
crowdin download sources -c /app/crowdin/config.yml
- name: Extract the frontend translation
run: make frontend-i18n-extract
- name: Upload files to Crowdin
run: |
docker run \
--rm \
-e CROWDIN_API_TOKEN=$CROWDIN_API_TOKEN \
-e CROWDIN_PROJECT_ID=$CROWDIN_PROJECT_ID \
-e CROWDIN_BASE_PATH=$CROWDIN_BASE_PATH \
-v "${{ github.workspace }}:/app" \
crowdin/cli:3.16.0 \
crowdin upload sources -c /app/crowdin/config.yml

View File

@@ -0,0 +1,34 @@
name: Release Chart
run-name: Release Chart
on:
push:
paths:
- src/helm/impress/**
jobs:
release:
# depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions
# see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cleanup
run: rm -rf ./src/helm/extra
- name: Install Helm
uses: azure/setup-helm@v4
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Publish Helm charts
uses: numerique-gouv/helm-gh-pages@add-overwrite-option
with:
charts_dir: ./src/helm
token: ${{ secrets.GITHUB_TOKEN }}

8
.gitignore vendored
View File

@@ -30,10 +30,10 @@ MANIFEST
.next/
# Translations # Translations
*.mo
*.pot
# Environments
.env
.venv
env/
venv/
@@ -50,9 +50,6 @@ node_modules
# Mails
src/backend/core/templates/mail/
# Typescript client
src/frontend/tsclient
# Swagger
**/swagger.json
@@ -78,5 +75,4 @@ db.sqlite3
.idea/
.vscode/
*.iml
.devcontainer/
.tool-versions
.devcontainer

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "secrets"]
path = secrets
url = ../secrets

10
.sops.yaml Normal file
View File

@@ -0,0 +1,10 @@
creation_rules:
- path_regex: ./*
key_groups:
- age:
- age15fyxdwmg5mvldtqqus87xspuws2u0cpvwheehrtvkexj4tnsqqysw6re2x # jacques
- age16hnlml8yv4ynwy0seer57g8qww075crd0g7nsundz3pj4wk7m3vqftszg7 # github-repo
- age1plkp8td6zzfcavjusmsfrlk54t9vn8jjxm8zaz7cmnr7kzl2nfnsd54hwg # Anthony Le-Courric
- age12g6f5fse25tgrwweleh4jls3qs52hey2edh759smulwmk5lnzadslu2cp3 # Antoine Lebaud
- age1hnhuzj96ktkhpyygvmz0x9h8mfvssz7ss6emmukags644mdhf4msajk93r # Samuel Paccoud

View File

@@ -7,3 +7,488 @@ and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [2.5.0] - 2025-03-18
## Added
- 📝(doc) Added GNU Make link to README #750
- ✨(frontend) add pinning on doc detail #711
- 🚩(frontend) feature flag analytic on copy as html #649
- ✨(frontend) Custom block divider with export #698
- 🌐(i18n) activate dutch language #742
- ✨(frontend) add Beautify action to AI transform #478
- ✨(frontend) add Emojify action to AI transform #478
## Changed
- 🧑‍💻(frontend) change literal section open source #702
- ♻️(frontend) replace cors proxy for export #695
- 🚨(gitlint) Allow uppercase in commit messages #756
- ♻️(frontend) Improve AI translations #478
## Fixed
- 🐛(frontend) SVG export #706
- 🐛(frontend) remove scroll listener table content #688
- 🔒️(back) restrict access to favorite_list endpoint #690
- 🐛(backend) refactor to fix filtering on children
and descendants views #695
- 🐛(action) fix notify-argocd workflow #713
- 🚨(helm) fix helmfile lint #736
- 🚚(frontend) redirect to 401 page when 401 error #759
## [2.4.0] - 2025-03-06
## Added
- ✨(frontend) synchronize language-choice #401
## Changed
- Use sentry tags instead of extra scope
## Fixed
- 🐛(frontend) fix collaboration error #684
## [2.3.0] - 2025-03-03
## Added
- ✨(backend) limit link reach/role select options depending on ancestors #645
- ✨(backend) add new "descendants" action to document API endpoint #645
- ✨(backend) new "tree" action on document detail endpoint #645
- ✨(backend) allow forcing page size within limits #645
- 💄(frontend) add error pages #643
- 🔒️ Manage unsafe attachments #663
- ✨(frontend) Custom block quote with export #646
- ✨(frontend) add open source section homepage #666
- ✨(frontend) synchronize language-choice #401
## Changed
- 🛂(frontend) Restore version visibility #629
- 📝(doc) minor README.md formatting and wording enhancements
-Stop setting a default title on doc creation #634
- ♻️(frontend) misc ui improvements #644
## Fixed
- 🐛(backend) allow any type of extensions for media download #671
- ♻️(frontend) improve table pdf rendering
- 🐛(email) invitation emails in receivers language
## [2.2.0] - 2025-02-10
## Added
- 📝(doc) Add security.md and codeofconduct.md #604
- ✨(frontend) add home page #608
- ✨(frontend) cursor display on activity #609
- ✨(frontend) Add export page break #623
## Changed
- 🔧(backend) make AI feature reach configurable #628
## Fixed
- 🌐(CI) Fix email partially translated #616
- 🐛(frontend) fix cursor breakline #609
- 🐛(frontend) fix style pdf export #609
## [2.1.0] - 2025-01-29
## Added
- ✨(backend) add soft delete and restore API endpoints to documents #516
- ✨(backend) allow organizing documents in a tree structure #516
- ✨(backend) add "excerpt" field to document list serializer #516
- ✨(backend) add github actions to manage Crowdin workflow #559 & #563
- 📈Integrate Posthog #540
- 🏷️(backend) add content-type to uploaded files #552
- ✨(frontend) export pdf docx front side #537
## Changed
- 💄(frontend) add abilities on doc row #581
- 💄(frontend) improve DocsGridItem responsive padding #582
- 🔧(backend) Bump maximum page size to 200 #516
- 📝(doc) Improve Read me #558
## Fixed
- 🐛Fix invitations #575
## Removed
- 🔥(backend) remove "content" field from list serializer # 516
## [2.0.1] - 2025-01-17
## Fixed
-🐛(frontend) share modal is shown when you don't have the abilities #557
-🐛(frontend) title copy break app #564
## [2.0.0] - 2025-01-13
## Added
- 🔧(backend) add option to configure list of essential OIDC claims #525 & #531
- 🔧(helm) add option to disable default tls setting by @dominikkaminski #519
- 💄(frontend) Add left panel #420
- 💄(frontend) add filtering to left panel #475
- ✨(frontend) new share modal ui #489
- ✨(frontend) add favorite feature #515
- 📝(documentation) Documentation about self-hosted installation #530
- ✨(helm) helm versioning #530
## Changed
- 🏗️(yjs-server) organize yjs server #528
- ♻️(frontend) better separation collaboration process #528
- 💄(frontend) updating the header and leftpanel for responsive #421
- 💄(frontend) update DocsGrid component #431
- 💄(frontend) update DocsGridOptions component #432
- 💄(frontend) update DocHeader ui #448
- 💄(frontend) update doc versioning ui #463
- 💄(frontend) update doc summary ui #473
- 📝(doc) update readme.md to match V2 changes #558 & #572
## Fixed
- 🐛(backend) fix create document via s2s if sub unknown but email found #543
- 🐛(frontend) hide search and create doc button if not authenticated #555
- 🐛(backend) race condition creation issue #556
## [1.10.0] - 2024-12-17
## Added
- ✨(backend) add server-to-server API endpoint to create documents #467
- ✨(email) white brand email #412
- ✨(y-provider) create a markdown converter endpoint #488
## Changed
- ⚡️(docker) improve y-provider image #422
## Fixed
- ⚡️(e2e) reduce flakiness on e2e tests #511
## Fixed
- 🐛(frontend) update doc editor height #481
- 💄(frontend) add doc search #485
## [1.9.0] - 2024-12-11
## Added
- ✨(backend) annotate number of accesses on documents in list view #429
- ✨(backend) allow users to mark/unmark documents as favorite #429
## Changed
- 🔒️(collaboration) increase collaboration access security #472
- 🔨(frontend) encapsulated title to its own component #474
- ⚡️(backend) optimize number of queries on document list view #429
- ♻️(frontend) stop to use provider with version #480
- 🚚(collaboration) change the websocket key name #480
## Fixed
- 🐛(frontend) fix initial content with collaboration #484
- 🐛(frontend) Fix hidden menu on Firefox #468
- 🐛(backend) fix sanitize problem IA #490
## [1.8.2] - 2024-11-28
## Changed
- ♻️(SW) change strategy html caching #460
## [1.8.1] - 2024-11-27
## Fixed
- 🐛(frontend) link not clickable and flickering firefox #457
## [1.8.0] - 2024-11-25
## Added
- 🌐(backend) add German translation #259
- 🌐(frontend) add German translation #255
- ✨(frontend) add a broadcast store #387
- ✨(backend) whitelist pod's IP address #443
- ✨(backend) config endpoint #425
- ✨(frontend) config endpoint #424
- ✨(frontend) add sentry #424
- ✨(frontend) add crisp chatbot #450
## Changed
- 🚸(backend) improve users similarity search and sort results #391
- ♻️(frontend) simplify stores #402
- ✨(frontend) update $css Box props type to add styled components RuleSet #423
- ✅(CI) trivy continue on error #453
## Fixed
- 🔧(backend) fix logging for docker and make it configurable by envar #427
- 🦺(backend) add comma to sub regex #408
- 🐛(editor) collaborative user tag hidden when read only #385
- 🐛(frontend) users have view access when revoked #387
- 🐛(frontend) fix placeholder editable when double clicks #454
## [1.7.0] - 2024-10-24
## Added
- 📝Contributing.md #352
- 🌐(frontend) add localization to editor #368
- ✨Public and restricted doc editable #357
- ✨(frontend) Add full name if available #380
- ✨(backend) Add view accesses ability #376
## Changed
- ♻️(frontend) list accesses if user has abilities #376
- ♻️(frontend) avoid documents indexing in search engine #372
- 👔(backend) doc restricted by default #388
## Fixed
- 🐛(backend) require right to manage document accesses to see invitations #369
- 🐛(i18n) same frontend and backend language using shared cookies #365
- 🐛(frontend) add default toolbar buttons #355
- 🐛(frontend) throttle error correctly display #378
## Removed
- 🔥(helm) remove infra related codes #366
## [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 #318
## Changed
- ♻️(frontend) more multi theme friendly #325
- ♻️ Bootstrap frontend #257
- ♻️ Add username in email #314
## 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
## Fixed
- 🐛(db) fix users duplicate #316
## [1.5.0] - 2024-10-09
## Added
- ✨(backend) add name fields to the user synchronized with OIDC #301
- ✨(ci) add security scan #291
- ♻️(frontend) Add versions #277
- ✨(frontend) one-click document creation #275
- ✨(frontend) edit title inline #275
- 📱(frontend) mobile responsive #304
- 🌐(frontend) Update translation #308
## Changed
- 💄(frontend) error alert closeable on editor #284
- ♻️(backend) Change email content #283
- 🛂(frontend) viewers and editors can access share modal #302
- ♻️(frontend) remove footer on doc editor #313
## Fixed
- 🛂(frontend) match email if no existing user matches the sub
- 🐛(backend) gitlab oicd userinfo endpoint #232
- 🛂(frontend) redirect to the OIDC when private doc and unauthentified #292
- ♻️(backend) getting list of document versions available for a user #258
- 🔧(backend) fix configuration to avoid different ssl warning #297
- 🐛(frontend) fix editor break line not working #302
## [1.4.0] - 2024-09-17
## Added
- ✨Add link public/authenticated/restricted access with read/editor roles #234
- ✨(frontend) add copy link button #235
- 🛂(frontend) access public docs without being logged #235
## Changed
- ♻️(backend) Allow null titles on documents for easier creation #234
- 🛂(backend) stop to list public doc to everyone #234
- 🚚(frontend) change visibility in share modal #235
- ⚡️(frontend) Improve summary #244
## Fixed
- 🐛(backend) Fix forcing ID when creating a document via API endpoint #234
- 🐛 Rebuild frontend dev container from makefile #248
## [1.3.0] - 2024-09-05
## Added
- ✨Add image attachments with access control
- ✨(frontend) Upload image to a document #211
- ✨(frontend) Summary #223
- ✨(frontend) update meta title for docs page #231
## Changed
- 💄(frontend) code background darkened on editor #214
- 🔥(frontend) hide markdown button if not text #213
## Fixed
- 🐛 Fix emoticon in pdf export #225
- 🐛 Fix collaboration on document #226
- 🐛 (docker) Fix compatibility with mac #230
## Removed
- 🔥(frontend) remove saving modal #213
## [1.2.1] - 2024-08-23
## Changed
- ♻️ Change ordering docs datagrid #195
- 🔥(helm) use scaleway email #194
## [1.2.0] - 2024-08-22
## Added
- 🎨(frontend) better conversion editor to pdf #151
- ✨Export docx (word) #161
- 🌐Internationalize invitation email #167
- ✨(frontend) White branding #164
- ✨Email invitation when add user to doc #171
- ✨Invitation management #174
## Fixed
- 🐛(y-webrtc) fix prob connection #147
- ⚡️(frontend) improve select share stability #159
- 🐛(backend) enable SSL when sending email #165
## Changed
- 🎨(frontend) stop limit layout height to screen size #158
- ⚡️(CI) only e2e chrome mandatory #177
## Removed
- 🔥(helm) remove htaccess #181
## [1.1.0] - 2024-07-15
## Added
- 🤡(demo) generate dummy documents on dev users #120
- ✨(frontend) create side modal component #134
- ✨(frontend) Doc grid actions (update / delete) #136
- ✨(frontend) Doc editor header information #137
## Changed
- ♻️(frontend) replace docs panel with docs grid #120
- ♻️(frontend) create a doc from a modal #132
- ♻️(frontend) manage members from the share modal #140
## [1.0.0] - 2024-07-02
## Added
- 🛂(frontend) Manage the document's right (#75)
- ✨(frontend) Update document (#68)
- ✨(frontend) Remove document (#68)
- 🐳(docker) dockerize dev frontend (#63)
- 👔(backend) list users with email filtering (#79)
- ✨(frontend) add user to a document (#52)
- ✨(frontend) invite user to a document (#52)
- 🛂(frontend) manage members (update role / list / remove) (#81)
- ✨(frontend) offline mode (#88)
- 🌐(frontend) translate cgu (#83)
- ✨(service-worker) offline doc management (#94)
- ⚗️(frontend) Add beta tag on logo (#121)
## Changed
- ♻️(frontend) Change site from Impress to Docs (#76)
- ✨(frontend) Generate PDF from a modal (#68)
- 🔧(helm) sticky session by request_uri for signaling server (#78)
- ♻️(frontend) change logo (#84)
- ♻️(frontend) pdf has title doc (#84)
- ⚡️(e2e) unique login between tests (#80)
- ⚡️(CI) improve e2e job (#86)
- ♻️(frontend) improve the error and message info ui (#93)
- ✏️(frontend) change all occurences of pad to doc (#99)
## Fixed
- 🐛(frontend) Fix the break line when generate PDF (#84)
## Delete
- 💚(CI) Remove trigger workflow on push tags on CI (#68)
- 🔥(frontend) Remove coming soon page (#121)
## [0.1.0] - 2024-05-24
## Added
- ✨(frontend) Coming Soon page (#67)
- 🚀 Impress, project to manage your documents easily and collaboratively.
[unreleased]: https://github.com/numerique-gouv/impress/compare/v2.5.0...main
[v2.5.0]: https://github.com/numerique-gouv/impress/releases/v2.5.0
[v2.4.0]: https://github.com/numerique-gouv/impress/releases/v2.4.0
[v2.3.0]: https://github.com/numerique-gouv/impress/releases/v2.3.0
[v2.2.0]: https://github.com/numerique-gouv/impress/releases/v2.2.0
[v2.1.0]: https://github.com/numerique-gouv/impress/releases/v2.1.0
[v2.0.1]: https://github.com/numerique-gouv/impress/releases/v2.0.1
[v2.0.0]: https://github.com/numerique-gouv/impress/releases/v2.0.0
[v1.10.0]: https://github.com/numerique-gouv/impress/releases/v1.10.0
[v1.9.0]: https://github.com/numerique-gouv/impress/releases/v1.9.0
[v1.8.2]: https://github.com/numerique-gouv/impress/releases/v1.8.2
[v1.8.1]: https://github.com/numerique-gouv/impress/releases/v1.8.1
[v1.8.0]: https://github.com/numerique-gouv/impress/releases/v1.8.0
[v1.7.0]: https://github.com/numerique-gouv/impress/releases/v1.7.0
[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
[1.3.0]: https://github.com/numerique-gouv/impress/releases/v1.3.0
[1.2.1]: https://github.com/numerique-gouv/impress/releases/v1.2.1
[1.2.0]: https://github.com/numerique-gouv/impress/releases/v1.2.0
[1.1.0]: https://github.com/numerique-gouv/impress/releases/v1.1.0
[1.0.0]: https://github.com/numerique-gouv/impress/releases/v1.0.0
[0.1.0]: https://github.com/numerique-gouv/impress/releases/v0.1.0

75
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,75 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
## Scope
- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Enforcement
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at docs@numerique.gouv.fr.
- All complaints will be reviewed and investigated promptly and fairly.
- All community leaders are obligated to respect the privacy and security of the reporter of any incident.
## Enforcement Guidelines
- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this
## Code of Conduct:
1. Correction
Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
2. Warning
Community Impact: A violation through a single incident or series of actions.
Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
3. Temporary Ban
Community Impact: A serious violation of community standards, including sustained inappropriate behavior.
Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
4. Permanent Ban
Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
Consequence: A permanent ban from any sort of public interaction within the community.
Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.
For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.

86
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,86 @@
# 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/suitenumerique/docs/blob/main/README.md) for detailed instructions.
Please also check out our [dev handbook](https://suitenumerique.gitbook.io/handbook) to learn our best practices.
## Help us with translations
You can help us with translations on [Crowdin](https://crowdin.com/project/lasuite-docs).
Your language is not there? Request it on our Crowdin page 😊.
## 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).
## Selecting an issue
We use a [GitHub Project](https://github.com/orgs/numerique-gouv/projects/13) in order to prioritize our workload.
Please check in priority the issues that are in the **todo** column and have a higher priority (P0 -> P2).
## 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.
* **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.
```
## Changelog Update
Please add a line to the changelog describing your development. The changelog entry should include a brief summary of the changes, this helps in tracking changes effectively and keeping everyone informed. We usually include the title of the pull request, followed by the pull request ID to finish the log entry. The changelog line should be less than 80 characters in total.
### Example Changelog Message
```
## [Unreleased]
## Added
- ✨(frontend) add AI to the project #321
```
## 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. If you can, add some pictures or a small video to show the changes.
### Don't forget to:
- check your commits
- check the linting: `make lint && make frontend-lint`
- check the tests: `make test`
- add a changelog entry
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! 👍

View File

@@ -1,67 +1,17 @@
# Django People
# Django impress
# ---- base image to inherit from ----
FROM python:3.10-slim-bullseye as base
FROM python:3.12.6-alpine3.20 AS base
# Upgrade pip to its latest release to speed up dependencies installation
RUN python -m pip install --upgrade pip
RUN python -m pip install --upgrade pip setuptools
# Upgrade system packages to install security updates
RUN apt-get update && \
apt-get -y upgrade && \
rm -rf /var/lib/apt/lists/*
### ---- Front-end dependencies image ----
FROM node:20 as frontend-deps
WORKDIR /deps
COPY ./src/frontend/package.json ./package.json
COPY ./src/frontend/yarn.lock ./yarn.lock
COPY ./src/frontend/apps/desk/package.json ./apps/desk/package.json
COPY ./src/frontend/packages/i18n/package.json ./packages/i18n/package.json
COPY ./src/frontend/packages/eslint-config-people/package.json ./packages/eslint-config-people/package.json
RUN yarn --frozen-lockfile
### ---- Front-end builder dev image ----
FROM node:20 as frontend-builder-dev
WORKDIR /builder
COPY --from=frontend-deps /deps/node_modules ./node_modules
COPY ./src/frontend .
WORKDIR ./apps/desk
### ---- Front-end builder image ----
FROM frontend-builder-dev as frontend-builder
RUN yarn build
# ---- Front-end image ----
FROM nginxinc/nginx-unprivileged:1.25 as frontend-production
# Un-privileged user running the application
ARG DOCKER_USER
USER ${DOCKER_USER}
COPY --from=frontend-builder \
/builder/apps/desk/out \
/usr/share/nginx/html
COPY ./src/frontend/apps/desk/conf/default.conf /etc/nginx/conf.d
# Copy entrypoint
COPY ./docker/files/usr/local/bin/entrypoint /usr/local/bin/entrypoint
ENTRYPOINT [ "/usr/local/bin/entrypoint" ]
CMD ["nginx", "-g", "daemon off;"]
RUN apk update && \
apk upgrade
# ---- Back-end builder image ----
FROM base as back-builder
FROM base AS back-builder
WORKDIR /builder
@@ -73,7 +23,7 @@ RUN mkdir /install && \
# ---- mails ----
FROM node:20 as mail-builder
FROM node:20 AS mail-builder
COPY ./src/mail /mail/app
@@ -84,48 +34,48 @@ RUN yarn install --frozen-lockfile && \
# ---- static link collector ----
FROM base as link-collector
ARG PEOPLE_STATIC_ROOT=/data/static
FROM base AS link-collector
ARG IMPRESS_STATIC_ROOT=/data/static
# Install libpangocairo & rdfind
RUN apt-get update && \
apt-get install -y \
libpangocairo-1.0-0 \
rdfind && \
rm -rf /var/lib/apt/lists/*
# Install pango & rdfind
RUN apk add \
pango \
rdfind
# Copy installed python dependencies
COPY --from=back-builder /install /usr/local
# Copy people application (see .dockerignore)
# Copy impress application (see .dockerignore)
COPY ./src/backend /app/
WORKDIR /app
# collectstatic
RUN DJANGO_CONFIGURATION=Build DJANGO_JWT_PRIVATE_SIGNING_KEY=Dummy \
RUN DJANGO_CONFIGURATION=Build \
python manage.py collectstatic --noinput
# Replace duplicated file by a symlink to decrease the overall size of the
# final image
RUN rdfind -makesymlinks true -followsymlinks true -makeresultsfile false ${PEOPLE_STATIC_ROOT}
RUN rdfind -makesymlinks true -followsymlinks true -makeresultsfile false ${IMPRESS_STATIC_ROOT}
# ---- Core application image ----
FROM base as core
FROM base AS core
ENV PYTHONUNBUFFERED=1
# Install required system libs
RUN apt-get update && \
apt-get install -y \
gettext \
libcairo2 \
libffi-dev \
libgdk-pixbuf2.0-0 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
shared-mime-info && \
rm -rf /var/lib/apt/lists/*
RUN apk add \
cairo \
file \
font-noto \
font-noto-emoji \
gettext \
gdk-pixbuf \
libffi-dev \
pango \
shared-mime-info
RUN wget https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types -O /etc/mime.types
# Copy entrypoint
COPY ./docker/files/usr/local/bin/entrypoint /usr/local/bin/entrypoint
@@ -138,30 +88,33 @@ RUN chmod g=u /etc/passwd
# Copy installed python dependencies
COPY --from=back-builder /install /usr/local
# Copy people application (see .dockerignore)
# Copy impress application (see .dockerignore)
COPY ./src/backend /app/
WORKDIR /app
# Generate compiled translation messages
RUN DJANGO_CONFIGURATION=Build \
python manage.py compilemessages
# We wrap commands run in this container by the following entrypoint that
# creates a user on-the-fly with the container user ID (see USER) and root group
# ID.
ENTRYPOINT [ "/usr/local/bin/entrypoint" ]
# ---- Development image ----
FROM core as backend-development
FROM core AS backend-development
# Switch back to the root user to install development dependencies
USER root:root
# Install psql
RUN apt-get update && \
apt-get install -y postgresql-client && \
rm -rf /var/lib/apt/lists/*
RUN apk add postgresql-client
# Uninstall people and re-install it in editable mode along with development
# Uninstall impress and re-install it in editable mode along with development
# dependencies
RUN pip uninstall -y people
RUN pip uninstall -y impress
RUN pip install -e .[dev]
# Restore the un-privileged user running the application
@@ -177,23 +130,23 @@ ENV DB_HOST=postgresql \
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
# ---- Production image ----
FROM core as backend-production
FROM core AS backend-production
ARG PEOPLE_STATIC_ROOT=/data/static
ARG IMPRESS_STATIC_ROOT=/data/static
# Gunicorn
RUN mkdir -p /usr/local/etc/gunicorn
COPY docker/files/usr/local/etc/gunicorn/people.py /usr/local/etc/gunicorn/people.py
COPY docker/files/usr/local/etc/gunicorn/impress.py /usr/local/etc/gunicorn/impress.py
# Un-privileged user running the application
ARG DOCKER_USER
USER ${DOCKER_USER}
# Copy statics
COPY --from=link-collector ${PEOPLE_STATIC_ROOT} ${PEOPLE_STATIC_ROOT}
COPY --from=link-collector ${IMPRESS_STATIC_ROOT} ${IMPRESS_STATIC_ROOT}
# Copy people mails
# Copy impress mails
COPY --from=mail-builder /mail/backend/core/templates/mail /app/core/templates/mail
# The default command runs gunicorn WSGI server in people's main module
CMD ["gunicorn", "-c", "/usr/local/etc/gunicorn/people.py", "people.wsgi:application"]
# The default command runs gunicorn WSGI server in impress's main module
CMD ["gunicorn", "-c", "/usr/local/etc/gunicorn/impress.py", "impress.wsgi:application"]

124
Makefile
View File

@@ -44,17 +44,14 @@ COMPOSE_EXEC_APP = $(COMPOSE_EXEC) app-dev
COMPOSE_RUN = $(COMPOSE) run --rm
COMPOSE_RUN_APP = $(COMPOSE_RUN) app-dev
COMPOSE_RUN_CROWDIN = $(COMPOSE_RUN) crowdin crowdin
WAIT_DB = @$(COMPOSE_RUN) dockerize -wait tcp://$(DB_HOST):$(DB_PORT) -timeout 60s
WAIT_KC_DB = $(COMPOSE_RUN) dockerize -wait tcp://kc_postgresql:5432 -timeout 60s
# -- Backend
MANAGE = $(COMPOSE_RUN_APP) python manage.py
MAIL_YARN = $(COMPOSE_RUN) -w /app/src/mail node yarn
TSCLIENT_YARN = $(COMPOSE_RUN) -w /app/src/tsclient node yarn
# -- Frontend
PATH_FRONT = ./src/frontend
PATH_FRONT_DESK = $(PATH_FRONT)/apps/desk
PATH_FRONT_IMPRESS = $(PATH_FRONT)/apps/impress
# ==============================================================================
# RULES
@@ -77,25 +74,43 @@ create-env-files: \
env.d/development/kc_postgresql
.PHONY: create-env-files
bootstrap: ## Prepare Docker images for the project and install frontend dependencies
bootstrap: ## Prepare Docker images for the project
bootstrap: \
data/media \
data/static \
create-env-files \
build \
run \
migrate \
demo \
back-i18n-compile \
mails-install \
mails-build \
install-front-desk
run
.PHONY: bootstrap
# -- Docker/compose
build: ## build the app-dev container
@$(COMPOSE) build app-dev
build: cache ?= --no-cache
build: ## build the project containers
@$(MAKE) build-backend cache=$(cache)
@$(MAKE) build-yjs-provider cache=$(cache)
@$(MAKE) build-frontend cache=$(cache)
.PHONY: build
build-backend: cache ?=
build-backend: ## build the app-dev container
@$(COMPOSE) build app-dev $(cache)
.PHONY: build-backend
build-yjs-provider: cache ?=
build-yjs-provider: ## build the y-provider container
@$(COMPOSE) build y-provider $(cache)
.PHONY: build-yjs-provider
build-frontend: cache ?=
build-frontend: ## build the frontend container
@$(COMPOSE) build frontend $(cache)
.PHONY: build-frontend
down: ## stop and remove containers, networks, images, and volumes
@$(COMPOSE) down
.PHONY: down
@@ -104,14 +119,16 @@ logs: ## display app-dev logs (follow mode)
@$(COMPOSE) logs -f app-dev
.PHONY: logs
run: ## start the wsgi (production) and development server
@$(COMPOSE) up --force-recreate -d nginx
@$(COMPOSE) up --force-recreate -d app-dev
run-backend: ## Start only the backend application and all needed services
@$(COMPOSE) up --force-recreate -d celery-dev
@$(COMPOSE) up --force-recreate -d keycloak
@echo "Wait for postgresql to be up..."
@$(WAIT_KC_DB)
@$(WAIT_DB)
@$(COMPOSE) up --force-recreate -d y-provider
@$(COMPOSE) up --force-recreate -d nginx
.PHONY: run-backend
run: ## start the wsgi (production) and development server
run:
@$(MAKE) run-backend
@$(COMPOSE) up --force-recreate -d frontend
.PHONY: run
status: ## an alias for "docker compose ps"
@@ -166,30 +183,21 @@ test-back-parallel: ## run all back-end tests in parallel
bin/pytest -n auto $${args:-${1}}
.PHONY: test-back-parallel
makemigrations: ## run django makemigrations for the people project.
makemigrations: ## run django makemigrations for the impress project.
@echo "$(BOLD)Running makemigrations$(RESET)"
@$(COMPOSE) up -d postgresql
@$(WAIT_DB)
@$(MANAGE) makemigrations $(ARGS)
@$(MANAGE) makemigrations
.PHONY: makemigrations
migrate: ## run django migrations for the people project.
migrate: ## run django migrations for the impress project.
@echo "$(BOLD)Running migrations$(RESET)"
@$(COMPOSE) up -d postgresql
@$(WAIT_DB)
@$(MANAGE) migrate $(ARGS)
@$(MANAGE) migrate
.PHONY: migrate
showmigrations: ## run django showmigrations for the people project.
@echo "$(BOLD)Running showmigrations$(RESET)"
@$(COMPOSE) up -d postgresql
@$(WAIT_DB)
@$(MANAGE) showmigrations $(ARGS)
.PHONY: showmigrations
superuser: ## Create an admin superuser with password "admin"
@echo "$(BOLD)Creating a Django superuser$(RESET)"
@$(MANAGE) createsuperuser --admin_email admin@example.com --password admin
@$(MANAGE) createsuperuser --email admin@example.com --password admin
.PHONY: superuser
back-i18n-compile: ## compile the gettext files
@@ -197,7 +205,7 @@ back-i18n-compile: ## compile the gettext files
.PHONY: back-i18n-compile
back-i18n-generate: ## create the .pot files used for i18n
@$(MANAGE) makemessages -a --keep-pot
@$(MANAGE) makemessages -a --keep-pot --all
.PHONY: back-i18n-generate
shell: ## connect to database shell
@@ -231,7 +239,7 @@ env.d/development/kc_postgresql:
env.d/development/crowdin:
cp -n env.d/development/crowdin.dist env.d/development/crowdin
crowdin-download: ## Download translated message from Crowdin
crowdin-download: ## Download translated message from crowdin
@$(COMPOSE_RUN_CROWDIN) download -c crowdin/config.yml
.PHONY: crowdin-download
@@ -239,7 +247,7 @@ crowdin-download-sources: ## Download sources from Crowdin
@$(COMPOSE_RUN_CROWDIN) download sources -c crowdin/config.yml
.PHONY: crowdin-download-sources
crowdin-upload: ## Upload source translations to Crowdin
crowdin-upload: ## Upload source translations to crowdin
@$(COMPOSE_RUN_CROWDIN) upload sources -c crowdin/config.yml
.PHONY: crowdin-upload
@@ -286,35 +294,30 @@ mails-install: ## install the mail generator
@$(MAIL_YARN) install
.PHONY: mails-install
# -- TS client generator
tsclient-install: ## Install the Typescript API client generator
@$(TSCLIENT_YARN) install
.PHONY: tsclient-install
tsclient: tsclient-install ## Generate a Typescript API client
@$(TSCLIENT_YARN) generate:api:client:local ../frontend/tsclient
.PHONY: tsclient-install
# -- Misc
clean: ## restore repository state as it was freshly cloned
git clean -idx
.PHONY: clean
help:
@echo "$(BOLD)People Makefile"
@echo "$(BOLD)impress Makefile"
@echo "Please use 'make $(BOLD)target$(RESET)' where $(BOLD)target$(RESET) is one of:"
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-30s$(RESET) %s\n", $$1, $$2}'
.PHONY: help
# Front
install-front-desk: ## Install the frontend dependencies of app Desk
cd $(PATH_FRONT_DESK) && yarn
.PHONY: install-front-desk
# Front
frontend-development-install: ## install the frontend locally
cd $(PATH_FRONT_IMPRESS) && yarn
.PHONY: frontend-development-install
run-front-desk: ## Start app Desk
cd $(PATH_FRONT_DESK) && yarn dev
.PHONY: run-front-desk
frontend-lint: ## run the frontend linter
cd $(PATH_FRONT) && yarn lint
.PHONY: frontend-lint
run-frontend-development: ## Run the frontend in development mode
@$(COMPOSE) stop frontend
cd $(PATH_FRONT_IMPRESS) && yarn dev
.PHONY: run-frontend-development
frontend-i18n-extract: ## Extract the frontend translation inside a json to be used for crowdin
cd $(PATH_FRONT) && yarn i18n:extract
@@ -331,10 +334,21 @@ frontend-i18n-compile: ## Format the crowin json files used deploy to the apps
.PHONY: frontend-i18n-compile
# -- K8S
start-kind: ## Create the kubernetes cluster
build-k8s-cluster: ## build the kubernetes cluster using kind
./bin/start-kind.sh
.PHONY: start-kind
.PHONY: build-k8s-cluster
tilt-up: ## start tilt - k8s local development
start-tilt: ## start the kubernetes cluster using kind
tilt up -f ./bin/Tiltfile
.PHONY: tilt-up
.PHONY: build-k8s-cluster
bump-packages-version: VERSION_TYPE ?= minor
bump-packages-version: ## bump the version of the project - VERSION_TYPE can be "major", "minor", "patch"
cd ./src/mail && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/apps/e2e/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/apps/impress/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/servers/y-provider/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/packages/eslint-config-impress/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
cd ./src/frontend/packages/i18n/ && yarn version --no-git-tag-version --$(VERSION_TYPE)
.PHONY: bump-packages-version

194
README.md
View File

@@ -1,98 +1,200 @@
# People
<p align="center">
<a href="https://github.com/suitenumerique/docs">
<img alt="Docs" src="/docs/assets/docs-logo.png" width="300" />
</a>
</p>
People is an application to handle users and teams.
<p align="center">
Welcome to Docs! The open source document editor where your notes can become knowledge through live collaboration
</p>
As of today, this project is **not yet ready for production**. Expect breaking changes.
<p align="center">
<a href="https://matrix.to/#/#docs-official:matrix.org">
Chat on Matrix
</a> - <a href="/docs/">
Documentation
</a> - <a href="#getting-started-">
Getting started
</a> - <a href="mailto:docs@numerique.gouv.fr">
Reach out
</a>
</p>
People is built on top of [Django Rest
Framework](https://www.django-rest-framework.org/).
<img src="/docs/assets/docs_live_collaboration_light.gif" width="100%" align="center"/>
## Getting started
## Why use Docs ❓
### Prerequisite
Docs is a collaborative text editor designed to address common challenges in knowledge building and sharing.
Make sure you have a recent version of Docker and [Docker
Compose](https://docs.docker.com/compose/install) installed on your laptop:
### Write
* 😌 Simple collaborative editing without the formatting complexity of markdown
* 🔌 Offline? No problem, keep writing, your edits will get synced when back online
* 💅 Create clean documents with limited but beautiful formatting options and focus on content
* 🧱 Built for productivity (markdown support, many block types, slash commands, keyboard shortcuts).
* ✨ Save time thanks to our AI actions (generate, sum up, correct, translate)
```bash
### Collaborate
* 🤝 Collaborate with your team in real time
* 🔒 Granular access control to ensure your information is secure and only shared with the right people
* 📑 Professional document exports in multiple formats (.odt, .doc, .pdf) with customizable templates
* 📚 Built-in wiki functionality to turn your team's collaborative work into organized knowledge `ETA 02/2025`
### Self-host
* 🚀 Easy to install, scalable and secure alternative to Notion, Outline or Confluence
## Getting started 🔧
### Test it
Test Docs on your browser by logging in on this [environment](https://impress-preprod.beta.numerique.gouv.fr/)
```
email: test.docs@yopmail.com
password: I'd<3ToTestDocs
```
### Run it locally
> ⚠️ Running Docs locally using the methods described below is for testing purposes only. It is based on building Docs using Minio as the S3 storage solution but you can choose any S3 compatible object storage of your choice.
**Prerequisite**
Make sure you have a recent version of Docker and [Docker Compose](https://docs.docker.com/compose/install) installed on your laptop:
```shellscript
$ docker -v
Docker version 20.10.2, build 2291f61
$ docker compose -v
docker compose version 1.27.4, build 40524192
Docker version 20.10.2, build 2291f61
$ docker compose version
Docker Compose version v2.32.4
```
> ⚠️ You may need to run the following commands with `sudo` but this can be
> avoided by assigning your user to the `docker` group.
> ⚠️ You may need to run the following commands with sudo but this can be avoided by adding your user to the `docker` group.
### Project bootstrap
**Project bootstrap**
The easiest way to start working on the project is to use GNU Make:
The easiest way to start working on the project is to use [GNU Make](https://www.gnu.org/software/make/):
```bash
$ make bootstrap
```shellscript
$ make bootstrap FLUSH_ARGS='--no-input'
```
This command builds the `app` container, installs dependencies, performs
database migrations and compile translations. It's a good idea to use this
command each time you are pulling code from the project repository to avoid
dependency-related or migration-related issues.
This command builds the `app` container, installs dependencies, performs database migrations and compile translations. It's a good idea to use this command each time you are pulling code from the project repository to avoid dependency-related or migration-related issues.
Your Docker services should now be up and running 🎉
Note that if you need to run them afterward, you can use the eponym Make rule:
You can access to the project by going to <http://localhost:3000>.
```bash
You will be prompted to log in, the default credentials are:
```
username: impress
password: impress
```
📝 Note that if you need to run them afterwards, you can use the eponym Make rule:
```shellscript
$ make run
```
### Adding content
⚠️ For the frontend developer, it is often better to run the frontend in development mode locally.
To do so, install the frontend dependencies with the following command:
```shellscript
$ make frontend-development-install
```
And run the frontend locally in development mode with the following command:
```shellscript
$ make run-frontend-development
```
To start all the services, except the frontend container, you can use the following command:
```shellscript
$ make run-backend
```
**Adding content**
You can create a basic demo site by running:
$ make demo
```shellscript
$ make demo
```
Finally, you can check all available Make rules using:
```bash
```shellscript
$ make help
```
### Django admin
**Django admin**
You can access the Django admin site at
[http://localhost:8071/admin](http://localhost:8071/admin).
<http://localhost:8071/admin>.
You first need to create a superuser account:
```bash
```shellscript
$ make superuser
```
You can then login with credentials `admin@example` / `admin`.
## Feedback 🙋‍♂️🙋‍♀️
### Run frontend
We'd love to hear your thoughts and hear about your experiments, so come and say hi on [Matrix](https://matrix.to/#/#docs-official:matrix.org).
Run the front with:
## Roadmap
```bash
$ make run-front-desk
Want to know where the project is headed? [🗺️ Checkout our roadmap](https://github.com/orgs/numerique-gouv/projects/13/views/11)
## Licence 📝
This work is released under the MIT License (see [LICENSE](https://github.com/suitenumerique/docs/blob/main/LICENSE)).
While Docs is a public driven initiative our licence choice is an invitation for private sector actors to use, sell and contribute to the project.
## Contributing 🙌
This project is intended to be community-driven, so please, do not hesitate to [get in touch](https://matrix.to/#/#docs-official:matrix.org) if you have any question related to our implementation or design decisions.
You can help us with translations on [Crowdin](https://crowdin.com/project/lasuite-docs).
If you intend to make pull requests see [CONTRIBUTING](https://github.com/suitenumerique/docs/blob/main/CONTRIBUTING.md) for guidelines.
Directory structure:
```markdown
docs
├── bin - executable scripts or binaries that are used for various tasks, such as setup scripts, utility scripts, or custom commands.
├── crowdin - for crowdin translations, a tool or service that helps manage translations for the project.
├── docker - Dockerfiles and related configuration files used to build Docker images for the project. These images can be used for development, testing, or production environments.
├── docs - documentation for the project, including user guides, API documentation, and other helpful resources.
├── env.d/development - environment-specific configuration files for the development environment. These files might include environment variables, configuration settings, or other setup files needed for development.
├── gitlint - configuration files for `gitlint`, a tool that enforces commit message guidelines to ensure consistency and quality in commit messages.
├── playground - experimental or temporary code, where developers can test new features or ideas without affecting the main codebase.
└── src - main source code directory, containing the core application code, libraries, and modules of the project.
```
Then access at
[http://localhost:3000](http://localhost:3000)
## Credits ❤️
user: people
### Stack
password: people
Docs is built on top of [Django Rest Framework](https://www.django-rest-framework.org/), [Next.js](https://nextjs.org/), [BlockNote.js](https://www.blocknotejs.org/), [HocusPocus](https://tiptap.dev/docs/hocuspocus/introduction) and [Yjs](https://yjs.dev/).
### Gov ❤️ open source
## Contributing
Docs is the result of a joint effort led by the French 🇫🇷🥖 ([DINUM](https://www.numerique.gouv.fr/dinum/)) and German 🇩🇪🥨 governments ([ZenDiS](https://zendis.de/)).
This project is intended to be community-driven, so please, do not hesitate to
get in touch if you have any question related to our implementation or design
decisions.
We are proud sponsors of [BlockNotejs](https://www.blocknotejs.org/) and [Yjs](https://yjs.dev/).
## License
We are always looking for new public partners (we are currently onboarding the Netherlands 🇳🇱🧀), feel free to [reach out](mailto:docs@numerique.gouv.fr) if you are interested in using or contributing to Docs.
This work is released under the MIT License (see [LICENSE](./LICENSE)).
<p align="center">
<img src="/docs/assets/europe_opensource.png" width="50%"/>
</p>

23
SECURITY.md Normal file
View File

@@ -0,0 +1,23 @@
# Security Policy
## Reporting a Vulnerability
Security is very important to us.
If you have any issue regarding security, please disclose the information responsibly submiting [this form](https://vdp.numerique.gouv.fr/p/Send-a-report?lang=en) and not by creating an issue on the repository. You can also email us at docs@numerique.gouv.fr
We appreciate your effort to make Docs more secure.
## Vulnerability disclosure policy
Working with security issues in an open source project can be challenging, as we are required to disclose potential problems that could be exploited by attackers. With this in mind, our security fix policy is as follows:
1. The Maintainers team will handle the fix as usual (Pull Request,
release).
2. In the release notes, we will include the identification numbers from the
GitHub Advisory Database (GHSA) and, if applicable, the Common Vulnerabilities
and Exposures (CVE) identifier for the vulnerability.
3. Once this grace period has passed, we will publish the vulnerability.
By adhering to this security policy, we aim to address security concerns
effectively and responsibly in our open source software project.

View File

@@ -15,3 +15,8 @@ the following command inside your docker container:
(Note : in your development environment, you can `make migrate`.)
## [Unreleased]
- AI features are now limited to users who are authenticated. Before this release, even anonymous
users who gained editor access on a document with link reach used to get AI feature.
IF you want anonymous users to keep access on AI features, you must now define the
`AI_ALLOW_REACH_FROM` setting to "public".

View File

@@ -1,9 +1,9 @@
load('ext://uibutton', 'cmd_button', 'bool_input', 'location')
load('ext://namespace', 'namespace_create', 'namespace_inject')
namespace_create('desk')
namespace_create('impress')
docker_build(
'localhost:5001/people-backend:latest',
'localhost:5001/impress-backend:latest',
context='..',
dockerfile='../Dockerfile',
only=['./src/backend', './src/mail', './docker'],
@@ -18,28 +18,38 @@ docker_build(
)
docker_build(
'localhost:5001/people-frontend:latest',
'localhost:5001/impress-y-provider:latest',
context='..',
dockerfile='../Dockerfile',
build_args={'ENV': 'dev'},
only=['./src/frontend', './src/mail', './docker'],
target = 'frontend-builder-dev',
dockerfile='../src/frontend/servers/y-provider/Dockerfile',
only=['./src/frontend/', './docker/', './.dockerignore'],
target = 'y-provider',
live_update=[
sync('../src/frontend', '/builder'),
sync('../src/frontend/servers/y-provider/src', '/home/frontend/servers/y-provider/src'),
]
)
k8s_yaml(local('cd ../src/helm && helmfile -n desk -e dev template .'))
docker_build(
'localhost:5001/impress-frontend:latest',
context='..',
dockerfile='../src/frontend/Dockerfile',
only=['./src/frontend', './docker', './.dockerignore'],
target = 'impress',
live_update=[
sync('../src/frontend', '/home/frontend'),
]
)
k8s_yaml(local('cd ../src/helm && helmfile -n impress -e dev template .'))
migration = '''
set -eu
# get k8s pod name from tilt resource name
POD_NAME="$(tilt get kubernetesdiscovery desk-backend -ojsonpath='{.status.pods[0].name}')"
kubectl -n desk exec "$POD_NAME" -- python manage.py makemigrations
POD_NAME="$(tilt get kubernetesdiscovery impress-backend -ojsonpath='{.status.pods[0].name}')"
kubectl -n impress exec "$POD_NAME" -- python manage.py makemigrations
'''
cmd_button('Make migration',
argv=['sh', '-c', migration],
resource='desk-backend',
resource='impress-backend',
icon_name='developer_board',
text='Run makemigration',
)
@@ -47,12 +57,12 @@ cmd_button('Make migration',
pod_migrate = '''
set -eu
# get k8s pod name from tilt resource name
POD_NAME="$(tilt get kubernetesdiscovery desk-backend -ojsonpath='{.status.pods[0].name}')"
kubectl -n desk exec "$POD_NAME" -- python manage.py migrate --no-input
POD_NAME="$(tilt get kubernetesdiscovery impress-backend -ojsonpath='{.status.pods[0].name}')"
kubectl -n impress exec "$POD_NAME" -- python manage.py migrate --no-input
'''
cmd_button('Migrate db',
argv=['sh', '-c', pod_migrate],
resource='desk-backend',
resource='impress-backend',
icon_name='developer_board',
text='Run database migration',
)

View File

@@ -7,7 +7,6 @@ UNSET_USER=0
TERRAFORM_DIRECTORY="./env.d/terraform"
COMPOSE_FILE="${REPO_DIR}/docker-compose.yml"
COMPOSE_PROJECT="people"
# _set_user: set (or unset) default user id used to run docker commands
@@ -40,9 +39,8 @@ function _set_user() {
# ARGS : docker compose command arguments
function _docker_compose() {
echo "🐳(compose) project: '${COMPOSE_PROJECT}' file: '${COMPOSE_FILE}'"
echo "🐳(compose) file: '${COMPOSE_FILE}'"
docker compose \
-p "${COMPOSE_PROJECT}" \
-f "${COMPOSE_FILE}" \
--project-directory "${REPO_DIR}" \
"$@"

View File

102
bin/start-kind.sh Normal file → Executable file
View File

@@ -1,102 +1,2 @@
#!/bin/sh
set -o errexit
CURRENT_DIR=$(pwd)
# 0. Create ca
echo "0. Create ca"
mkcert -install
cd /tmp
mkcert "127.0.0.1.nip.io" "*.127.0.0.1.nip.io"
cd $CURRENT_DIR
# 1. Create registry container unless it already exists
echo "1. Create registry container unless it already exists"
reg_name='kind-registry'
reg_port='5001'
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
docker run \
-d --restart=always -p "127.0.0.1:${reg_port}:5000" --network bridge --name "${reg_name}" \
registry:2
fi
# 2. Create kind cluster with containerd registry config dir enabled
echo "2. Create kind cluster with containerd registry config dir enabled"
# TODO: kind will eventually enable this by default and this patch will
# be unnecessary.
#
# See:
# https://github.com/kubernetes-sigs/kind/issues/2875
# https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration
# See: https://github.com/containerd/containerd/blob/main/docs/hosts.md
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
nodes:
- role: control-plane
image: kindest/node:v1.27.3
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker
image: kindest/node:v1.27.3
- role: worker
image: kindest/node:v1.27.3
EOF
# 3. Add the registry config to the nodes
echo "3. Add the registry config to the nodes"
#
# This is necessary because localhost resolves to loopback addresses that are
# network-namespace local.
# In other words: localhost in the container is not localhost on the host.
#
# We want a consistent name that works from both ends, so we tell containerd to
# alias localhost:${reg_port} to the registry container when pulling images
REGISTRY_DIR="/etc/containerd/certs.d/localhost:${reg_port}"
for node in $(kind get nodes); do
docker exec "${node}" mkdir -p "${REGISTRY_DIR}"
cat <<EOF | docker exec -i "${node}" cp /dev/stdin "${REGISTRY_DIR}/hosts.toml"
[host."http://${reg_name}:5000"]
EOF
done
# 4. Connect the registry to the cluster network if not already connected
echo "4. Connect the registry to the cluster network if not already connected"
# This allows kind to bootstrap the network but ensures they're on the same network
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
docker network connect "kind" "${reg_name}"
fi
# 5. Document the local registry
echo "5. Document the local registry"
# https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${reg_port}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kubectl -n ingress-nginx create secret tls mkcert --key /tmp/127.0.0.1.nip.io+1-key.pem --cert /tmp/127.0.0.1.nip.io+1.pem
kubectl -n ingress-nginx patch deployments.apps ingress-nginx-controller --type 'json' -p '[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value":"--default-ssl-certificate=ingress-nginx/mkcert"}]'
curl https://raw.githubusercontent.com/numerique-gouv/tools/refs/heads/main/kind/create_cluster.sh | bash -s -- impress

View File

View File

@@ -7,6 +7,6 @@ _dc_run \
app-dev \
python manage.py spectacular \
--api-version 'v1.0' \
--urlconf 'people.api_urls' \
--urlconf 'impress.api_urls' \
--format openapi-json \
--file /app/core/tests/swagger/swagger.json

3
bin/updatekeys.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
find . -name "*.enc.*" -exec sops updatekeys -y {} \;

View File

@@ -1,7 +1,7 @@
#
# Your crowdin's credentials
#
api_token_env: CROWDIN_API_TOKEN
api_token_env: CROWDIN_PERSONAL_TOKEN
project_id_env: CROWDIN_PROJECT_ID
base_path_env: CROWDIN_BASE_PATH
@@ -14,17 +14,16 @@ preserve_hierarchy: true
#
# Files configuration
#
files:
[
{
source: "/backend/locale/django.pot",
dest: "/backend.pot",
translation: "/backend/locale/%locale_with_underscore%/LC_MESSAGES/django.po",
},
{
source: "/frontend/packages/i18n/locales/desk/translations-crowdin.json",
dest: "/desk.json",
translation: "/frontend/packages/i18n/locales/desk/%two_letters_code%/translations.json",
skip_untranslated_strings: true,
},
]
files: [
{
source : "/backend/locale/django.pot",
dest: "/backend-impress.pot",
translation : "/backend/locale/%locale_with_underscore%/LC_MESSAGES/django.po"
},
{
source: "/frontend/packages/i18n/locales/impress/translations-crowdin.json",
dest: "/frontend-impress.json",
translation: "/frontend/packages/i18n/locales/impress/%two_letters_code%/translations.json",
skip_untranslated_strings: true,
},
]

View File

@@ -1,8 +1,13 @@
version: '3.8'
name: docs
services:
postgresql:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 1s
timeout: 2s
retries: 300
env_file:
- env.d/development/postgresql
ports:
@@ -16,6 +21,38 @@ services:
ports:
- "1081:1080"
minio:
user: ${DOCKER_USER:-1000}
image: minio/minio
environment:
- MINIO_ROOT_USER=impress
- MINIO_ROOT_PASSWORD=password
ports:
- '9000:9000'
- '9001:9001'
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 1s
timeout: 20s
retries: 300
entrypoint: ""
command: minio server --console-address :9001 /data
volumes:
- ./data/media:/data
createbuckets:
image: minio/mc
depends_on:
minio:
condition: service_healthy
restart: true
entrypoint: >
sh -c "
/usr/bin/mc alias set impress http://minio:9000 impress password && \
/usr/bin/mc mb impress/impress-media-storage && \
/usr/bin/mc version enable impress/impress-media-storage && \
exit 0;"
app-dev:
build:
context: .
@@ -23,7 +60,7 @@ services:
args:
DOCKER_USER: ${DOCKER_USER:-1000}
user: ${DOCKER_USER:-1000}
image: people:backend-development
image: impress:backend-development
environment:
- PYLINTHOME=/app/.pylint.d
- DJANGO_CONFIGURATION=Development
@@ -34,17 +71,22 @@ services:
- "8071:8000"
volumes:
- ./src/backend:/app
- ./data/media:/data/media
- ./data/static:/data/static
depends_on:
- postgresql
- mailcatcher
- redis
postgresql:
condition: service_healthy
restart: true
mailcatcher:
condition: service_started
redis:
condition: service_started
createbuckets:
condition: service_started
celery-dev:
user: ${DOCKER_USER:-1000}
image: people:backend-development
command: ["celery", "-A", "people.celery_app", "worker", "-l", "DEBUG"]
image: impress:backend-development
command: ["celery", "-A", "impress.celery_app", "worker", "-l", "DEBUG"]
environment:
- DJANGO_CONFIGURATION=Development
env_file:
@@ -52,7 +94,6 @@ services:
- env.d/development/postgresql
volumes:
- ./src/backend:/app
- ./data/media:/data/media
- ./data/static:/data/static
depends_on:
- app-dev
@@ -64,22 +105,25 @@ services:
args:
DOCKER_USER: ${DOCKER_USER:-1000}
user: ${DOCKER_USER:-1000}
image: people:backend-production
image: impress:backend-production
environment:
- DJANGO_CONFIGURATION=Demo
env_file:
- env.d/development/common
- env.d/development/postgresql
volumes:
- ./data/media:/data/media
depends_on:
- postgresql
- redis
postgresql:
condition: service_healthy
restart: true
redis:
condition: service_started
minio:
condition: service_started
celery:
user: ${DOCKER_USER:-1000}
image: people:backend-production
command: ["celery", "-A", "people.celery_app", "worker", "-l", "INFO"]
image: impress:backend-production
command: ["celery", "-A", "impress.celery_app", "worker", "-l", "INFO"]
environment:
- DJANGO_CONFIGURATION=Demo
env_file:
@@ -95,11 +139,28 @@ services:
volumes:
- ./docker/files/etc/nginx/conf.d:/etc/nginx/conf.d:ro
depends_on:
- app
- keycloak
app-dev:
condition: service_started
y-provider:
condition: service_started
keycloak:
condition: service_healthy
restart: true
dockerize:
image: jwilder/dockerize
frontend:
user: "${DOCKER_USER:-1000}"
build:
context: .
dockerfile: ./src/frontend/Dockerfile
target: frontend-production
args:
API_ORIGIN: "http://localhost:8071"
Y_PROVIDER_URL: "ws://localhost:4444"
MEDIA_URL: "http://localhost:8083"
SW_DEACTIVATED: "true"
image: impress:frontend-development
ports:
- "3000:3000"
crowdin:
image: crowdin/cli:3.16.0
@@ -114,32 +175,33 @@ services:
image: node:18
user: "${DOCKER_USER:-1000}"
environment:
HOME: /tmp
HOME: /tmp
volumes:
- ".:/app"
terraform-state:
image: hashicorp/terraform:1.6
environment:
- TF_WORKSPACE=${PROJECT:-} # avoid env conflict in local state
y-provider:
user: ${DOCKER_USER:-1000}
working_dir: /app
volumes:
- ./src/terraform/create_state_bucket:/app
terraform:
image: hashicorp/terraform:1.6
user: ${DOCKER_USER:-1000}
working_dir: /app
volumes:
- ./src/terraform:/app
build:
context: .
dockerfile: ./src/frontend/servers/y-provider/Dockerfile
target: y-provider
restart: unless-stopped
env_file:
- env.d/development/common
ports:
- "4444:4444"
kc_postgresql:
image: postgres:14.3
ports:
- "5433:5432"
env_file:
- env.d/development/kc_postgresql
image: postgres:14.3
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 1s
timeout: 2s
retries: 300
ports:
- "5433:5432"
env_file:
- env.d/development/kc_postgresql
keycloak:
image: quay.io/keycloak/keycloak:20.0.1
@@ -154,6 +216,13 @@ services:
- --hostname-admin-url=http://localhost:8083/
- --hostname-strict=false
- --hostname-strict-https=false
- --health-enabled=true
- --metrics-enabled=true
healthcheck:
test: ["CMD", "curl", "--head", "-fsS", "http://localhost:8080/health/ready"]
interval: 1s
timeout: 2s
retries: 300
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
@@ -161,10 +230,12 @@ services:
KC_DB_URL_HOST: kc_postgresql
KC_DB_URL_DATABASE: keycloak
KC_DB_PASSWORD: pass
KC_DB_USERNAME: people
KC_DB_USERNAME: impress
KC_DB_SCHEMA: public
PROXY_ADDRESS_FORWARDING: 'true'
ports:
- "8080:8080"
depends_on:
- kc_postgresql
kc_postgresql:
condition: service_healthy
restart: true

View File

@@ -1,6 +1,6 @@
{
"id": "ccf4fd40-4286-474d-854a-4714282a8bec",
"realm": "people",
"realm": "impress",
"notBefore": 0,
"defaultSignatureAlgorithm": "RS256",
"revokeRefreshToken": false,
@@ -45,15 +45,15 @@
"failureFactor": 30,
"users": [
{
"username": "people",
"email": "people@people.world",
"username": "impress",
"email": "impress@impress.world",
"firstName": "John",
"lastName": "Doe",
"enabled": true,
"credentials": [
{
"type": "password",
"value": "people"
"value": "impress"
}
],
"realmRoles": ["user"]
@@ -114,7 +114,7 @@
},
{
"id": "1bfe401a-08fc-4d94-80e0-86c4f5195f99",
"name": "default-roles-people",
"name": "default-roles-impress",
"description": "${role_default-roles}",
"composite": true,
"composites": {
@@ -359,7 +359,7 @@
"attributes": {}
}
],
"people": [],
"impress": [],
"account": [
{
"id": "63b1a4e1-a594-4571-99c3-7c5c3efd61ce",
@@ -449,7 +449,7 @@
"groups": [],
"defaultRole": {
"id": "1bfe401a-08fc-4d94-80e0-86c4f5195f99",
"name": "default-roles-people",
"name": "default-roles-impress",
"description": "${role_default-roles}",
"composite": true,
"clientRole": false,
@@ -504,12 +504,12 @@
"clientId": "account",
"name": "${client_account}",
"rootUrl": "${authBaseUrl}",
"baseUrl": "/realms/people/account/",
"baseUrl": "/realms/impress/account/",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"redirectUris": ["/realms/people/account/*"],
"redirectUris": ["/realms/impress/account/*"],
"webOrigins": [],
"notBefore": 0,
"bearerOnly": false,
@@ -546,12 +546,12 @@
"clientId": "account-console",
"name": "${client_account-console}",
"rootUrl": "${authBaseUrl}",
"baseUrl": "/realms/people/account/",
"baseUrl": "/realms/impress/account/",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"redirectUris": ["/realms/people/account/*"],
"redirectUris": ["/realms/impress/account/*"],
"webOrigins": [],
"notBefore": 0,
"bearerOnly": false,
@@ -676,7 +676,7 @@
},
{
"id": "869481d0-5774-4e64-bc30-fedc7c58958f",
"clientId": "people",
"clientId": "impress",
"name": "",
"description": "",
"rootUrl": "",
@@ -791,12 +791,12 @@
"clientId": "security-admin-console",
"name": "${client_security-admin-console}",
"rootUrl": "${authAdminUrl}",
"baseUrl": "/admin/people/console/",
"baseUrl": "/admin/impress/console/",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"redirectUris": ["/admin/people/console/*"],
"redirectUris": ["/admin/impress/console/*"],
"webOrigins": ["+"],
"notBefore": 0,
"bearerOnly": false,
@@ -1339,6 +1339,21 @@
"jsonType.label": "String"
}
},
{
"id": "qb109597-e31e-46d7-7844-62e5fcf32ac8",
"name": "email sub",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-property-mapper",
"consentRequired": false,
"config": {
"userinfo.token.claim": "true",
"user.attribute": "email",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "sub",
"jsonType.label": "String"
}
},
{
"id": "61c135e5-2447-494b-bc70-9612f383be27",
"name": "email verified",

View File

@@ -4,10 +4,97 @@ server {
server_name localhost;
charset utf-8;
# Proxy auth for collaboration server
location /collaboration/ws/ {
# Collaboration Auth request configuration
auth_request /collaboration-auth;
auth_request_set $authHeader $upstream_http_authorization;
auth_request_set $canEdit $upstream_http_x_can_edit;
auth_request_set $userId $upstream_http_x_user_id;
# Pass specific headers from the auth response
proxy_set_header Authorization $authHeader;
proxy_set_header X-Can-Edit $canEdit;
proxy_set_header X-User-Id $userId;
# Ensure WebSocket upgrade
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
# Collaboration server
proxy_pass http://y-provider:4444;
# Set appropriate timeout for WebSocket
proxy_read_timeout 86400;
proxy_send_timeout 86400;
# Preserve original host and additional headers
proxy_set_header Host $host;
}
location /collaboration-auth {
proxy_pass http://app-dev:8000/api/v1.0/documents/collaboration-auth/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Original-URL $request_uri;
# Prevent the body from being passed
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-Method $request_method;
}
location /collaboration/api/ {
# Collaboration server
proxy_pass http://y-provider:4444;
proxy_set_header Host $host;
}
# Proxy auth for media
location /media/ {
# Auth request configuration
auth_request /media-auth;
auth_request_set $authHeader $upstream_http_authorization;
auth_request_set $authDate $upstream_http_x_amz_date;
auth_request_set $authContentSha256 $upstream_http_x_amz_content_sha256;
# Pass specific headers from the auth response
proxy_set_header Authorization $authHeader;
proxy_set_header X-Amz-Date $authDate;
proxy_set_header X-Amz-Content-SHA256 $authContentSha256;
# Get resource from Minio
proxy_pass http://minio:9000/impress-media-storage/;
proxy_set_header Host minio:9000;
add_header Content-Security-Policy "default-src 'none'" always;
}
location /media-auth {
proxy_pass http://app-dev:8000/api/v1.0/documents/media-auth/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Original-URL $request_uri;
# Prevent the body from being passed
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-Method $request_method;
}
location / {
proxy_pass http://keycloak:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Increase proxy buffer size to allow keycloak to send large
# header responses when a user is created.
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
}

View File

@@ -13,7 +13,7 @@
#
# To pass environment variables, you can either use the -e option of the docker run command:
#
# docker run --rm -e USER_NAME=foo -e HOME='/home/foo' people:latest python manage.py migrate
# docker run --rm -e USER_NAME=foo -e HOME='/home/foo' impress:latest python manage.py migrate
#
# or define new variables in an environment file to use with docker or docker compose:
#
@@ -21,7 +21,7 @@
# USER_NAME=foo
# HOME=/home/foo
#
# docker run --rm --env-file env.d/production people:latest python manage.py migrate
# docker run --rm --env-file env.d/production impress:latest python manage.py migrate
#
echo "🐳(entrypoint) creating user running in the container..."

View File

@@ -1,6 +1,6 @@
# Gunicorn-django settings
bind = ["0.0.0.0:8000"]
name = "people"
name = "impress"
python_path = "/app"
# Run

BIN
docs/assets/docs-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
docs/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1,163 @@
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "latest"
backend:
replicas: 1
envVars:
COLLABORATION_API_URL: https://impress.127.0.0.1.nip.io/collaboration/api/
COLLABORATION_SERVER_SECRET: my-secret
DJANGO_CSRF_TRUSTED_ORIGINS: https://impress.127.0.0.1.nip.io
DJANGO_CONFIGURATION: Feature
DJANGO_ALLOWED_HOSTS: impress.127.0.0.1.nip.io
DJANGO_SERVER_TO_SERVER_API_TOKENS: secret-api-key
DJANGO_SECRET_KEY: AgoodOrAbadKey
DJANGO_SETTINGS_MODULE: impress.settings
DJANGO_SUPERUSER_PASSWORD: admin
DJANGO_EMAIL_BRAND_NAME: "La Suite Numérique"
DJANGO_EMAIL_HOST: "mailcatcher"
DJANGO_EMAIL_LOGO_IMG: https://impress.127.0.0.1.nip.io/assets/logo-suite-numerique.png
DJANGO_EMAIL_PORT: 1025
DJANGO_EMAIL_USE_SSL: False
LOGGING_LEVEL_HANDLERS_CONSOLE: ERROR
LOGGING_LEVEL_LOGGERS_ROOT: INFO
LOGGING_LEVEL_LOGGERS_APP: INFO
OIDC_OP_JWKS_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/certs
OIDC_OP_AUTHORIZATION_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/auth
OIDC_OP_TOKEN_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/token
OIDC_OP_USER_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/userinfo
OIDC_OP_LOGOUT_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/session/end
OIDC_RP_CLIENT_ID: impress
OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
OIDC_RP_SIGN_ALGO: RS256
OIDC_RP_SCOPES: "openid email"
OIDC_VERIFY_SSL: False
USER_OIDC_FIELD_TO_SHORTNAME: "given_name"
USER_OIDC_FIELDS_TO_FULLNAME: "given_name,usual_name"
OIDC_REDIRECT_ALLOWED_HOSTS: https://impress.127.0.0.1.nip.io
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
LOGIN_REDIRECT_URL: https://impress.127.0.0.1.nip.io
LOGIN_REDIRECT_URL_FAILURE: https://impress.127.0.0.1.nip.io
LOGOUT_REDIRECT_URL: https://impress.127.0.0.1.nip.io
POSTHOG_KEY: "{'id': 'posthog_key', 'host': 'https://product.impress.127.0.0.1.nip.io'}"
DB_HOST: postgresql
DB_NAME: impress
DB_USER: dinum
DB_PASSWORD: pass
DB_PORT: 5432
POSTGRES_DB: impress
POSTGRES_USER: dinum
POSTGRES_PASSWORD: pass
REDIS_URL: redis://default:pass@redis-master:6379/1
AWS_S3_ENDPOINT_URL: http://minio.impress.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: root
AWS_S3_SECRET_ACCESS_KEY: password
AWS_STORAGE_BUCKET_NAME: impress-media-storage
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
Y_PROVIDER_API_BASE_URL: http://impress-y-provider:443/api/
Y_PROVIDER_API_KEY: my-secret
migrate:
command:
- "/bin/sh"
- "-c"
- |
python manage.py migrate --no-input &&
python manage.py create_demo --force
restartPolicy: Never
command:
- "gunicorn"
- "-c"
- "/usr/local/etc/gunicorn/impress.py"
- "impress.wsgi:application"
- "--reload"
createsuperuser:
command:
- "/bin/sh"
- "-c"
- |
python manage.py createsuperuser --email admin@example.com --password admin
restartPolicy: Never
# Exra volume to manage our local custom CA and avoid to set ssl_verify: false
extraVolumeMounts:
- name: certs
mountPath: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem
subPath: cacert.pem
# Exra volume to manage our local custom CA and avoid to set ssl_verify: false
extraVolumes:
- name: certs
configMap:
name: certifi
items:
- key: cacert.pem
path: cacert.pem
frontend:
envVars:
PORT: 8080
NEXT_PUBLIC_API_ORIGIN: https://impress.127.0.0.1.nip.io
replicas: 1
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "latest"
yProvider:
replicas: 1
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "latest"
envVars:
COLLABORATION_LOGGING: true
COLLABORATION_SERVER_ORIGIN: https://impress.127.0.0.1.nip.io
COLLABORATION_SERVER_SECRET: my-secret
Y_PROVIDER_API_KEY: my-secret
posthog:
ingress:
enabled: false
ingressAssets:
enabled: false
ingress:
enabled: true
host: impress.127.0.0.1.nip.io
ingressCollaborationWS:
enabled: true
host: impress.127.0.0.1.nip.io
annotations:
nginx.ingress.kubernetes.io/auth-url: https://impress.127.0.0.1.nip.io/api/v1.0/documents/collaboration-auth/
ingressCollaborationApi:
enabled: true
host: impress.127.0.0.1.nip.io
ingressAdmin:
enabled: true
host: impress.127.0.0.1.nip.io
ingressMedia:
enabled: true
host: impress.127.0.0.1.nip.io
annotations:
nginx.ingress.kubernetes.io/auth-url: https://impress.127.0.0.1.nip.io/api/v1.0/documents/media-auth/
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
nginx.ingress.kubernetes.io/upstream-vhost: minio.impress.svc.cluster.local:9000
nginx.ingress.kubernetes.io/rewrite-target: /impress-media-storage/$1
serviceMedia:
host: minio.impress.svc.cluster.local
port: 9000

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
auth:
rootUser: root
rootPassword: password
provisioning:
enabled: true
buckets:
- name: impress-media-storage
versioning: true

View File

@@ -0,0 +1,7 @@
auth:
username: dinum
password: pass
database: impress
tls:
enabled: true
autoGenerated: true

View File

@@ -0,0 +1,4 @@
auth:
password: pass
architecture: standalone

231
docs/installation.md Normal file
View File

@@ -0,0 +1,231 @@
# Installation on a k8s cluster
This document is a step-by-step guide that describes how to install Docs on a k8s cluster without AI features. It's a teaching document to learn how it's work. It needs to be adapt for production environment.
## Prerequisites
- k8s cluster with an nginx-ingress controller
- an OIDC provider (if you don't have one, we will provide an example)
- a PostgreSQL server (if you don't have one, we will provide an example)
- a Memcached server (if you don't have one, we will provide an example)
- a S3 bucket (if you don't have one, we will provide an example)
### Test cluster
If you do not have a test cluster, you can install everything on a local kind cluster. In this case, the simplest way is to use our script **bin/start-kind.sh**.
To be able to use the script, you will need to install:
- Docker (https://docs.docker.com/desktop/)
- Kind (https://kind.sigs.k8s.io/docs/user/quick-start/#installation)
- Mkcert (https://github.com/FiloSottile/mkcert#installation)
- Helm (https://helm.sh/docs/intro/quickstart/#install-helm)
```
./bin/start-kind.sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4700 100 4700 0 0 92867 0 --:--:-- --:--:-- --:--:-- 94000
0. Create ca
The local CA is already installed in the system trust store! 👍
The local CA is already installed in the Firefox and/or Chrome/Chromium trust store! 👍
Created a new certificate valid for the following names 📜
- "127.0.0.1.nip.io"
- "*.127.0.0.1.nip.io"
Reminder: X.509 wildcards only go one level deep, so this won't match a.b.127.0.0.1.nip.io
The certificate is at "./127.0.0.1.nip.io+1.pem" and the key at "./127.0.0.1.nip.io+1-key.pem" ✅
It will expire on 24 March 2027 🗓
1. Create registry container unless it already exists
2. Create kind cluster with containerd registry config dir enabled
Creating cluster "suite" ...
✓ Ensuring node image (kindest/node:v1.27.3) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-suite"
You can now use your cluster with:
kubectl cluster-info --context kind-suite
Thanks for using kind! 😊
3. Add the registry config to the nodes
4. Connect the registry to the cluster network if not already connected
5. Document the local registry
configmap/local-registry-hosting created
Warning: resource configmaps/coredns is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
configmap/coredns configured
deployment.apps/coredns restarted
6. Install ingress-nginx
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
secret/mkcert created
deployment.apps/ingress-nginx-controller patched
7. Setup namespace
namespace/impress created
Context "kind-suite" modified.
secret/mkcert created
$ kubectl -n ingress-nginx get po
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-t55ph 0/1 Completed 0 2m56s
ingress-nginx-admission-patch-94dvt 0/1 Completed 1 2m56s
ingress-nginx-controller-57c548c4cd-2rx47 1/1 Running 0 2m56s
```
When your k8s cluster is ready (the ingress nginx controller is up), you can start the deployment. This cluster is special because it uses the *.127.0.0.1.nip.io domain and mkcert certificates to have full HTTPS support and easy domain name management.
Please remember that *.127.0.0.1.nip.io will always resolve to 127.0.0.1, except in the k8s cluster where we configure CoreDNS to answer with the ingress-nginx service IP.
## Preparation
### What will you use to authenticate your users ?
Docs uses OIDC, so if you already have an OIDC provider, obtain the necessary information to use it. In the next step, we will see how to configure Django (and thus Docs) to use it. If you do not have a provider, we will show you how to deploy a local Keycloak instance (this is not a production deployment, just a demo).
```
$ kubectl create namespace impress
$ kubectl config set-context --current --namespace=impress
$ helm install keycloak oci://registry-1.docker.io/bitnamicharts/keycloak -f examples/keycloak.values.yaml
$ #wait until
$ kubectl get po
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 0 6m48s
keycloak-postgresql-0 1/1 Running 0 6m48s
```
From here the important informations you will need are :
```
OIDC_OP_JWKS_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/certs
OIDC_OP_AUTHORIZATION_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/auth
OIDC_OP_TOKEN_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/token
OIDC_OP_USER_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/userinfo
OIDC_OP_LOGOUT_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/impress/protocol/openid-connect/session/end
OIDC_RP_CLIENT_ID: impress
OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
OIDC_RP_SIGN_ALGO: RS256
OIDC_RP_SCOPES: "openid email"
```
You can find these values in **examples/keycloak.values.yaml**
### Find redis server connexion values
Impress need a redis so we will start by deploying a redis :
```
$ helm install redis oci://registry-1.docker.io/bitnamicharts/redis -f examples/redis.values.yaml
$ kubectl get po
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 0 26m
keycloak-postgresql-0 1/1 Running 0 26m
redis-master-0 1/1 Running 0 35s
```
### Find postgresql connexion values
Impress uses a postgresql db as backend so if you have a provider, obtain the necessary information to use it. If you do not have, you can install a postgresql testing environment as follow:
```
$ helm install postgresql oci://registry-1.docker.io/bitnamicharts/postgresql -f examples/postgresql.values.yaml
$ kubectl get po
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 0 28m
keycloak-postgresql-0 1/1 Running 0 28m
postgresql-0 1/1 Running 0 14m
redis-master-0 1/1 Running 0 42s
```
From here important informations you will need are :
```
DB_HOST: postgres-postgresql
DB_NAME: impress
DB_USER: dinum
DB_PASSWORD: pass
DB_PORT: 5432
POSTGRES_DB: impress
POSTGRES_USER: dinum
POSTGRES_PASSWORD: pass
```
### Find s3 bucket connexion values
Impress uses a s3 bucket to store documents so if you have a provider obtain the necessary information to use it. If you do not have, you can install a local minio testing environment as follow:
```
$ helm install minio oci://registry-1.docker.io/bitnamicharts/minio -f examples/minio.values.yaml
$ kubectl get po
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 0 38m
keycloak-postgresql-0 1/1 Running 0 38m
minio-84f5c66895-bbhsk 1/1 Running 0 42s
minio-provisioning-2b5sq 0/1 Completed 0 42s
postgresql-0 1/1 Running 0 24m
redis-master-0 1/1 Running 0 10m
```
## Deployment
Now you are ready to deploy Impress without AI. AI requiered more dependancies (openai API). To deploy impress you need to provide all previous informations to the helm chart.
```
$ helm repo add impress https://suitenumerique.github.io/docs/
$ helm repo update
$ helm install impress impress/docs -f examples/impress.values.yaml
$ kubectl get po
NAME READY STATUS RESTARTS AGE
impress-docs-backend-96558758d-xtkbp 0/1 Running 0 79s
impress-docs-backend-createsuperuser-r7ltc 0/1 Completed 0 79s
impress-docs-backend-migrate-c949s 0/1 Completed 0 79s
impress-docs-frontend-6749f644f7-p5s42 1/1 Running 0 79s
impress-docs-y-provider-6947fd8f54-78f2l 1/1 Running 0 79s
keycloak-0 1/1 Running 0 48m
keycloak-postgresql-0 1/1 Running 0 48m
minio-84f5c66895-bbhsk 1/1 Running 0 10m
minio-provisioning-2b5sq 0/1 Completed 0 10m
postgresql-0 1/1 Running 0 34m
redis-master-0 1/1 Running 0 20m
```
## Test your deployment
In order to test your deployment you have to login to your instance. If you use exclusively our examples you can do :
```
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
impress-docs <none> impress.127.0.0.1.nip.io localhost 80, 443 114s
impress-docs-admin <none> impress.127.0.0.1.nip.io localhost 80, 443 114s
impress-docs-collaboration-api <none> impress.127.0.0.1.nip.io localhost 80, 443 114s
impress-docs-media <none> impress.127.0.0.1.nip.io localhost 80, 443 114s
impress-docs-ws <none> impress.127.0.0.1.nip.io localhost 80, 443 114s
keycloak <none> keycloak.127.0.0.1.nip.io localhost 80 49m
```
You can use impress on https://impress.127.0.0.1.nip.io. The provisionning user in keycloak is impress/impress.

View File

@@ -1,235 +0,0 @@
# What is People?
Space Odyssey is a dynamic organization. They use the People application to enhance teamwork and
streamline communication among their co-workers. Let's explore how this application helps them
interact efficiently.
Let's see how we could interact with Django's shell to recreate David's environment in the app.
## Base contacts from the organization records
David Bowman is an exemplary employee at Space Odyssey Corporation. His email is
`david.bowman@spaceodyssey.com` and he is registered in the organization's records via a base
contact as follows:
```python
david_base_contact = Contact.objects.create(
full_name="David Bowman",
short_name="David",
data={
"emails": [
{"type": "Work", "value": "david.bowman@spaceodyssey.com"},
],
"phones": [
{"type": "Work", "value": "(123) 456-7890"},
],
"addresses": [
{
"type": "Work",
"street": "123 Main St",
"city": "Cityville",
"state": "CA",
"zip": "12345",
"country": "USA",
}
],
"links": [
{"type": "Website", "value": "http://www.spaceodyssey.com"},
{"type": "Twitter", "value": "https://www.twitter.com/dbowman"},
],
"organizations": [
{
"name": "Space Odyssey Corporation",
"department": "IT",
"jobTitle": "AI Engineer",
},
],
}
)
```
When David logs-in to the People application for the first time using the corporation's OIDC
Single Sign-On service. A user is created for him on the fly by the system, together with an
identity record representing the OIDC session:
```python
david_user = User.objects.create(
language="en-us",
timezone="America/Los_Angeles",
)
david_identity = Identity.objects.create(
"user": david_user,
"sub": "2a1b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email" : "david.bowman@spaceodyssey.com",
"is_main": True,
)
```
## Profile contact
The system identifies Dave through the email associated with his OIDC session and prompts him to
confirm the details of the base contact stored in the database.
When David confirms, giving an alternative short name that he prefers, a contact override is
created on top of the organization's base contact. This new contact is marked as David's profile
on the user:
```python
david_contact = Contact.objects.create(
base=david_base_contact,
owner=david_user,
full_name="David Bowman",
short_name="Dave",
data={}
)
david_user.profile_contact = david_contact
david_user.save()
```
If Dave had not had any existing contact in the organization's records, the profile contact would
have been created independently, without any connection to a base contact:
```python
david_contact = Contact.objects.create(
base=None,
owner=david_user,
full_name="David Bowman",
short_name="Dave",
data={}
)
```
Now, Dave feels like sharing his mobile phone number with his colleagues. He can do this
by editing his contact in the application:
```python
contact.data["phones"] = [
{"type": "Mobile", "value": "(123) 456-7890"},
]
contact.save()
```
## Contact override
During a Space conference he attended, Dave met Dr Ryan Stone, a medical engineer who gave him
her professional email address. Ryan is already present in the system but her email is missing.
Dave can add it to his private version of the contact:
```python
ryan_base_contact = Contact.objects.create(
full_name="Ryan Stone",
data={}
)
ryan_contact = Contact.objects.create(
base=ryan_base_contact,
owner=david_user,
full_name="Ryan Stone",
short_name="Dr Ryan",
data={
"emails": [
{"type": "Work", "value": "ryan.stone@hubblestation.com"},
],
}
)
```
## Team Collaboration
Dave wants to form a team with Ryan and other colleagues to work together better on using the organization's digital tools for their projects.
Dave would like to create a team with Ryan and some other colleagues, to enhance collaboration
throughout their projects:
```python
projectx = Team.objects.create(name="Project X")
```
A team can for example be used to create an email alias or to define role based access rights
(RBAC) in a specific application or all applications of the organization's digital Suite.
Having created he team, Dave is automatically assigned the "owner" role. He invites Ryan,
granting an "administrator" role to her so she can invite her own colleagues. Both of them can
then proceed to invite other colleagues as simple members. If Ryan wants, she can upgrade a
colleague to "administrator" but only David can upgrade someone to the "owner" status:
```python
TeamAccess.objects.create(user=david_user, team=projectx, role="owner")
TeamAccess.objects.create(user=ryan_user, team=projectx, role="administrator")
TeamAccess.objects.create(user=julie_user, team=projectx, role="member")
```
| Role | Member | Administrator | Owner |
|-----------------------------------|--------|---------------|-------|
| Can view team | ✔ | ✔ | ✔ |
| Can set roles except for owners | | ✔ | ✔ |
| Can set roles for owners | | | ✔ |
| Can delete team | | | ✔ |
Importantly, the system ensures that there is always at least one owner left to maintain control
of the team.
# Models overview
The following graph represents the application's models and their relationships:
```mermaid
erDiagram
%% Models
Contact {
UUID id PK
Contact base
User owner
string full_name
string short_name
json data
DateTime created_at
DateTime updated_at
}
User {
UUID id PK
Contact profile_contact
string language
string timezone
boolean is_device
boolean is_staff
boolean is_active
DateTime created_at
DateTime updated_at
}
Identity {
UUID id PK
User user
string sub
Email email
boolean is_main
DateTime created_at
DateTime updated_at
}
Team {
UUID id PK
string name
DateTime created_at
DateTime updated_at
}
TeamAccess {
UUID id PK
Team team
User user
string role
DateTime created_at
DateTime updated_at
}
%% Relations
User ||--o{ Contact : "owns"
Contact ||--o{ User : "profile for"
User ||--o{ TeamAccess : ""
Team ||--o{ TeamAccess : ""
Identity ||--o{ User : "connects"
Contact }o--|| Contact : "overrides"
```

72
docs/release.md Normal file
View File

@@ -0,0 +1,72 @@
# Releasing a new version
Whenever we are cooking a new release (e.g. `4.18.1`) we should follow a standard procedure described below:
1. Create a new branch named: `release/4.18.1`.
2. Bump the release number for backend project, frontend projects, and Helm files:
- for backend, update the version number by hand in `pyproject.toml`,
- for each projects (`src/frontend`, `src/frontend/apps/*`, `src/frontend/packages/*`, `src/mail`), run `yarn version --new-version --no-git-tag-version 4.18.1` in their directory. This will update their `package.json` for you,
- for Helm, update Docker image tag in files located at `src/helm/env.d` for both `preprod` and `production` environments:
```yaml
image:
repository: lasuite/impress-backend
pullPolicy: Always
tag: "v4.18.1" # Replace with your new version number, without forgetting the "v" prefix
...
frontend:
image:
repository: lasuite/impress-frontend
pullPolicy: Always
tag: "v4.18.1"
y-provider:
image:
repository: lasuite/impress-y-provider
pullPolicy: Always
tag: "v4.18.1"
```
The new images don't exist _yet_: they will be created automatically later in the process.
3. Update the project's `Changelog` following the [keepachangelog](https://keepachangelog.com/en/0.3.0/) recommendations
4. Commit your changes with the following format: the 🔖 release emoji, the type of release (patch/minor/patch) and the release version:
```text
🔖(minor) bump release to 4.18.0
```
5. Open a pull request, wait for an approval from your peers and merge it.
6. Checkout and pull changes from the `main` branch to ensure you have the latest updates.
7. Tag and push your commit:
```bash
git tag v4.18.1 && git push origin tag v4.18.1
```
Doing this triggers the CI and tells it to build the new Docker image versions that you targeted earlier in the Helm files.
8. Ensure the new [backend](https://hub.docker.com/r/lasuite/impress-frontend/tags) and [frontend](https://hub.docker.com/r/lasuite/impress-frontend/tags) image tags are on Docker Hub.
9. The release is now done!
# Deploying
> [!TIP]
> The `staging` platform is deployed automatically with every update of the `main` branch.
Making a new release doesn't publish it automatically in production.
Deployment is done by ArgoCD. ArgoCD checks for the `production` tag and automatically deploys the production platform with the targeted commit.
To publish, we mark the commit we want with the `production` tag. ArgoCD is then notified that the tag has changed. It then deploys the Docker image tags specified in the Helm files of the targeted commit.
To publish the release you just made:
```bash
git tag --force production v4.18.1
git push --force origin production
```

View File

@@ -1,25 +0,0 @@
# Api client TypeScript
The backend application can automatically create a TypeScript client to be used in frontend
applications. It is used in the People front application itself.
This client is made with [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen)
and People's backend OpenAPI schema (available [here](http://localhost:8071/v1.0/swagger/) if you have the backend running).
## Requirements
We'll need the online OpenAPI schema generated by swagger. Therefore you will first need to
install the backend application.
## Install openApiClientJs
```sh
$ cd src/tsclient
$ yarn install
```
## Generate the client
```sh
yarn generate:api:client:local <output_path_for_generated_client>
```

View File

@@ -1,28 +1,43 @@
# Django
DJANGO_ALLOWED_HOSTS=*
DJANGO_SECRET_KEY=ThisIsAnExampleKeyForDevPurposeOnly
DJANGO_SETTINGS_MODULE=people.settings
DJANGO_SETTINGS_MODULE=impress.settings
DJANGO_SUPERUSER_PASSWORD=admin
# Logging
# Set to DEBUG level for dev only
LOGGING_LEVEL_HANDLERS_CONSOLE=INFO
LOGGING_LEVEL_LOGGERS_ROOT=INFO
LOGGING_LEVEL_LOGGERS_APP=INFO
# Python
PYTHONPATH=/app
# People settings
# impress settings
# Mail
DJANGO_EMAIL_BRAND_NAME="La Suite Numérique"
DJANGO_EMAIL_HOST="mailcatcher"
DJANGO_EMAIL_LOGO_IMG="http://localhost:3000/assets/logo-suite-numerique.png"
DJANGO_EMAIL_PORT=1025
# Backend url
PEOPLE_BASE_URL="http://localhost:8072"
IMPRESS_BASE_URL="http://localhost:8072"
# Media
STORAGES_STATICFILES_BACKEND=django.contrib.staticfiles.storage.StaticFilesStorage
AWS_S3_ENDPOINT_URL=http://minio:9000
AWS_S3_ACCESS_KEY_ID=impress
AWS_S3_SECRET_ACCESS_KEY=password
MEDIA_BASE_URL=http://localhost:8083
# OIDC
OIDC_OP_JWKS_ENDPOINT=http://nginx:8083/realms/people/protocol/openid-connect/certs
OIDC_OP_AUTHORIZATION_ENDPOINT=http://localhost:8083/realms/people/protocol/openid-connect/auth
OIDC_OP_TOKEN_ENDPOINT=http://nginx:8083/realms/people/protocol/openid-connect/token
OIDC_OP_USER_ENDPOINT=http://nginx:8083/realms/people/protocol/openid-connect/userinfo
OIDC_OP_JWKS_ENDPOINT=http://nginx:8083/realms/impress/protocol/openid-connect/certs
OIDC_OP_AUTHORIZATION_ENDPOINT=http://localhost:8083/realms/impress/protocol/openid-connect/auth
OIDC_OP_TOKEN_ENDPOINT=http://nginx:8083/realms/impress/protocol/openid-connect/token
OIDC_OP_USER_ENDPOINT=http://nginx:8083/realms/impress/protocol/openid-connect/userinfo
OIDC_RP_CLIENT_ID=people
OIDC_RP_CLIENT_ID=impress
OIDC_RP_CLIENT_SECRET=ThisIsAnExampleKeyForDevPurposeOnly
OIDC_RP_SIGN_ALGO=RS256
OIDC_RP_SCOPES="openid email"
@@ -33,3 +48,17 @@ LOGOUT_REDIRECT_URL=http://localhost:3000
OIDC_REDIRECT_ALLOWED_HOSTS=["http://localhost:8083", "http://localhost:3000"]
OIDC_AUTH_REQUEST_EXTRA_PARAMS={"acr_values": "eidas1"}
# AI
AI_BASE_URL=https://openaiendpoint.com
AI_API_KEY=password
AI_MODEL=llama
# Collaboration
COLLABORATION_API_URL=http://nginx:8083/collaboration/api/
COLLABORATION_SERVER_ORIGIN=http://localhost:3000
COLLABORATION_SERVER_SECRET=my-secret
COLLABORATION_WS_URL=ws://localhost:8083/collaboration/ws/
# Frontend
FRONTEND_THEME=dsfr

View File

@@ -1,3 +1,6 @@
# For the CI job test-e2e
SUSTAINED_THROTTLE_RATES="200/hour"
BURST_THROTTLE_RATES="200/minute"
DJANGO_SERVER_TO_SERVER_API_TOKENS=test-e2e
Y_PROVIDER_API_KEY=yprovider-api-key
Y_PROVIDER_API_BASE_URL=http://y-provider:4444/api/

View File

@@ -1,3 +1,3 @@
CROWDIN_API_TOKEN=Your-Api-Token
CROWDIN_PERSONAL_TOKEN=Your-Personal-Token
CROWDIN_PROJECT_ID=Your-Project-Id
CROWDIN_BASE_PATH=/app/src

View File

@@ -1,11 +1,11 @@
# Postgresql db container configuration
POSTGRES_DB=keycloak
POSTGRES_USER=people
POSTGRES_USER=impress
POSTGRES_PASSWORD=pass
# App database configuration
DB_HOST=kc_postgresql
DB_NAME=keycloak
DB_USER=people
DB_USER=impress
DB_PASSWORD=pass
DB_PORT=5433
DB_PORT=5433

View File

@@ -1,11 +1,11 @@
# Postgresql db container configuration
POSTGRES_DB=people
POSTGRES_DB=impress
POSTGRES_USER=dinum
POSTGRES_PASSWORD=pass
# App database configuration
DB_HOST=postgresql
DB_NAME=people
DB_NAME=impress
DB_USER=dinum
DB_PASSWORD=pass
DB_PORT=5432
DB_PORT=5432

View File

@@ -24,14 +24,14 @@ class GitmojiTitle(LineRule):
def validate(self, title, _commit):
"""
Download the list possible gitmojis from the project's GitHub repository and check that
Download the list possible gitmojis from the project's github repository and check that
title contains one of them.
"""
gitmojis = requests.get(
"https://raw.githubusercontent.com/carloscuesta/gitmoji/master/packages/gitmojis/src/gitmojis.json"
).json()["gitmojis"]
emojis = [item["emoji"] for item in gitmojis]
pattern = r"^({:s})\(.*\)\s[a-z].*$".format("|".join(emojis))
pattern = r"^({:s})\(.*\)\s[a-zA-Z].*$".format("|".join(emojis))
if not re.search(pattern, title):
violation_msg = 'Title does not match regex "<gitmoji>(<scope>) <subject>"'
return [RuleViolation(self.id, violation_msg, title)]

View File

@@ -13,7 +13,13 @@
"enabled": false,
"groupName": "ignored js dependencies",
"matchManagers": ["npm"],
"matchPackageNames": ["node", "node-fetch", "i18next-parser", "eslint"]
"matchPackageNames": [
"eslint",
"fetch-mock",
"node",
"node-fetch",
"workbox-webpack-plugin"
]
}
]
}

Submodule secrets deleted from d7cfe7bcdc

View File

@@ -172,7 +172,7 @@ ignore-on-opaque-inference=yes
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local,responses,
Team,Contact
Template,Contact
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
@@ -447,10 +447,10 @@ max-bool-expr=5
max-branches=12
# Maximum number of locals for function / method body
max-locals=15
max-locals=20
# Maximum number of parents for a class (see R0901).
max-parents=7
max-parents=10
# Maximum number of public methods for a class (see R0904).
max-public-methods=20

View File

@@ -1,3 +1,3 @@
include LICENSE
include README.md
recursive-include src/backend/people *.html *.png *.gif *.css *.ico *.jpg *.jpeg *.po *.mo *.eot *.svg *.ttf *.woff *.woff2
recursive-include src/backend/impress *.html *.png *.gif *.css *.ico *.jpg *.jpeg *.po *.mo *.eot *.svg *.ttf *.woff *.woff2

View File

@@ -1,65 +1,21 @@
"""Admin classes and registrations for People's core app."""
"""Admin classes and registrations for core app."""
from django import forms
from django.contrib import admin
from django.contrib.auth import admin as auth_admin
from django.utils.translation import gettext_lazy as _
from treebeard.admin import TreeAdmin
from treebeard.forms import movenodeform_factory
from . import models
class IdentityFormSet(forms.BaseInlineFormSet):
"""
Make the "is_main" field readonly when it is True so that declaring another identity
works in the admin.
"""
class TemplateAccessInline(admin.TabularInline):
"""Inline admin class for template accesses."""
def add_fields(self, form, index):
"""Disable the "is_main" field when it is set to True"""
super().add_fields(form, index)
is_main_value = form.instance.is_main if form.instance else False
form.fields["is_main"].disabled = is_main_value
class IdentityInline(admin.TabularInline):
"""Inline admin class for user identities."""
fields = (
"sub",
"email",
"is_main",
"created_at",
"updated_at",
)
formset = IdentityFormSet
model = models.Identity
autocomplete_fields = ["user"]
model = models.TemplateAccess
extra = 0
readonly_fields = ("email", "created_at", "sub", "updated_at")
def has_add_permission(self, request, obj):
"""
Identities are automatically created on successful OIDC logins.
Disable creating identities via the admin.
"""
return False
class TeamAccessInline(admin.TabularInline):
"""Inline admin class for team accesses."""
extra = 0
autocomplete_fields = ["user", "team"]
model = models.TeamAccess
readonly_fields = ("created_at", "updated_at")
class TeamWebhookInline(admin.TabularInline):
"""Inline admin class for team webhooks."""
extra = 0
autocomplete_fields = ["team"]
model = models.TeamWebhook
readonly_fields = ("created_at", "updated_at")
@admin.register(models.User)
@@ -72,11 +28,24 @@ class UserAdmin(auth_admin.UserAdmin):
{
"fields": (
"id",
"admin_email",
"password",
)
},
),
(_("Personal info"), {"fields": ("admin_email", "language", "timezone")}),
(
_("Personal info"),
{
"fields": (
"sub",
"email",
"full_name",
"short_name",
"language",
"timezone",
)
},
),
(
_("Permissions"),
{
@@ -97,38 +66,113 @@ class UserAdmin(auth_admin.UserAdmin):
None,
{
"classes": ("wide",),
"fields": ("admin_email", "password1", "password2"),
"fields": ("email", "password1", "password2"),
},
),
)
inlines = (IdentityInline, TeamAccessInline)
inlines = (TemplateAccessInline,)
list_display = (
"id",
"sub",
"full_name",
"admin_email",
"created_at",
"updated_at",
"email",
"is_active",
"is_device",
"is_staff",
"is_superuser",
)
list_filter = ("is_staff", "is_superuser", "is_device", "is_active")
ordering = ("is_active", "-is_superuser", "-is_staff", "-is_device", "-updated_at")
readonly_fields = ("id", "created_at", "updated_at")
search_fields = ("id", "admin_email", "identities__sub", "identities__email")
@admin.register(models.Team)
class TeamAdmin(admin.ModelAdmin):
"""Team admin interface declaration."""
inlines = (TeamAccessInline, TeamWebhookInline)
list_display = (
"name",
"slug",
"is_device",
"created_at",
"updated_at",
)
search_fields = ("name",)
list_filter = ("is_staff", "is_superuser", "is_device", "is_active")
ordering = (
"is_active",
"-is_superuser",
"-is_staff",
"-is_device",
"-updated_at",
"full_name",
)
readonly_fields = (
"id",
"sub",
"email",
"full_name",
"short_name",
"created_at",
"updated_at",
)
search_fields = ("id", "sub", "admin_email", "email", "full_name")
@admin.register(models.Template)
class TemplateAdmin(admin.ModelAdmin):
"""Template admin interface declaration."""
inlines = (TemplateAccessInline,)
class DocumentAccessInline(admin.TabularInline):
"""Inline admin class for template accesses."""
autocomplete_fields = ["user"]
model = models.DocumentAccess
extra = 0
@admin.register(models.Document)
class DocumentAdmin(TreeAdmin):
"""Document admin interface declaration."""
fieldsets = (
(
None,
{
"fields": (
"id",
"title",
)
},
),
(
_("Permissions"),
{
"fields": (
"creator",
"link_reach",
"link_role",
)
},
),
(
_("Tree structure"),
{
"fields": (
"path",
"depth",
"numchild",
)
},
),
)
form = movenodeform_factory(models.Document)
inlines = (DocumentAccessInline,)
list_display = (
"id",
"title",
"link_reach",
"link_role",
"created_at",
"updated_at",
)
readonly_fields = (
"creator",
"depth",
"id",
"numchild",
"path",
)
search_fields = ("id", "title")
@admin.register(models.Invitation)
@@ -137,7 +181,7 @@ class InvitationAdmin(admin.ModelAdmin):
fields = (
"email",
"team",
"document",
"role",
"created_at",
"issuer",
@@ -149,25 +193,11 @@ class InvitationAdmin(admin.ModelAdmin):
)
list_display = (
"email",
"team",
"document",
"created_at",
"is_expired",
)
def get_readonly_fields(self, request, obj=None):
if obj:
# all fields read only = disable update
return self.fields
return self.readonly_fields
def change_view(self, request, object_id, form_url="", extra_context=None):
"""Custom edit form. Remove 'save' buttons."""
extra_context = extra_context or {}
extra_context["show_save_and_continue"] = False
extra_context["show_save"] = False
extra_context["show_save_and_add_another"] = False
return super().change_view(request, object_id, extra_context=extra_context)
def save_model(self, request, obj, form, change):
obj.issuer = request.user
obj.save()

View File

@@ -1,4 +1,4 @@
"""People core API endpoints"""
"""Impress core API endpoints"""
from django.conf import settings
from django.core.exceptions import ValidationError
@@ -17,14 +17,13 @@ def exception_handler(exc, context):
https://gist.github.com/twidi/9d55486c36b6a51bdcb05ce3a763e79f
"""
if isinstance(exc, ValidationError):
detail = None
if hasattr(exc, "message_dict"):
detail = exc.message_dict
elif hasattr(exc, "message"):
detail = exc.message
elif hasattr(exc, "messages"):
detail = exc.messages
else:
detail = ""
exc = drf_exceptions.ValidationError(detail=detail)

View File

@@ -0,0 +1,25 @@
"""A JSONField for DRF to handle serialization/deserialization."""
import json
from rest_framework import serializers
class JSONField(serializers.Field):
"""
A custom field for handling JSON data.
"""
def to_representation(self, value):
"""
Convert the JSON string to a Python dictionary for serialization.
"""
return value
def to_internal_value(self, data):
"""
Convert the Python dictionary to a JSON string for deserialization.
"""
if data is None:
return None
return json.dumps(data)

View File

@@ -0,0 +1,77 @@
"""API filters for Impress' core application."""
from django.utils.translation import gettext_lazy as _
import django_filters
from core import models
class DocumentFilter(django_filters.FilterSet):
"""
Custom filter for filtering documents.
"""
title = django_filters.CharFilter(
field_name="title", lookup_expr="icontains", label=_("Title")
)
class Meta:
model = models.Document
fields = ["title"]
class ListDocumentFilter(DocumentFilter):
"""
Custom filter for filtering documents.
"""
is_creator_me = django_filters.BooleanFilter(
method="filter_is_creator_me", label=_("Creator is me")
)
is_favorite = django_filters.BooleanFilter(
method="filter_is_favorite", label=_("Favorite")
)
class Meta:
model = models.Document
fields = ["is_creator_me", "is_favorite", "title"]
# pylint: disable=unused-argument
def filter_is_creator_me(self, queryset, name, value):
"""
Filter documents based on the `creator` being the current user.
Example:
- /api/v1.0/documents/?is_creator_me=true
→ Filters documents created by the logged-in user
- /api/v1.0/documents/?is_creator_me=false
→ Filters documents created by other users
"""
user = self.request.user
if not user.is_authenticated:
return queryset
if value:
return queryset.filter(creator=user)
return queryset.exclude(creator=user)
# pylint: disable=unused-argument
def filter_is_favorite(self, queryset, name, value):
"""
Filter documents based on whether they are marked as favorite by the current user.
Example:
- /api/v1.0/documents/?is_favorite=true
→ Filters documents marked as favorite by the logged-in user
- /api/v1.0/documents/?is_favorite=false
→ Filters documents not marked as favorite by the logged-in user
"""
user = self.request.user
if not user.is_authenticated:
return queryset
return queryset.filter(is_favorite=bool(value))

View File

@@ -1,9 +1,18 @@
"""Permission handlers for the People core app."""
"""Permission handlers for the impress core app."""
from django.core import exceptions
from django.db.models import Q
from django.http import Http404
from rest_framework import permissions
from core.models import DocumentAccess, RoleChoices, get_trashbin_cutoff
ACTION_FOR_METHOD_TO_PERMISSION = {
"versions_detail": {"DELETE": "versions_destroy", "GET": "versions_retrieve"},
"children": {"GET": "children_list", "POST": "children_create"},
}
class IsAuthenticated(permissions.BasePermission):
"""
@@ -12,7 +21,16 @@ class IsAuthenticated(permissions.BasePermission):
"""
def has_permission(self, request, view):
return bool(request.auth) if request.auth else request.user.is_authenticated
return bool(request.auth) or request.user.is_authenticated
class IsAuthenticatedOrSafe(IsAuthenticated):
"""Allows access to authenticated users (or anonymous users but only on safe methods)."""
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return super().has_permission(request, view)
class IsSelf(IsAuthenticated):
@@ -46,10 +64,73 @@ class IsOwnedOrPublic(IsAuthenticated):
return False
class AccessPermission(IsAuthenticated):
class CanCreateInvitationPermission(permissions.BasePermission):
"""
Custom permission class to handle permission checks for managing invitations.
"""
def has_permission(self, request, view):
user = request.user
# Ensure the user is authenticated
if not (bool(request.auth) or request.user.is_authenticated):
return False
# Apply permission checks only for creation (POST requests)
if view.action != "create":
return True
# Check if resource_id is passed in the context
try:
document_id = view.kwargs["resource_id"]
except KeyError as exc:
raise exceptions.ValidationError(
"You must set a document ID in kwargs to manage document invitations."
) from exc
# Check if the user has access to manage invitations (Owner/Admin roles)
return DocumentAccess.objects.filter(
Q(user=user) | Q(team__in=user.teams),
document=document_id,
role__in=[RoleChoices.OWNER, RoleChoices.ADMIN],
).exists()
class AccessPermission(permissions.BasePermission):
"""Permission class for access objects."""
def has_permission(self, request, view):
return request.user.is_authenticated or view.action != "create"
def has_object_permission(self, request, view, obj):
"""Check permission for a given object."""
abilities = obj.get_abilities(request.user)
return abilities.get(request.method.lower(), False)
action = view.action
try:
action = ACTION_FOR_METHOD_TO_PERMISSION[view.action][request.method]
except KeyError:
pass
return abilities.get(action, False)
class DocumentAccessPermission(AccessPermission):
"""Subclass to handle soft deletion specificities."""
def has_object_permission(self, request, view, obj):
"""
Return a 404 on deleted documents
- for which the trashbin cutoff is past
- for which the current user is not owner of the document or one of its ancestors
"""
if (
deleted_at := obj.ancestors_deleted_at
) and deleted_at < get_trashbin_cutoff():
raise Http404
# Compute permission first to ensure the "user_roles" attribute is set
has_permission = super().has_object_permission(request, view, obj)
if obj.ancestors_deleted_at and not RoleChoices.OWNER in obj.user_roles:
raise Http404
return has_permission

View File

@@ -1,84 +1,37 @@
"""Client serializers for the People core app."""
"""Client serializers for the impress core app."""
import mimetypes
from django.conf import settings
from django.db.models import Q
from django.utils.functional import lazy
from django.utils.translation import gettext_lazy as _
import magic
from rest_framework import exceptions, serializers
from timezone_field.rest_framework import TimeZoneSerializerField
from core import models
from core import enums, models
from core.services.ai_services import AI_ACTIONS
from core.services.converter_services import (
ConversionError,
YdocConverter,
)
class ContactSerializer(serializers.ModelSerializer):
"""Serialize contacts."""
class Meta:
model = models.Contact
fields = [
"id",
"base",
"data",
"full_name",
"owner",
"short_name",
]
read_only_fields = ["id", "owner"]
def update(self, instance, validated_data):
"""Make "base" field readonly but only for update/patch."""
validated_data.pop("base", None)
return super().update(instance, validated_data)
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
"""
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop("fields", None)
# Instantiate the superclass normally
super().__init__(*args, **kwargs)
if fields is not None:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
class UserSerializer(DynamicFieldsModelSerializer):
class UserSerializer(serializers.ModelSerializer):
"""Serialize users."""
timezone = TimeZoneSerializerField(use_pytz=False, required=True)
email = serializers.ReadOnlyField()
name = serializers.ReadOnlyField()
class Meta:
model = models.User
fields = [
"id",
"email",
"language",
"name",
"timezone",
"is_device",
"is_staff",
]
read_only_fields = ["id", "name", "email", "is_device", "is_staff"]
fields = ["id", "email", "full_name", "short_name", "language"]
read_only_fields = ["id", "email", "full_name", "short_name"]
class TeamAccessSerializer(serializers.ModelSerializer):
"""Serialize team accesses."""
class BaseAccessSerializer(serializers.ModelSerializer):
"""Serialize template accesses."""
abilities = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.TeamAccess
fields = ["id", "user", "role", "abilities"]
read_only_fields = ["id", "abilities"]
def update(self, instance, validated_data):
"""Make "user" field is readonly but only on update."""
validated_data.pop("user", None)
@@ -107,133 +60,565 @@ class TeamAccessSerializer(serializers.ModelSerializer):
message = (
f"You are only allowed to set role to {', '.join(can_set_role_to)}"
if can_set_role_to
else "You are not allowed to set this role for this team."
else "You are not allowed to set this role for this template."
)
raise exceptions.PermissionDenied(message)
# Create
else:
try:
team_id = self.context["team_id"]
resource_id = self.context["resource_id"]
except KeyError as exc:
raise exceptions.ValidationError(
"You must set a team ID in kwargs to create a new team access."
"You must set a resource ID in kwargs to create a new access."
) from exc
if not models.TeamAccess.objects.filter(
team=team_id,
user=user,
if not self.Meta.model.objects.filter( # pylint: disable=no-member
Q(user=user) | Q(team__in=user.teams),
role__in=[models.RoleChoices.OWNER, models.RoleChoices.ADMIN],
**{self.Meta.resource_field_name: resource_id}, # pylint: disable=no-member
).exists():
raise exceptions.PermissionDenied(
"You are not allowed to manage accesses for this team."
"You are not allowed to manage accesses for this resource."
)
if (
role == models.RoleChoices.OWNER
and not models.TeamAccess.objects.filter(
team=team_id,
user=user,
and not self.Meta.model.objects.filter( # pylint: disable=no-member
Q(user=user) | Q(team__in=user.teams),
role=models.RoleChoices.OWNER,
**{self.Meta.resource_field_name: resource_id}, # pylint: disable=no-member
).exists()
):
raise exceptions.PermissionDenied(
"Only owners of a team can assign other users as owners."
"Only owners of a resource can assign other users as owners."
)
attrs["team_id"] = self.context["team_id"]
# pylint: disable=no-member
attrs[f"{self.Meta.resource_field_name}_id"] = self.context["resource_id"]
return attrs
class TeamAccessReadOnlySerializer(TeamAccessSerializer):
"""Serialize team accesses for list and retrieve actions."""
class DocumentAccessSerializer(BaseAccessSerializer):
"""Serialize document accesses."""
user = UserSerializer(read_only=True, fields=["id", "name", "email"])
user_id = serializers.PrimaryKeyRelatedField(
queryset=models.User.objects.all(),
write_only=True,
source="user",
required=False,
allow_null=True,
)
user = UserSerializer(read_only=True)
class Meta:
model = models.TeamAccess
model = models.DocumentAccess
resource_field_name = "document"
fields = ["id", "user", "user_id", "team", "role", "abilities"]
read_only_fields = ["id", "abilities"]
class TemplateAccessSerializer(BaseAccessSerializer):
"""Serialize template accesses."""
class Meta:
model = models.TemplateAccess
resource_field_name = "template"
fields = ["id", "user", "team", "role", "abilities"]
read_only_fields = ["id", "abilities"]
class ListDocumentSerializer(serializers.ModelSerializer):
"""Serialize documents with limited fields for display in lists."""
is_favorite = serializers.BooleanField(read_only=True)
nb_accesses_ancestors = serializers.IntegerField(read_only=True)
nb_accesses_direct = serializers.IntegerField(read_only=True)
user_roles = serializers.SerializerMethodField(read_only=True)
abilities = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.Document
fields = [
"id",
"user",
"role",
"abilities",
"created_at",
"creator",
"depth",
"excerpt",
"is_favorite",
"link_role",
"link_reach",
"nb_accesses_ancestors",
"nb_accesses_direct",
"numchild",
"path",
"title",
"updated_at",
"user_roles",
]
read_only_fields = [
"id",
"user",
"role",
"abilities",
"created_at",
"creator",
"depth",
"excerpt",
"is_favorite",
"link_role",
"link_reach",
"nb_accesses_ancestors",
"nb_accesses_direct",
"numchild",
"path",
"updated_at",
"user_roles",
]
def get_abilities(self, document) -> dict:
"""Return abilities of the logged-in user on the instance."""
request = self.context.get("request")
if request:
paths_links_mapping = self.context.get("paths_links_mapping", None)
# Retrieve ancestor links from paths_links_mapping (if provided)
ancestors_links = (
paths_links_mapping.get(document.path[: -document.steplen])
if paths_links_mapping
else None
)
return document.get_abilities(request.user, ancestors_links=ancestors_links)
return {}
def get_user_roles(self, document):
"""
Return roles of the logged-in user for the current document,
taking into account ancestors.
"""
request = self.context.get("request")
if request:
return document.get_roles(request.user)
return []
class DocumentSerializer(ListDocumentSerializer):
"""Serialize documents with all fields for display in detail views."""
content = serializers.CharField(required=False)
class Meta:
model = models.Document
fields = [
"id",
"abilities",
"content",
"created_at",
"creator",
"depth",
"excerpt",
"is_favorite",
"link_role",
"link_reach",
"nb_accesses_ancestors",
"nb_accesses_direct",
"numchild",
"path",
"title",
"updated_at",
"user_roles",
]
read_only_fields = [
"id",
"abilities",
"created_at",
"creator",
"depth",
"is_favorite",
"link_role",
"link_reach",
"nb_accesses_ancestors",
"nb_accesses_direct",
"numchild",
"path",
"updated_at",
"user_roles",
]
def get_fields(self):
"""Dynamically make `id` read-only on PUT requests but writable on POST requests."""
fields = super().get_fields()
request = self.context.get("request")
if request and request.method == "POST":
fields["id"].read_only = False
return fields
def validate_id(self, value):
"""Ensure the provided ID does not already exist when creating a new document."""
request = self.context.get("request")
# Only check this on POST (creation)
if request and request.method == "POST":
if models.Document.objects.filter(id=value).exists():
raise serializers.ValidationError(
"A document with this ID already exists. You cannot override it."
)
return value
class ServerCreateDocumentSerializer(serializers.Serializer):
"""
Serializer for creating a document from a server-to-server request.
Expects 'content' as a markdown string, which is converted to our internal format
via a Node.js microservice. The conversion is handled automatically, so third parties
only need to provide markdown.
Both "sub" and "email" are required because the external app calling doesn't know
if the user will pre-exist in Docs database. If the user pre-exist, we will ignore the
submitted "email" field and use the email address set on the user account in our database
"""
# Document
title = serializers.CharField(required=True)
content = serializers.CharField(required=True)
# User
sub = serializers.CharField(
required=True, validators=[models.User.sub_validator], max_length=255
)
email = serializers.EmailField(required=True)
language = serializers.ChoiceField(
required=False, choices=lazy(lambda: settings.LANGUAGES, tuple)()
)
# Invitation
message = serializers.CharField(required=False)
subject = serializers.CharField(required=False)
def create(self, validated_data):
"""Create the document and associate it with the user or send an invitation."""
language = validated_data.get("language", settings.LANGUAGE_CODE)
# Get the user on its sub (unique identifier). Default on email if allowed in settings
email = validated_data["email"]
try:
user = models.User.objects.get_user_by_sub_or_email(
validated_data["sub"], email
)
except models.DuplicateEmailError as err:
raise serializers.ValidationError({"email": [err.message]}) from err
if user:
email = user.email
language = user.language or language
try:
document_content = YdocConverter().convert_markdown(
validated_data["content"]
)
except ConversionError as err:
raise serializers.ValidationError(
{"content": ["Could not convert content"]}
) from err
document = models.Document.add_root(
title=validated_data["title"],
content=document_content,
creator=user,
)
if user:
# Associate the document with the pre-existing user
models.DocumentAccess.objects.create(
document=document,
role=models.RoleChoices.OWNER,
user=user,
)
else:
# The user doesn't exist in our database: we need to invite him/her
models.Invitation.objects.create(
document=document,
email=email,
role=models.RoleChoices.OWNER,
)
self._send_email_notification(document, validated_data, email, language)
return document
def _send_email_notification(self, document, validated_data, email, language):
"""Notify the user about the newly created document."""
subject = validated_data.get("subject") or _(
"A new document was created on your behalf!"
)
context = {
"message": validated_data.get("message")
or _("You have been granted ownership of a new document:"),
"title": subject,
}
document.send_email(subject, [email], context, language)
def update(self, instance, validated_data):
"""
This serializer does not support updates.
"""
raise NotImplementedError("Update is not supported for this serializer.")
class LinkDocumentSerializer(serializers.ModelSerializer):
"""
Serialize link configuration for documents.
We expose it separately from document in order to simplify and secure access control.
"""
class Meta:
model = models.Document
fields = [
"link_role",
"link_reach",
]
class TeamSerializer(serializers.ModelSerializer):
"""Serialize teams."""
# Suppress the warning about not implementing `create` and `update` methods
# since we don't use a model and only rely on the serializer for validation
# pylint: disable=abstract-method
class FileUploadSerializer(serializers.Serializer):
"""Receive file upload requests."""
file = serializers.FileField()
def validate_file(self, file):
"""Add file size and type constraints as defined in settings."""
# Validate file size
if file.size > settings.DOCUMENT_IMAGE_MAX_SIZE:
max_size = settings.DOCUMENT_IMAGE_MAX_SIZE // (1024 * 1024)
raise serializers.ValidationError(
f"File size exceeds the maximum limit of {max_size:d} MB."
)
extension = file.name.rpartition(".")[-1] if "." in file.name else None
# Read the first few bytes to determine the MIME type accurately
mime = magic.Magic(mime=True)
magic_mime_type = mime.from_buffer(file.read(1024))
file.seek(0) # Reset file pointer to the beginning after reading
self.context["is_unsafe"] = (
magic_mime_type in settings.DOCUMENT_UNSAFE_MIME_TYPES
)
extension_mime_type, _ = mimetypes.guess_type(file.name)
# Try guessing a coherent extension from the mimetype
if extension_mime_type != magic_mime_type:
self.context["is_unsafe"] = True
guessed_ext = mimetypes.guess_extension(magic_mime_type)
# Missing extensions or extensions longer than 5 characters (it's as long as an extension
# can be) are replaced by the extension we eventually guessed from mimetype.
if (extension is None or len(extension) > 5) and guessed_ext:
extension = guessed_ext[1:]
if extension is None:
raise serializers.ValidationError("Could not determine file extension.")
self.context["expected_extension"] = extension
self.context["content_type"] = magic_mime_type
self.context["file_name"] = file.name
return file
def validate(self, attrs):
"""Override validate to add the computed extension to validated_data."""
attrs["expected_extension"] = self.context["expected_extension"]
attrs["is_unsafe"] = self.context["is_unsafe"]
attrs["content_type"] = self.context["content_type"]
attrs["file_name"] = self.context["file_name"]
return attrs
class TemplateSerializer(serializers.ModelSerializer):
"""Serialize templates."""
abilities = serializers.SerializerMethodField(read_only=True)
slug = serializers.SerializerMethodField()
accesses = TemplateAccessSerializer(many=True, read_only=True)
class Meta:
model = models.Team
model = models.Template
fields = [
"id",
"name",
"title",
"accesses",
"abilities",
"slug",
"created_at",
"updated_at",
]
read_only_fields = [
"id",
"accesses",
"abilities",
"slug",
"created_at",
"updated_at",
"css",
"code",
"is_public",
]
read_only_fields = ["id", "accesses", "abilities"]
def get_abilities(self, team) -> dict:
def get_abilities(self, document) -> dict:
"""Return abilities of the logged-in user on the instance."""
request = self.context.get("request")
if request:
return team.get_abilities(request.user)
return document.get_abilities(request.user)
return {}
def get_slug(self, instance):
"""Return slug from the team's name."""
return instance.get_slug()
# pylint: disable=abstract-method
class DocumentGenerationSerializer(serializers.Serializer):
"""Serializer to receive a request to generate a document on a template."""
body = serializers.CharField(label=_("Body"))
body_type = serializers.ChoiceField(
choices=["html", "markdown"],
label=_("Body type"),
required=False,
default="html",
)
format = serializers.ChoiceField(
choices=["pdf", "docx"],
label=_("Format"),
required=False,
default="pdf",
)
class InvitationSerializer(serializers.ModelSerializer):
"""Serialize invitations."""
abilities = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.Invitation
fields = ["id", "created_at", "email", "team", "role", "issuer", "is_expired"]
read_only_fields = ["id", "created_at", "team", "issuer", "is_expired"]
fields = [
"id",
"abilities",
"created_at",
"email",
"document",
"role",
"issuer",
"is_expired",
]
read_only_fields = [
"id",
"abilities",
"created_at",
"document",
"issuer",
"is_expired",
]
def get_abilities(self, invitation) -> dict:
"""Return abilities of the logged-in user on the instance."""
request = self.context.get("request")
if request:
return invitation.get_abilities(request.user)
return {}
def validate(self, attrs):
"""Validate and restrict invitation to new user based on email."""
"""Validate invitation data."""
request = self.context.get("request")
user = getattr(request, "user", None)
try:
team_id = self.context["team_id"]
except KeyError as exc:
raise exceptions.ValidationError(
"You must set a team ID in kwargs to create a new team invitation."
) from exc
attrs["document_id"] = self.context["resource_id"]
if not models.TeamAccess.objects.filter(
team=team_id,
user=user,
role__in=[models.RoleChoices.OWNER, models.RoleChoices.ADMIN],
).exists():
raise exceptions.PermissionDenied(
"You are not allowed to manage invitation for this team."
)
# Only set the issuer if the instance is being created
if self.instance is None:
attrs["issuer"] = user
attrs["team_id"] = team_id
attrs["issuer"] = user
return attrs
def validate_role(self, role):
"""Custom validation for the role field."""
request = self.context.get("request")
user = getattr(request, "user", None)
document_id = self.context["resource_id"]
# If the role is OWNER, check if the user has OWNER access
if role == models.RoleChoices.OWNER:
if not models.DocumentAccess.objects.filter(
Q(user=user) | Q(team__in=user.teams),
document=document_id,
role=models.RoleChoices.OWNER,
).exists():
raise serializers.ValidationError(
"Only owners of a document can invite other users as owners."
)
return role
class VersionFilterSerializer(serializers.Serializer):
"""Validate version filters applied to the list endpoint."""
version_id = serializers.CharField(required=False, allow_blank=True)
page_size = serializers.IntegerField(
required=False, min_value=1, max_value=50, default=20
)
class AITransformSerializer(serializers.Serializer):
"""Serializer for AI transform requests."""
action = serializers.ChoiceField(choices=AI_ACTIONS, required=True)
text = serializers.CharField(required=True)
def validate_text(self, value):
"""Ensure the text field is not empty."""
if len(value.strip()) == 0:
raise serializers.ValidationError("Text field cannot be empty.")
return value
class AITranslateSerializer(serializers.Serializer):
"""Serializer for AI translate requests."""
language = serializers.ChoiceField(
choices=tuple(enums.ALL_LANGUAGES.items()), required=True
)
text = serializers.CharField(required=True)
def validate_text(self, value):
"""Ensure the text field is not empty."""
if len(value.strip()) == 0:
raise serializers.ValidationError("Text field cannot be empty.")
return value
class MoveDocumentSerializer(serializers.Serializer):
"""
Serializer for validating input data to move a document within the tree structure.
Fields:
- target_document_id (UUIDField): The ID of the target parent document where the
document should be moved. This field is required and must be a valid UUID.
- position (ChoiceField): Specifies the position of the document in relation to
the target parent's children.
Choices:
- "first-child": Place the document as the first child of the target parent.
- "last-child": Place the document as the last child of the target parent (default).
- "left": Place the document as the left sibling of the target parent.
- "right": Place the document as the right sibling of the target parent.
Example:
Input payload for moving a document:
{
"target_document_id": "123e4567-e89b-12d3-a456-426614174000",
"position": "first-child"
}
Notes:
- The `target_document_id` is mandatory.
- The `position` defaults to "last-child" if not provided.
"""
target_document_id = serializers.UUIDField(required=True)
position = serializers.ChoiceField(
choices=enums.MoveNodePositionChoices.choices,
default=enums.MoveNodePositionChoices.LAST_CHILD,
)

View File

@@ -0,0 +1,181 @@
"""Util to generate S3 authorization headers for object storage access control"""
import time
from abc import ABC, abstractmethod
from django.conf import settings
from django.core.cache import cache
from django.core.files.storage import default_storage
import botocore
from rest_framework.throttling import BaseThrottle
def nest_tree(flat_list, steplen):
"""
Convert a flat list of serialized documents into a nested tree making advantage
of the`path` field and its step length.
"""
node_dict = {}
roots = []
# Sort the flat list by path to ensure parent nodes are processed first
flat_list.sort(key=lambda x: x["path"])
for node in flat_list:
node["children"] = [] # Initialize children list
node_dict[node["path"]] = node
# Determine parent path
parent_path = node["path"][:-steplen]
if parent_path in node_dict:
node_dict[parent_path]["children"].append(node)
else:
roots.append(node) # Collect root nodes
if len(roots) > 1:
raise ValueError("More than one root element detected.")
return roots[0] if roots else None
def filter_root_paths(paths, skip_sorting=False):
"""
Filters root paths from a list of paths representing a tree structure.
A root path is defined as a path that is not a prefix of any other path.
Args:
paths (list of str): The list of paths.
Returns:
list of str: The filtered list of root paths.
"""
if not skip_sorting:
paths.sort()
root_paths = []
for path in paths:
# If the current path is not a prefix of the last added root path, add it
if not root_paths or not path.startswith(root_paths[-1]):
root_paths.append(path)
return root_paths
def generate_s3_authorization_headers(key):
"""
Generate authorization headers for an s3 object.
These headers can be used as an alternative to signed urls with many benefits:
- the urls of our files never expire and can be stored in our documents' content
- we don't leak authorized urls that could be shared (file access can only be done
with cookies)
- access control is truly realtime
- the object storage service does not need to be exposed on internet
"""
url = default_storage.unsigned_connection.meta.client.generate_presigned_url(
"get_object",
ExpiresIn=0,
Params={"Bucket": default_storage.bucket_name, "Key": key},
)
request = botocore.awsrequest.AWSRequest(method="get", url=url)
s3_client = default_storage.connection.meta.client
# pylint: disable=protected-access
credentials = s3_client._request_signer._credentials # noqa: SLF001
frozen_credentials = credentials.get_frozen_credentials()
region = s3_client.meta.region_name
auth = botocore.auth.S3SigV4Auth(frozen_credentials, "s3", region)
auth.add_auth(request)
return request
class AIBaseRateThrottle(BaseThrottle, ABC):
"""Base throttle class for AI-related rate limiting with backoff."""
def __init__(self, rates):
"""Initialize instance attributes with configurable rates."""
super().__init__()
self.rates = rates
self.cache_key = None
self.recent_requests_minute = 0
self.recent_requests_hour = 0
self.recent_requests_day = 0
@abstractmethod
def get_cache_key(self, request, view):
"""Abstract method to generate cache key for throttling."""
def allow_request(self, request, view):
"""Check if the request is allowed based on rate limits."""
self.cache_key = self.get_cache_key(request, view)
if not self.cache_key:
return True # Allow if no cache key is generated
now = time.time()
history = cache.get(self.cache_key, [])
# Keep requests within the last 24 hours
history = [req for req in history if req > now - 86400]
# Calculate recent requests
self.recent_requests_minute = len([req for req in history if req > now - 60])
self.recent_requests_hour = len([req for req in history if req > now - 3600])
self.recent_requests_day = len(history)
# Check rate limits
if self.recent_requests_minute >= self.rates["minute"]:
return False
if self.recent_requests_hour >= self.rates["hour"]:
return False
if self.recent_requests_day >= self.rates["day"]:
return False
# Log the request
history.append(now)
cache.set(self.cache_key, history, timeout=86400)
return True
def wait(self):
"""Implement a backoff strategy by increasing wait time based on limits hit."""
if self.recent_requests_day >= self.rates["day"]:
return 86400
if self.recent_requests_hour >= self.rates["hour"]:
return 3600
if self.recent_requests_minute >= self.rates["minute"]:
return 60
return None
class AIDocumentRateThrottle(AIBaseRateThrottle):
"""Throttle for limiting AI requests per document with backoff."""
def __init__(self, *args, **kwargs):
super().__init__(settings.AI_DOCUMENT_RATE_THROTTLE_RATES)
def get_cache_key(self, request, view):
"""Include document ID in the cache key."""
document_id = view.kwargs["pk"]
return f"document_{document_id}_throttle_ai"
class AIUserRateThrottle(AIBaseRateThrottle):
"""Throttle that limits requests per user or IP with backoff and rate limits."""
def __init__(self, *args, **kwargs):
super().__init__(settings.AI_USER_RATE_THROTTLE_RATES)
def get_cache_key(self, request, view=None):
"""Generate a cache key based on the user ID or IP for anonymous users."""
if request.user.is_authenticated:
return f"user_{request.user.id!s}_throttle_ai"
return f"anonymous_{self.get_ident(request)}_throttle_ai"
def get_ident(self, request):
"""Return the request IP address."""
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
return (
x_forwarded_for.split(",")[0]
if x_forwarded_for
else request.META.get("REMOTE_ADDR")
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
"""People Core application"""
"""Impress Core application"""
# from django.apps import AppConfig
# from django.utils.translation import gettext_lazy as _
# class CoreConfig(AppConfig):
# """Configuration class for the People core app."""
# """Configuration class for the impress core app."""
# name = "core"
# app_label = "core"
# verbose_name = _("People core application")
# verbose_name = _("impress core application")

View File

@@ -0,0 +1,52 @@
"""Custom authentication classes for the Impress core app"""
from django.conf import settings
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class ServerToServerAuthentication(BaseAuthentication):
"""
Custom authentication class for server-to-server requests.
Validates the presence and correctness of the Authorization header.
"""
AUTH_HEADER = "Authorization"
TOKEN_TYPE = "Bearer" # noqa S105
def authenticate(self, request):
"""
Authenticate the server-to-server request by validating the Authorization header.
This method checks if the Authorization header is present in the request, ensures it
contains a valid token with the correct format, and verifies the token against the
list of allowed server-to-server tokens. If the header is missing, improperly formatted,
or contains an invalid token, an AuthenticationFailed exception is raised.
Returns:
None: If authentication is successful
(no user is authenticated for server-to-server requests).
Raises:
AuthenticationFailed: If the Authorization header is missing, malformed,
or contains an invalid token.
"""
auth_header = request.headers.get(self.AUTH_HEADER)
if not auth_header:
raise AuthenticationFailed("Authorization header is missing.")
# Validate token format and existence
auth_parts = auth_header.split(" ")
if len(auth_parts) != 2 or auth_parts[0] != self.TOKEN_TYPE:
raise AuthenticationFailed("Invalid authorization header.")
token = auth_parts[1]
if token not in settings.SERVER_TO_SERVER_API_TOKENS:
raise AuthenticationFailed("Invalid server-to-server token.")
# Authentication is successful, but no user is authenticated
def authenticate_header(self, request):
"""Return the WWW-Authenticate header value."""
return f"{self.TOKEN_TYPE} realm='Create document server to server'"

View File

@@ -1,8 +1,9 @@
"""Authentication Backends for the People core app."""
"""Authentication Backends for the Impress core app."""
import logging
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
from django.db import models
from django.utils.translation import gettext_lazy as _
import requests
@@ -10,7 +11,9 @@ from mozilla_django_oidc.auth import (
OIDCAuthenticationBackend as MozillaOIDCAuthenticationBackend,
)
from core.models import Identity
from core.models import DuplicateEmailError, User
logger = logging.getLogger(__name__)
class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
@@ -47,76 +50,81 @@ class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
proxies=self.get_settings("OIDC_PROXY", None),
)
user_response.raise_for_status()
userinfo = self.verify_token(user_response.text)
try:
userinfo = user_response.json()
except ValueError:
try:
userinfo = self.verify_token(user_response.text)
except Exception as e:
raise SuspiciousOperation(
_("Invalid response format or token verification failed")
) from e
return userinfo
def get_or_create_user(self, access_token, id_token, payload):
"""Return a User based on userinfo. Get or create a new user if no user matches the Sub.
Parameters:
- access_token (str): The access token.
- id_token (str): The ID token.
- payload (dict): The user payload.
Returns:
- User: An existing or newly created User instance.
Raises:
- Exception: Raised when user creation is not allowed and no existing user is found.
def verify_claims(self, claims):
"""
Verify the presence of essential claims and the "sub" (which is mandatory as defined
by the OIDC specification) to decide if authentication should be allowed.
"""
essential_claims = settings.USER_OIDC_ESSENTIAL_CLAIMS
missing_claims = [claim for claim in essential_claims if claim not in claims]
if missing_claims:
logger.error("Missing essential claims: %s", missing_claims)
return False
return True
def get_or_create_user(self, access_token, id_token, payload):
"""Return a User based on userinfo. Create a new user if no match is found."""
user_info = self.get_userinfo(access_token, id_token, payload)
# Compute user name from OIDC name fields as defined in settings
names_list = [
user_info[field]
for field in settings.USER_OIDC_FIELDS_TO_NAME
if user_info.get(field)
]
user_info["name"] = " ".join(names_list) or None
if not self.verify_claims(user_info):
raise SuspiciousOperation("Claims verification failed.")
sub = user_info.get("sub")
if sub is None:
raise SuspiciousOperation(
_("User info contained no recognizable user identification")
)
sub = user_info["sub"]
email = user_info.get("email")
# Get user's full name from OIDC fields defined in settings
full_name = self.compute_full_name(user_info)
short_name = user_info.get(settings.USER_OIDC_FIELD_TO_SHORTNAME)
claims = {
"email": email,
"full_name": full_name,
"short_name": short_name,
}
try:
user = User.objects.get_user_by_sub_or_email(sub, email)
except DuplicateEmailError as err:
raise SuspiciousOperation(err.message) from err
user = (
self.UserModel.objects.filter(identities__sub=sub)
.annotate(
identity_email=models.F("identities__email"),
identity_name=models.F("identities__name"),
)
.distinct()
.first()
)
if user:
email = user_info.get("email")
name = user_info.get("name")
if (
email
and email != user.identity_email
or name
and name != user.identity_name
):
Identity.objects.filter(sub=sub).update(email=email, name=name)
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 = self.create_user(user_info)
user = User.objects.create(sub=sub, password="!", **claims) # noqa: S106
return user
def create_user(self, claims):
"""Return a newly created User instance."""
sub = claims.get("sub")
if sub is None:
raise SuspiciousOperation(
_("Claims contained no recognizable user identification")
)
user = self.UserModel.objects.create(password="!") # noqa: S106
Identity.objects.create(
user=user, sub=sub, email=claims.get("email"), name=claims.get("name")
def compute_full_name(self, user_info):
"""Compute user's full name based on OIDC fields in settings."""
name_fields = settings.USER_OIDC_FIELDS_TO_FULLNAME
full_name = " ".join(
user_info[field] for field in name_fields if user_info.get(field)
)
return full_name or None
return user
def update_user_if_needed(self, user, claims):
"""Update user claims if they have changed."""
has_changed = any(
value and value != getattr(user, key) for key, value in claims.items()
)
if has_changed:
updated_claims = {key: value for key, value in claims.items() if value}
self.UserModel.objects.filter(id=user.id).update(**updated_claims)

View File

@@ -2,24 +2,23 @@
Core application enums declaration
"""
from django.conf import global_settings, settings
from django.conf import global_settings
from django.db import models
from django.utils.translation import gettext_lazy as _
# Django sets `LANGUAGES` by default with all supported languages. We can use it for
# the choice of languages which should not be limited to the few languages active in
# the app.
# In Django's code base, `LANGUAGES` is set by default with all supported languages.
# We can use it for the choice of languages which should not be limited to the few languages
# active in the app.
# pylint: disable=no-member
ALL_LANGUAGES = getattr(
settings,
"ALL_LANGUAGES",
[(language, _(name)) for language, name in global_settings.LANGUAGES],
)
ALL_LANGUAGES = {language: _(name) for language, name in global_settings.LANGUAGES}
class WebhookStatusChoices(models.TextChoices):
"""Defines the possible statuses in which a webhook can be."""
class MoveNodePositionChoices(models.TextChoices):
"""Defines the possible positions when moving a django-treebeard node."""
FAILURE = "failure", _("Failure")
PENDING = "pending", _("Pending")
SUCCESS = "success", _("Success")
FIRST_CHILD = "first-child", _("First child")
LAST_CHILD = "last-child", _("Last child")
FIRST_SIBLING = "first-sibling", _("First sibling")
LAST_SIBLING = "last-sibling", _("Last sibling")
LEFT = "left", _("Left")
RIGHT = "right", _("Right")

View File

@@ -14,177 +14,199 @@ from core import models
fake = Faker()
class BaseContactFactory(factory.django.DjangoModelFactory):
"""A factory to create contacts for a user"""
class Meta:
model = models.Contact
full_name = factory.Faker("name")
short_name = factory.LazyAttributeSequence(
lambda o, n: o.full_name.split()[0] if o.full_name else f"user{n!s}"
)
data = factory.Dict(
{
"emails": factory.LazyAttribute(
lambda x: [
{
"type": fake.random_element(["Home", "Work", "Other"]),
"value": fake.email(),
}
for _ in range(fake.random_int(1, 3))
]
),
"phones": factory.LazyAttribute(
lambda x: [
{
"type": fake.random_element(
[
"Mobile",
"Home",
"Work",
"Main",
"Work Fax",
"Home Fax",
"Pager",
"Other",
]
),
"value": fake.phone_number(),
}
for _ in range(fake.random_int(1, 3))
]
),
"addresses": factory.LazyAttribute(
lambda x: [
{
"type": fake.random_element(["Home", "Work", "Other"]),
"street": fake.street_address(),
"city": fake.city(),
"state": fake.state(),
"zip": fake.zipcode(),
"country": fake.country(),
}
for _ in range(fake.random_int(1, 3))
]
),
"links": factory.LazyAttribute(
lambda x: [
{
"type": fake.random_element(
[
"Profile",
"Blog",
"Website",
"Twitter",
"Facebook",
"Instagram",
"LinkedIn",
"Other",
]
),
"value": fake.url(),
}
for _ in range(fake.random_int(1, 3))
]
),
"customFields": factory.LazyAttribute(
lambda x: {
f"custom_field_{i:d}": fake.word()
for i in range(fake.random_int(1, 3))
},
),
"organizations": factory.LazyAttribute(
lambda x: [
{
"name": fake.company(),
"department": fake.word(),
"jobTitle": fake.job(),
}
for _ in range(fake.random_int(1, 3))
]
),
}
)
class ContactFactory(BaseContactFactory):
"""A factory to create contacts for a user"""
class Meta:
model = models.Contact
base = factory.SubFactory("core.factories.ContactFactory", base=None, owner=None)
owner = factory.SubFactory("core.factories.UserFactory", profile_contact=None)
class UserFactory(factory.django.DjangoModelFactory):
"""A factory to create random users for testing purposes."""
"""A factory to random users for testing purposes."""
class Meta:
model = models.User
django_get_or_create = ("admin_email",)
admin_email = factory.Faker("email")
sub = factory.Sequence(lambda n: f"user{n!s}")
email = factory.Faker("email")
full_name = factory.Faker("name")
short_name = factory.Faker("first_name")
language = factory.fuzzy.FuzzyChoice([lang[0] for lang in settings.LANGUAGES])
password = make_password("password")
@factory.post_generation
def with_owned_document(self, create, extracted, **kwargs):
"""
Create a document for which the user is owner to check
that there is no interference
"""
if create and (extracted is True):
UserDocumentAccessFactory(user=self, role="owner")
class IdentityFactory(factory.django.DjangoModelFactory):
"""A factory to create identities for a user"""
@factory.post_generation
def with_owned_template(self, create, extracted, **kwargs):
"""
Create a template for which the user is owner to check
that there is no interference
"""
if create and (extracted is True):
UserTemplateAccessFactory(user=self, role="owner")
class ParentNodeFactory(factory.declarations.ParameteredAttribute):
"""Custom factory attribute for setting the parent node."""
def generate(self, step, params):
"""
Generate a parent node for the factory.
This method is invoked during the factory's build process to determine the parent
node of the current object being created. If `params` is provided, it uses the factory's
metadata to recursively create or fetch the parent node. Otherwise, it returns `None`.
"""
if not params:
return None
subfactory = step.builder.factory_meta.factory
return step.recurse(subfactory, params)
class DocumentFactory(factory.django.DjangoModelFactory):
"""A factory to create documents"""
class Meta:
model = models.Identity
django_get_or_create = ("sub",)
user = factory.SubFactory(UserFactory)
sub = factory.Sequence(lambda n: f"user{n!s}")
email = factory.Faker("email")
name = factory.Faker("name")
class TeamFactory(factory.django.DjangoModelFactory):
"""A factory to create teams"""
class Meta:
model = models.Team
django_get_or_create = ("name",)
model = models.Document
django_get_or_create = ("title",)
skip_postgeneration_save = True
name = factory.Sequence(lambda n: f"team{n}")
parent = ParentNodeFactory()
title = factory.Sequence(lambda n: f"document{n}")
excerpt = factory.Sequence(lambda n: f"excerpt{n}")
content = factory.Sequence(lambda n: f"content{n}")
creator = factory.SubFactory(UserFactory)
deleted_at = None
link_reach = factory.fuzzy.FuzzyChoice(
[a[0] for a in models.LinkReachChoices.choices]
)
link_role = factory.fuzzy.FuzzyChoice(
[r[0] for r in models.LinkRoleChoices.choices]
)
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""
Custom creation logic for the factory: creates a document as a child node if
a parent is provided; otherwise, creates it as a root node.
"""
parent = kwargs.pop("parent", None)
if parent:
# Add as a child node
kwargs["ancestors_deleted_at"] = (
kwargs.get("ancestors_deleted_at") or parent.ancestors_deleted_at
)
return parent.add_child(instance=model_class(**kwargs))
# Add as a root node
return model_class.add_root(instance=model_class(**kwargs))
@factory.lazy_attribute
def ancestors_deleted_at(self):
"""Should always be set when "deleted_at" is set."""
return self.deleted_at
@factory.post_generation
def users(self, create, extracted, **kwargs):
"""Add users to team from a given list of users with or without roles."""
if not create or not extracted:
return
for user_entry in extracted:
if isinstance(user_entry, models.User):
TeamAccessFactory(team=self, user=user_entry)
else:
TeamAccessFactory(team=self, user=user_entry[0], role=user_entry[1])
"""Add users to document from a given list of users with or without roles."""
if create and extracted:
for item in extracted:
if isinstance(item, models.User):
UserDocumentAccessFactory(document=self, user=item)
else:
UserDocumentAccessFactory(document=self, user=item[0], role=item[1])
@factory.post_generation
def teams(self, create, extracted, **kwargs):
"""Add teams to document from a given list of teams with or without roles."""
if create and extracted:
for item in extracted:
if isinstance(item, str):
TeamDocumentAccessFactory(document=self, team=item)
else:
TeamDocumentAccessFactory(document=self, team=item[0], role=item[1])
@factory.post_generation
def link_traces(self, create, extracted, **kwargs):
"""Add link traces to document from a given list of users."""
if create and extracted:
for item in extracted:
models.LinkTrace.objects.create(document=self, user=item)
@factory.post_generation
def favorited_by(self, create, extracted, **kwargs):
"""Mark document as favorited by a list of users."""
if create and extracted:
for item in extracted:
models.DocumentFavorite.objects.create(document=self, user=item)
class TeamAccessFactory(factory.django.DjangoModelFactory):
"""Create fake team user accesses for testing."""
class UserDocumentAccessFactory(factory.django.DjangoModelFactory):
"""Create fake document user accesses for testing."""
class Meta:
model = models.TeamAccess
model = models.DocumentAccess
team = factory.SubFactory(TeamFactory)
document = factory.SubFactory(DocumentFactory)
user = factory.SubFactory(UserFactory)
role = factory.fuzzy.FuzzyChoice([r[0] for r in models.RoleChoices.choices])
class TeamWebhookFactory(factory.django.DjangoModelFactory):
"""Create fake team webhooks for testing."""
class TeamDocumentAccessFactory(factory.django.DjangoModelFactory):
"""Create fake document team accesses for testing."""
class Meta:
model = models.TeamWebhook
model = models.DocumentAccess
team = factory.SubFactory(TeamFactory)
url = factory.Sequence(lambda n: f"https://example.com/Groups/{n!s}")
document = factory.SubFactory(DocumentFactory)
team = factory.Sequence(lambda n: f"team{n}")
role = factory.fuzzy.FuzzyChoice([r[0] for r in models.RoleChoices.choices])
class TemplateFactory(factory.django.DjangoModelFactory):
"""A factory to create templates"""
class Meta:
model = models.Template
django_get_or_create = ("title",)
skip_postgeneration_save = True
title = factory.Sequence(lambda n: f"template{n}")
is_public = factory.Faker("boolean")
@factory.post_generation
def users(self, create, extracted, **kwargs):
"""Add users to template from a given list of users with or without roles."""
if create and extracted:
for item in extracted:
if isinstance(item, models.User):
UserTemplateAccessFactory(template=self, user=item)
else:
UserTemplateAccessFactory(template=self, user=item[0], role=item[1])
class UserTemplateAccessFactory(factory.django.DjangoModelFactory):
"""Create fake template user accesses for testing."""
class Meta:
model = models.TemplateAccess
template = factory.SubFactory(TemplateFactory)
user = factory.SubFactory(UserFactory)
role = factory.fuzzy.FuzzyChoice([r[0] for r in models.RoleChoices.choices])
class TeamTemplateAccessFactory(factory.django.DjangoModelFactory):
"""Create fake template team accesses for testing."""
class Meta:
model = models.TemplateAccess
template = factory.SubFactory(TemplateFactory)
team = factory.Sequence(lambda n: f"team{n}")
role = factory.fuzzy.FuzzyChoice([r[0] for r in models.RoleChoices.choices])
class InvitationFactory(factory.django.DjangoModelFactory):
@@ -193,7 +215,7 @@ class InvitationFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Invitation
team = factory.SubFactory(TeamFactory)
email = factory.Faker("email")
document = factory.SubFactory(DocumentFactory)
role = factory.fuzzy.FuzzyChoice([role[0] for role in models.RoleChoices.choices])
issuer = factory.SubFactory(UserFactory)

View File

@@ -1,130 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Contact Information",
"properties": {
"emails": {
"type": "array",
"title": "Emails",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"title": "Type",
"enum": ["Work", "Home", "Other"]
},
"value": {
"type": "string",
"title": "Email Address",
"format": "email"
}
},
"required": ["type", "value"]
}
},
"phones": {
"type": "array",
"title": "Phones",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"title": "Type",
"enum": ["Mobile", "Home", "Work", "Main", "Work Fax", "Home Fax", "Pager", "Other"]
},
"value": {
"type": "string",
"title": "Phone Number"
}
},
"required": ["type", "value"]
}
},
"addresses": {
"type": "array",
"title": "Addresses",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"title": "Type",
"enum": ["Home", "Work", "Other"]
},
"street": {
"type": "string",
"title": "Street"
},
"city": {
"type": "string",
"title": "City"
},
"state": {
"type": "string",
"title": "State"
},
"zip": {
"type": "string",
"title": "ZIP Code"
},
"country": {
"type": "string",
"title": "Country"
}
}
}
},
"links": {
"type": "array",
"title": "Links",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"title": "Type",
"enum": ["Profile", "Blog", "Website", "Twitter", "Facebook", "Instagram", "LinkedIn", "Other"]
},
"value": {
"type": "string",
"title": "URL",
"format": "uri"
}
},
"required": ["type", "value"]
}
},
"customFields": {
"type": "object",
"title": "Custom Fields",
"additionalProperties": {
"type": "string"
}
},
"organizations": {
"type": "array",
"title": "Organizations",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "Organization Name"
},
"department": {
"type": "string",
"title": "Department"
},
"jobTitle": {
"type": "string",
"title": "Job Title"
}
},
"required": ["name"]
}
}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,95 @@
"""Management command updating the metadata for all the files in the MinIO bucket."""
from django.core.files.storage import default_storage
from django.core.management.base import BaseCommand
import magic
from core.models import Document
# pylint: disable=too-many-locals, broad-exception-caught
class Command(BaseCommand):
"""Update the metadata for all the files in the MinIO bucket."""
help = __doc__
def handle(self, *args, **options):
"""Execute management command."""
s3_client = default_storage.connection.meta.client
bucket_name = default_storage.bucket_name
mime_detector = magic.Magic(mime=True)
documents = Document.objects.all()
self.stdout.write(
f"[INFO] Found {documents.count()} documents. Starting ContentType fix..."
)
for doc in documents:
doc_id_str = str(doc.id)
prefix = f"{doc_id_str}/attachments/"
self.stdout.write(
f"[INFO] Processing attachments under prefix '{prefix}' ..."
)
continuation_token = None
total_updated = 0
while True:
list_kwargs = {"Bucket": bucket_name, "Prefix": prefix}
if continuation_token:
list_kwargs["ContinuationToken"] = continuation_token
response = s3_client.list_objects_v2(**list_kwargs)
# If no objects found under this prefix, break out of the loop
if "Contents" not in response:
break
for obj in response["Contents"]:
key = obj["Key"]
# Skip if it's a folder
if key.endswith("/"):
continue
try:
# Get existing metadata
head_resp = s3_client.head_object(Bucket=bucket_name, Key=key)
# Read first ~1KB for MIME detection
partial_obj = s3_client.get_object(
Bucket=bucket_name, Key=key, Range="bytes=0-1023"
)
partial_data = partial_obj["Body"].read()
# Detect MIME type
magic_mime_type = mime_detector.from_buffer(partial_data)
# Update ContentType
s3_client.copy_object(
Bucket=bucket_name,
CopySource={"Bucket": bucket_name, "Key": key},
Key=key,
ContentType=magic_mime_type,
Metadata=head_resp.get("Metadata", {}),
MetadataDirective="REPLACE",
)
total_updated += 1
except Exception as exc: # noqa
self.stderr.write(
f"[ERROR] Could not update ContentType for {key}: {exc}"
)
if response.get("IsTruncated"):
continuation_token = response.get("NextContinuationToken")
else:
break
if total_updated > 0:
self.stdout.write(
f"[INFO] -> Updated {total_updated} objects for Document {doc_id_str}."
)

View File

@@ -1,188 +1,552 @@
# Generated by Django 5.0.3 on 2024-03-25 22:58
# Generated by Django 5.0.3 on 2024-05-28 20:29
import uuid
import django.contrib.auth.models
import django.core.validators
import django.db.models.deletion
import timezone_field.fields
import uuid
from django.conf import settings
from django.db import migrations, models
import timezone_field.fields
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
("auth", "0012_alter_user_first_name_max_length"),
]
operations = [
migrations.RunSQL('CREATE EXTENSION IF NOT EXISTS pg_trgm;', 'DROP EXTENSION IF EXISTS pg_trgm;'),
migrations.RunSQL('CREATE EXTENSION IF NOT EXISTS unaccent;', 'DROP EXTENSION IF EXISTS unaccent;'),
migrations.CreateModel(
name='Team',
name="Document",
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created at')),
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated at')),
('name', models.CharField(max_length=100)),
('slug', models.SlugField(editable=False, max_length=100, unique=True)),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="primary key for the record as UUID",
primary_key=True,
serialize=False,
verbose_name="id",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="date and time at which a record was created",
verbose_name="created on",
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True,
help_text="date and time at which a record was last updated",
verbose_name="updated on",
),
),
("title", models.CharField(max_length=255, verbose_name="title")),
(
"is_public",
models.BooleanField(
default=False,
help_text="Whether this document is public for anyone to use.",
verbose_name="public",
),
),
],
options={
'verbose_name': 'Team',
'verbose_name_plural': 'Teams',
'db_table': 'people_team',
'ordering': ('name',),
"verbose_name": "Document",
"verbose_name_plural": "Documents",
"db_table": "impress_document",
"ordering": ("title",),
},
),
migrations.CreateModel(
name='User',
name="Template",
fields=[
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created at')),
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated at')),
('admin_email', models.EmailField(blank=True, max_length=254, null=True, unique=True, verbose_name='admin email address')),
('language', models.CharField(choices="(('en-us', 'English'), ('fr-fr', 'French'))", default='en-us', help_text='The language in which the user wants to see the interface.', max_length=10, verbose_name='language')),
('timezone', timezone_field.fields.TimeZoneField(choices_display='WITH_GMT_OFFSET', default='UTC', help_text='The timezone in which the user wants to see times.', use_pytz=False)),
('is_device', models.BooleanField(default=False, help_text='Whether the user is a device or a real user.', verbose_name='device')),
('is_staff', models.BooleanField(default=False, help_text='Whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="primary key for the record as UUID",
primary_key=True,
serialize=False,
verbose_name="id",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="date and time at which a record was created",
verbose_name="created on",
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True,
help_text="date and time at which a record was last updated",
verbose_name="updated on",
),
),
("title", models.CharField(max_length=255, verbose_name="title")),
(
"description",
models.TextField(blank=True, verbose_name="description"),
),
("code", models.TextField(blank=True, verbose_name="code")),
("css", models.TextField(blank=True, verbose_name="css")),
(
"is_public",
models.BooleanField(
default=False,
help_text="Whether this template is public for anyone to use.",
verbose_name="public",
),
),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'db_table': 'people_user',
"verbose_name": "Template",
"verbose_name_plural": "Templates",
"db_table": "impress_template",
"ordering": ("title",),
},
),
migrations.CreateModel(
name="User",
fields=[
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"is_superuser",
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="primary key for the record as UUID",
primary_key=True,
serialize=False,
verbose_name="id",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="date and time at which a record was created",
verbose_name="created on",
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True,
help_text="date and time at which a record was last updated",
verbose_name="updated on",
),
),
(
"sub",
models.CharField(
blank=True,
help_text="Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ characters only.",
max_length=255,
null=True,
unique=True,
validators=[
django.core.validators.RegexValidator(
message="Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_ characters.",
regex="^[\\w.@+-]+\\Z",
)
],
verbose_name="sub",
),
),
(
"email",
models.EmailField(
blank=True,
max_length=254,
null=True,
verbose_name="identity email address",
),
),
(
"admin_email",
models.EmailField(
blank=True,
max_length=254,
null=True,
unique=True,
verbose_name="admin email address",
),
),
(
"language",
models.CharField(
choices="(('en-us', 'English'), ('fr-fr', 'French'))",
default="en-us",
help_text="The language in which the user wants to see the interface.",
max_length=10,
verbose_name="language",
),
),
(
"timezone",
timezone_field.fields.TimeZoneField(
choices_display="WITH_GMT_OFFSET",
default="UTC",
help_text="The timezone in which the user wants to see times.",
use_pytz=False,
),
),
(
"is_device",
models.BooleanField(
default=False,
help_text="Whether the user is a device or a real user.",
verbose_name="device",
),
),
(
"is_staff",
models.BooleanField(
default=False,
help_text="Whether the user can log into this admin site.",
verbose_name="staff status",
),
),
(
"is_active",
models.BooleanField(
default=True,
help_text="Whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
),
),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
],
options={
"verbose_name": "user",
"verbose_name_plural": "users",
"db_table": "impress_user",
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
("objects", django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name='Contact',
name="DocumentAccess",
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created at')),
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated at')),
('full_name', models.CharField(blank=True, max_length=150, null=True, verbose_name='full name')),
('short_name', models.CharField(blank=True, max_length=30, null=True, verbose_name='short name')),
('data', models.JSONField(blank=True, help_text='A JSON object containing the contact information', verbose_name='contact information')),
('base', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='overriding_contacts', to='core.contact')),
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to=settings.AUTH_USER_MODEL)),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="primary key for the record as UUID",
primary_key=True,
serialize=False,
verbose_name="id",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="date and time at which a record was created",
verbose_name="created on",
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True,
help_text="date and time at which a record was last updated",
verbose_name="updated on",
),
),
("team", models.CharField(blank=True, max_length=100)),
(
"role",
models.CharField(
choices=[
("reader", "Reader"),
("editor", "Editor"),
("administrator", "Administrator"),
("owner", "Owner"),
],
default="reader",
max_length=20,
),
),
(
"document",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="accesses",
to="core.document",
),
),
(
"user",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
'verbose_name': 'contact',
'verbose_name_plural': 'contacts',
'db_table': 'people_contact',
'ordering': ('full_name', 'short_name'),
},
),
migrations.AddField(
model_name='user',
name='profile_contact',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user', to='core.contact'),
),
migrations.CreateModel(
name='Identity',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created at')),
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated at')),
('sub', models.CharField(help_text='Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ characters only.', max_length=255, unique=True, validators=[django.core.validators.RegexValidator(message='Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_ characters.', regex='^[\\w.@+-]+\\Z')], verbose_name='sub')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='email address')),
('name', models.CharField(blank=True, max_length=100, null=True, verbose_name='name')),
('is_main', models.BooleanField(default=False, help_text='Designates whether the email is the main one.', verbose_name='main')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='identities', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'identity',
'verbose_name_plural': 'identities',
'db_table': 'people_identity',
'ordering': ('-is_main', 'email'),
"verbose_name": "Document/user relation",
"verbose_name_plural": "Document/user relations",
"db_table": "impress_document_access",
"ordering": ("-created_at",),
},
),
migrations.CreateModel(
name='Invitation',
name="Invitation",
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created at')),
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated at')),
('email', models.EmailField(max_length=254, verbose_name='email address')),
('role', models.CharField(choices=[('member', 'Member'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='member', max_length=20)),
('issuer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to=settings.AUTH_USER_MODEL)),
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to='core.team')),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="primary key for the record as UUID",
primary_key=True,
serialize=False,
verbose_name="id",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="date and time at which a record was created",
verbose_name="created on",
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True,
help_text="date and time at which a record was last updated",
verbose_name="updated on",
),
),
(
"email",
models.EmailField(max_length=254, verbose_name="email address"),
),
(
"role",
models.CharField(
choices=[
("reader", "Reader"),
("editor", "Editor"),
("administrator", "Administrator"),
("owner", "Owner"),
],
default="reader",
max_length=20,
),
),
(
"document",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="invitations",
to="core.document",
),
),
(
"issuer",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="invitations",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
'verbose_name': 'Team invitation',
'verbose_name_plural': 'Team invitations',
'db_table': 'people_invitation',
"verbose_name": "Document invitation",
"verbose_name_plural": "Document invitations",
"db_table": "impress_invitation",
},
),
migrations.CreateModel(
name='TeamAccess',
name="TemplateAccess",
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created at')),
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated at')),
('role', models.CharField(choices=[('member', 'Member'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='member', max_length=20)),
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accesses', to='core.team')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accesses', to=settings.AUTH_USER_MODEL)),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="primary key for the record as UUID",
primary_key=True,
serialize=False,
verbose_name="id",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="date and time at which a record was created",
verbose_name="created on",
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True,
help_text="date and time at which a record was last updated",
verbose_name="updated on",
),
),
("team", models.CharField(blank=True, max_length=100)),
(
"role",
models.CharField(
choices=[
("reader", "Reader"),
("editor", "Editor"),
("administrator", "Administrator"),
("owner", "Owner"),
],
default="reader",
max_length=20,
),
),
(
"template",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="accesses",
to="core.template",
),
),
(
"user",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
'verbose_name': 'Team/user relation',
'verbose_name_plural': 'Team/user relations',
'db_table': 'people_team_access',
},
),
migrations.AddField(
model_name='team',
name='users',
field=models.ManyToManyField(related_name='teams', through='core.TeamAccess', to=settings.AUTH_USER_MODEL),
),
migrations.CreateModel(
name='TeamWebhook',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created at')),
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated at')),
('url', models.URLField(verbose_name='url')),
('secret', models.CharField(blank=True, max_length=255, null=True, verbose_name='secret')),
('status', models.CharField(choices=[('failure', 'Failure'), ('pending', 'Pending'), ('success', 'Success')], default='pending', max_length=10)),
('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='webhooks', to='core.team')),
],
options={
'verbose_name': 'Team webhook',
'verbose_name_plural': 'Team webhooks',
'db_table': 'people_team_webhook',
"verbose_name": "Template/user relation",
"verbose_name_plural": "Template/user relations",
"db_table": "impress_template_access",
"ordering": ("-created_at",),
},
),
migrations.AddConstraint(
model_name='contact',
constraint=models.CheckConstraint(check=models.Q(('base__isnull', False), ('owner__isnull', True), _negated=True), name='base_owner_constraint', violation_error_message='A contact overriding a base contact must be owned.'),
model_name="documentaccess",
constraint=models.UniqueConstraint(
condition=models.Q(("user__isnull", False)),
fields=("user", "document"),
name="unique_document_user",
violation_error_message="This user is already in this document.",
),
),
migrations.AddConstraint(
model_name='contact',
constraint=models.CheckConstraint(check=models.Q(('base', models.F('id')), _negated=True), name='base_not_self', violation_error_message='A contact cannot be based on itself.'),
),
migrations.AlterUniqueTogether(
name='contact',
unique_together={('owner', 'base')},
model_name="documentaccess",
constraint=models.UniqueConstraint(
condition=models.Q(("team__gt", "")),
fields=("team", "document"),
name="unique_document_team",
violation_error_message="This team is already in this document.",
),
),
migrations.AddConstraint(
model_name='identity',
constraint=models.UniqueConstraint(fields=('user', 'email'), name='unique_user_email', violation_error_message='This email address is already declared for this user.'),
model_name="documentaccess",
constraint=models.CheckConstraint(
check=models.Q(
models.Q(("team", ""), ("user__isnull", False)),
models.Q(("team__gt", ""), ("user__isnull", True)),
_connector="OR",
),
name="check_document_access_either_user_or_team",
violation_error_message="Either user or team must be set, not both.",
),
),
migrations.AddConstraint(
model_name='invitation',
constraint=models.UniqueConstraint(fields=('email', 'team'), name='email_and_team_unique_together'),
model_name="invitation",
constraint=models.UniqueConstraint(
fields=("email", "document"), name="email_and_document_unique_together"
),
),
migrations.AddConstraint(
model_name='teamaccess',
constraint=models.UniqueConstraint(fields=('user', 'team'), name='unique_team_user', violation_error_message='This user is already in this team.'),
model_name="templateaccess",
constraint=models.UniqueConstraint(
condition=models.Q(("user__isnull", False)),
fields=("user", "template"),
name="unique_template_user",
violation_error_message="This user is already in this template.",
),
),
migrations.AddConstraint(
model_name="templateaccess",
constraint=models.UniqueConstraint(
condition=models.Q(("team__gt", "")),
fields=("team", "template"),
name="unique_template_team",
violation_error_message="This team is already in this template.",
),
),
migrations.AddConstraint(
model_name="templateaccess",
constraint=models.CheckConstraint(
check=models.Q(
models.Q(("team", ""), ("user__isnull", False)),
models.Q(("team__gt", ""), ("user__isnull", True)),
_connector="OR",
),
name="check_template_access_either_user_or_team",
violation_error_message="Either user or team must be set, not both.",
),
),
]

View File

@@ -0,0 +1,14 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0001_initial"),
]
operations = [
migrations.RunSQL(
"CREATE EXTENSION IF NOT EXISTS pg_trgm;",
reverse_sql="DROP EXTENSION IF EXISTS pg_trgm;",
),
]

View File

@@ -0,0 +1,114 @@
# Generated by Django 5.1 on 2024-09-08 16:55
import uuid
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0002_create_pg_trgm_extension"),
]
operations = [
migrations.AddField(
model_name="document",
name="link_reach",
field=models.CharField(
choices=[
("restricted", "Restricted"),
("authenticated", "Authenticated"),
("public", "Public"),
],
default="authenticated",
max_length=20,
),
),
migrations.AddField(
model_name="document",
name="link_role",
field=models.CharField(
choices=[("reader", "Reader"), ("editor", "Editor")],
default="reader",
max_length=20,
),
),
migrations.AlterField(
model_name="document",
name="is_public",
field=models.BooleanField(null=True),
),
migrations.AlterField(
model_name="user",
name="language",
field=models.CharField(
choices="(('en-us', 'English'), ('fr-fr', 'French'))",
default="en-us",
help_text="The language in which the user wants to see the interface.",
max_length=10,
verbose_name="language",
),
),
migrations.CreateModel(
name="LinkTrace",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="primary key for the record as UUID",
primary_key=True,
serialize=False,
verbose_name="id",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="date and time at which a record was created",
verbose_name="created on",
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True,
help_text="date and time at which a record was last updated",
verbose_name="updated on",
),
),
(
"document",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="link_traces",
to="core.document",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="link_traces",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "Document/user link trace",
"verbose_name_plural": "Document/user link traces",
"db_table": "impress_link_trace",
"constraints": [
models.UniqueConstraint(
fields=("user", "document"),
name="unique_link_trace_document_user",
violation_error_message="A link trace already exists for this document/user.",
)
],
},
),
]

View File

@@ -0,0 +1,36 @@
# Generated by Django 5.1 on 2024-09-08 17:04
from django.db import migrations
def migrate_is_public_to_link_reach(apps, schema_editor):
"""
Forward migration: Migrate 'is_public' to 'link_reach'.
If is_public == True, set link_reach to 'public'
"""
Document = apps.get_model("core", "Document")
Document.objects.filter(is_public=True).update(link_reach="public")
def reverse_migrate_link_reach_to_is_public(apps, schema_editor):
"""
Reverse migration: Migrate 'link_reach' back to 'is_public'.
- If link_reach == 'public', set is_public to True
- Else set is_public to False
"""
Document = apps.get_model("core", "Document")
Document.objects.filter(link_reach="public").update(is_public=True)
Document.objects.filter(link_reach__in=["restricted", "authenticated"]).update(
is_public=False
)
class Migration(migrations.Migration):
dependencies = [
("core", "0003_document_link_reach_document_link_role_and_more"),
]
operations = [
migrations.RunPython(
migrate_is_public_to_link_reach, reverse_migrate_link_reach_to_is_public
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1 on 2024-09-09 17:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0004_migrate_is_public_to_link_reach"),
]
operations = [
migrations.AlterField(
model_name="document",
name="title",
field=models.CharField(
blank=True, max_length=255, null=True, verbose_name="title"
),
),
]

View File

@@ -0,0 +1,37 @@
# Generated by Django 5.1.1 on 2024-09-29 03:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0005_remove_document_is_public_alter_document_link_reach_and_more"),
]
operations = [
migrations.AddField(
model_name="user",
name="full_name",
field=models.CharField(
blank=True, max_length=100, null=True, verbose_name="full name"
),
),
migrations.AddField(
model_name="user",
name="short_name",
field=models.CharField(
blank=True, max_length=20, null=True, verbose_name="short name"
),
),
migrations.AlterField(
model_name="user",
name="language",
field=models.CharField(
choices="(('en-us', 'English'), ('fr-fr', 'French'))",
default="en-us",
help_text="The language in which the user wants to see the interface.",
max_length=10,
verbose_name="language",
),
),
]

View File

@@ -0,0 +1,128 @@
# Generated by Django 5.1.1 on 2024-10-10 11:45
from django.db import migrations
procedure = """
DO $$
DECLARE
user_email TEXT;
BEGIN
-- Step 1: Create a temporary table (without the unique constraint)
-- impress_document_access
DROP TABLE IF EXISTS impress_document_access_tmp;
CREATE TEMP TABLE impress_document_access_tmp AS
SELECT * FROM impress_document_access;
-- impress_link_trace
DROP TABLE IF EXISTS impress_link_trace_tmp;
CREATE TEMP TABLE impress_link_trace_tmp AS
SELECT * FROM impress_link_trace;
-- Step 2: Loop through each email that appears more than once
FOR user_email IN
SELECT email
FROM impress_user
GROUP BY email
HAVING COUNT(email) > 1
LOOP
-- Step 3: Update user_id in the temporary table based on email
-- For impress_document_access
UPDATE impress_document_access_tmp
SET user_id = (
SELECT id
FROM impress_user
WHERE email = user_email
LIMIT 1
)
WHERE user_id IN (
SELECT id
FROM impress_user
WHERE email = user_email
);
-- For impress_link_trace
UPDATE impress_link_trace_tmp
SET user_id = (
SELECT id
FROM impress_user
WHERE email = user_email
LIMIT 1
)
WHERE user_id IN (
SELECT id
FROM impress_user
WHERE email = user_email
);
-- update impress_invitation
UPDATE impress_invitation
SET issuer_id = (
SELECT id
FROM impress_user
WHERE email = user_email
LIMIT 1
)
WHERE issuer_id IN (
SELECT id
FROM impress_user
WHERE email = user_email
);
DELETE FROM impress_user
WHERE id IN (
SELECT id
FROM impress_user
WHERE email = user_email
)
AND id != (
SELECT id
FROM impress_user
WHERE email = user_email
LIMIT 1
);
RAISE NOTICE 'Processed updates for email: %', user_email;
END LOOP;
-- Step 4: Remove duplicate rows from the temporary table, keeping only one row per (document_id, user_id)
-- For impress_document_access
DELETE FROM impress_document_access_tmp a
USING impress_document_access_tmp b
WHERE a.ctid < b.ctid -- Keep one row
AND a.document_id = b.document_id
AND a.user_id = b.user_id;
-- Step 5: Replace the original table with the cleaned-up temporary table
TRUNCATE TABLE impress_document_access;
-- Insert cleaned-up data back into the original table
INSERT INTO impress_document_access
SELECT * FROM impress_document_access_tmp;
-- For impress_link_trace
DELETE FROM impress_link_trace_tmp a
USING impress_link_trace_tmp b
WHERE a.ctid < b.ctid -- Keep one row
AND a.document_id = b.document_id
AND a.user_id = b.user_id;
-- Step 5: Replace the original table with the cleaned-up temporary table
TRUNCATE TABLE impress_link_trace;
-- Insert cleaned-up data back into the original table
INSERT INTO impress_link_trace
SELECT * FROM impress_link_trace_tmp;
RAISE NOTICE 'Update and deduplication process completed.';
END $$;
"""
class Migration(migrations.Migration):
dependencies = [
("core", "0006_add_user_full_name_and_short_name"),
]
operations = [
migrations.RunSQL(procedure),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 5.1.2 on 2024-10-25 11:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0007_fix_users_duplicate"),
]
operations = [
migrations.AlterField(
model_name="document",
name="link_reach",
field=models.CharField(
choices=[
("restricted", "Restricted"),
("authenticated", "Authenticated"),
("public", "Public"),
],
default="restricted",
max_length=20,
),
),
]

View File

@@ -0,0 +1,87 @@
# Generated by Django 5.1.2 on 2024-11-08 07:59
import uuid
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0008_alter_document_link_reach"),
]
operations = [
migrations.AlterField(
model_name="user",
name="language",
field=models.CharField(
choices="(('en-us', 'English'), ('fr-fr', 'French'), ('de-de', 'German'))",
default="en-us",
help_text="The language in which the user wants to see the interface.",
max_length=10,
verbose_name="language",
),
),
migrations.CreateModel(
name="DocumentFavorite",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
help_text="primary key for the record as UUID",
primary_key=True,
serialize=False,
verbose_name="id",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="date and time at which a record was created",
verbose_name="created on",
),
),
(
"updated_at",
models.DateTimeField(
auto_now=True,
help_text="date and time at which a record was last updated",
verbose_name="updated on",
),
),
(
"document",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="favorited_by_users",
to="core.document",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="favorite_documents",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "Document favorite",
"verbose_name_plural": "Document favorites",
"db_table": "impress_document_favorite",
"constraints": [
models.UniqueConstraint(
fields=("user", "document"),
name="unique_document_favorite_user",
violation_error_message="This document is already targeted by a favorite relation instance for the same user.",
)
],
},
),
]

View File

@@ -0,0 +1,54 @@
# Generated by Django 5.1.2 on 2024-11-09 11:36
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0009_add_document_favorite"),
]
operations = [
migrations.AddField(
model_name="document",
name="creator",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.RESTRICT,
related_name="documents_created",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="user",
name="language",
field=models.CharField(
choices="(('en-us', 'English'), ('fr-fr', 'French'), ('de-de', 'German'))",
default="en-us",
help_text="The language in which the user wants to see the interface.",
max_length=10,
verbose_name="language",
),
),
migrations.AlterField(
model_name="user",
name="sub",
field=models.CharField(
blank=True,
help_text="Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only.",
max_length=255,
null=True,
unique=True,
validators=[
django.core.validators.RegexValidator(
message="Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_/: characters.",
regex="^[\\w.@+-:]+\\Z",
)
],
verbose_name="sub",
),
),
]

View File

@@ -0,0 +1,61 @@
# Generated by Django 5.1.2 on 2024-11-09 11:48
import django.db.models.deletion
from django.conf import settings
from django.db import migrations
from django.db.models import F, ForeignKey, OuterRef, Q, Subquery
def set_creator_from_document_access(apps, schema_editor):
"""
Populate the `creator` field for existing Document records.
This function assigns the `creator` field using the existing
DocumentAccess entries. We can be sure that all documents have at
least one user with "owner" role. If the document has several roles,
it should take the entry with the oldest date of creation.
The update is performed using efficient bulk queries with Django's
Subquery and OuterRef to minimize database hits and ensure performance.
Note: After running this migration, we quickly modify the schema to make
the `creator` field required.
"""
Document = apps.get_model("core", "Document")
DocumentAccess = apps.get_model("core", "DocumentAccess")
# Update `creator` using the "owner" role
owner_subquery = (
DocumentAccess.objects.filter(
document=OuterRef("pk"),
user__isnull=False,
role="owner",
)
.order_by("created_at")
.values("user_id")[:1]
)
Document.objects.filter(creator__isnull=True).update(
creator=Subquery(owner_subquery)
)
class Migration(migrations.Migration):
dependencies = [
("core", "0010_add_field_creator_to_document"),
]
operations = [
migrations.RunPython(
set_creator_from_document_access, reverse_code=migrations.RunPython.noop
),
migrations.AlterField(
model_name="document",
name="creator",
field=ForeignKey(
on_delete=django.db.models.deletion.RESTRICT,
related_name="documents_created",
to=settings.AUTH_USER_MODEL,
),
),
]

View File

@@ -0,0 +1,47 @@
# Generated by Django 5.1.2 on 2024-11-30 22:23
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0011_populate_creator_field_and_make_it_required"),
]
operations = [
migrations.AlterField(
model_name="document",
name="creator",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.RESTRICT,
related_name="documents_created",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="invitation",
name="issuer",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="invitations",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="user",
name="language",
field=models.CharField(
choices="(('en-us', 'English'), ('fr-fr', 'French'), ('de-de', 'German'))",
default="en-us",
help_text="The language in which the user wants to see the interface.",
max_length=10,
verbose_name="language",
),
),
]

View File

@@ -0,0 +1,16 @@
# Generated by Django 5.1.4 on 2025-01-25 08:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0012_make_document_creator_and_invitation_issuer_optional"),
]
operations = [
migrations.RunSQL(
"CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;",
reverse_sql="DROP EXTENSION IF EXISTS fuzzystrmatch;",
),
]

View File

@@ -0,0 +1,32 @@
# Generated by Django 5.1.2 on 2024-12-07 09:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0013_activate_fuzzystrmatch_extension"),
]
operations = [
migrations.AddField(
model_name="document",
name="depth",
field=models.PositiveIntegerField(default=0),
preserve_default=False,
),
migrations.AddField(
model_name="document",
name="numchild",
field=models.PositiveIntegerField(default=0),
),
migrations.AddField(
model_name="document",
name="path",
# Allow null values pending the next datamigration to populate the field
field=models.CharField(
db_collation="C", max_length=252, null=True, unique=True
),
preserve_default=False,
),
]

View File

@@ -0,0 +1,51 @@
# Generated by Django 5.1.2 on 2024-12-07 10:33
from django.db import migrations, models
from treebeard.numconv import NumConv
ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
STEPLEN = 7
def set_path_on_existing_documents(apps, schema_editor):
"""
Updates the `path` and `depth` fields for all existing Document records
to ensure valid materialized paths.
This function assigns a unique `path` to each Document as a root node
Note: After running this migration, we quickly modify the schema to make
the `path` field required as it should.
"""
Document = apps.get_model("core", "Document")
# Iterate over all existing documents and make them root nodes
documents = Document.objects.order_by("created_at").values_list("id", flat=True)
numconv = NumConv(len(ALPHABET), ALPHABET)
updates = []
for i, pk in enumerate(documents):
key = numconv.int2str(i)
path = "{0}{1}".format(ALPHABET[0] * (STEPLEN - len(key)), key)
updates.append(Document(pk=pk, path=path, depth=1))
# Bulk update using the prepared updates list
Document.objects.bulk_update(updates, ["depth", "path"])
class Migration(migrations.Migration):
dependencies = [
("core", "0014_add_tree_structure_to_documents"),
]
operations = [
migrations.RunPython(
set_path_on_existing_documents, reverse_code=migrations.RunPython.noop
),
migrations.AlterField(
model_name="document",
name="path",
field=models.CharField(db_collation="C", max_length=252, unique=True),
),
]

View File

@@ -0,0 +1,30 @@
# Generated by Django 5.1.4 on 2024-12-18 08:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0015_set_path_on_existing_documents"),
]
operations = [
migrations.AddField(
model_name="document",
name="excerpt",
field=models.TextField(
blank=True, max_length=300, null=True, verbose_name="excerpt"
),
),
migrations.AlterField(
model_name="user",
name="language",
field=models.CharField(
choices="(('en-us', 'English'), ('fr-fr', 'French'), ('de-de', 'German'))",
default="en-us",
help_text="The language in which the user wants to see the interface.",
max_length=10,
verbose_name="language",
),
),
]

View File

@@ -0,0 +1,52 @@
# Generated by Django 5.1.4 on 2025-01-12 14:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0016_add_document_excerpt"),
]
operations = [
migrations.AlterModelOptions(
name="document",
options={
"ordering": ("path",),
"verbose_name": "Document",
"verbose_name_plural": "Documents",
},
),
migrations.AddField(
model_name="document",
name="ancestors_deleted_at",
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name="document",
name="deleted_at",
field=models.DateTimeField(blank=True, null=True),
),
migrations.AlterField(
model_name="user",
name="language",
field=models.CharField(
choices="(('en-us', 'English'), ('fr-fr', 'French'), ('de-de', 'German'))",
default="en-us",
help_text="The language in which the user wants to see the interface.",
max_length=10,
verbose_name="language",
),
),
migrations.AddConstraint(
model_name="document",
constraint=models.CheckConstraint(
condition=models.Q(
("deleted_at__isnull", True),
("deleted_at", models.F("ancestors_deleted_at")),
_connector="OR",
),
name="check_deleted_at_matches_ancestors_deleted_at_when_set",
),
),
]

View File

@@ -0,0 +1,24 @@
from django.db import migrations
def update_titles_to_null(apps, schema_editor):
"""
If the titles are "Untitled document" or "Unbenanntes Dokument" or "Document sans titre"
we set them to Null
"""
Document = apps.get_model("core", "Document")
Document.objects.filter(
title__in=["Untitled document", "Unbenanntes Dokument", "Document sans titre"]
).update(title=None)
class Migration(migrations.Migration):
dependencies = [
("core", "0017_add_fields_for_soft_delete"),
]
operations = [
migrations.RunPython(
update_titles_to_null, reverse_code=migrations.RunPython.noop
),
]

View File

@@ -0,0 +1,36 @@
# Generated by Django 5.1.5 on 2025-03-04 12:23
from django.db import migrations, models
import core.models
class Migration(migrations.Migration):
dependencies = [
("core", "0018_update_blank_title"),
]
operations = [
migrations.AlterModelManagers(
name="user",
managers=[
("objects", core.models.UserManager()),
],
),
migrations.AlterField(
model_name="user",
name="language",
field=models.CharField(
blank=True,
choices=[
("en-us", "English"),
("fr-fr", "Français"),
("de-de", "Deutsch"),
],
default=None,
help_text="The language in which the user wants to see the interface.",
max_length=10,
null=True,
verbose_name="language",
),
),
]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
"""AI services."""
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from openai import OpenAI
from core import enums
AI_ACTIONS = {
"prompt": (
"Answer the prompt in markdown format. "
"Preserve the language and markdown formatting. "
"Do not provide any other information. "
"Preserve the language."
),
"correct": (
"Correct grammar and spelling of the markdown text, "
"preserving language and markdown formatting. "
"Do not provide any other information. "
"Preserve the language."
),
"rephrase": (
"Rephrase the given markdown text, "
"preserving language and markdown formatting. "
"Do not provide any other information. "
"Preserve the language."
),
"summarize": (
"Summarize the markdown text, preserving language and markdown formatting. "
"Do not provide any other information. "
"Preserve the language."
),
"beautify": (
"Add formatting to the text to make it more readable. "
"Do not provide any other information. "
"Preserve the language."
),
"emojify": (
"Add emojis to the important parts of the text. "
"Do not provide any other information. "
"Preserve the language."
),
}
AI_TRANSLATE = (
"Keep the same html stucture and formatting. "
"Translate the content in the html to the specified language {language:s}. "
"Check the translation for accuracy and make any necessary corrections. "
"Do not provide any other information."
)
class AIService:
"""Service class for AI-related operations."""
def __init__(self):
"""Ensure that the AI configuration is set properly."""
if (
settings.AI_BASE_URL is None
or settings.AI_API_KEY is None
or settings.AI_MODEL is None
):
raise ImproperlyConfigured("AI configuration not set")
self.client = OpenAI(base_url=settings.AI_BASE_URL, api_key=settings.AI_API_KEY)
def call_ai_api(self, system_content, text):
"""Helper method to call the OpenAI API and process the response."""
response = self.client.chat.completions.create(
model=settings.AI_MODEL,
messages=[
{"role": "system", "content": system_content},
{"role": "user", "content": text},
],
)
content = response.choices[0].message.content
if not content:
raise RuntimeError("AI response does not contain an answer")
return {"answer": content}
def transform(self, text, action):
"""Transform text based on specified action."""
system_content = AI_ACTIONS[action]
return self.call_ai_api(system_content, text)
def translate(self, text, language):
"""Translate text to a specified language."""
language_display = enums.ALL_LANGUAGES.get(language, language)
system_content = AI_TRANSLATE.format(language=language_display)
return self.call_ai_api(system_content, text)

View File

@@ -0,0 +1,43 @@
"""Collaboration services."""
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
import requests
class CollaborationService:
"""Service class for Collaboration related operations."""
def __init__(self):
"""Ensure that the collaboration configuration is set properly."""
if settings.COLLABORATION_API_URL is None:
raise ImproperlyConfigured("Collaboration configuration not set")
def reset_connections(self, room, user_id=None):
"""
Reset connections of a room in the collaboration server.
Reseting a connection means that the user will be disconnected and will
have to reconnect to the collaboration server, with updated rights.
"""
endpoint = "reset-connections"
# room is necessary as a parameter, it is easier to stick to the
# same pod thanks to a parameter
endpoint_url = f"{settings.COLLABORATION_API_URL}{endpoint}/?room={room}"
# Note: Collaboration microservice accepts only raw token, which is not recommended
headers = {"Authorization": settings.COLLABORATION_SERVER_SECRET}
if user_id:
headers["X-User-Id"] = user_id
try:
response = requests.post(endpoint_url, headers=headers, timeout=10)
except requests.RequestException as e:
raise requests.HTTPError("Failed to notify WebSocket server.") from e
if response.status_code != 200:
raise requests.HTTPError(
f"Failed to notify WebSocket server. Status code: {response.status_code}, "
f"Response: {response.text}"
)

Some files were not shown because too many files have changed in this diff Show More