mirror of
https://github.com/suitenumerique/docs.git
synced 2026-05-07 07:32:33 +02:00
Compare commits
1 Commits
track-post
...
buildpack
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddac6197e3 |
2
Procfile
Normal file
2
Procfile
Normal file
@@ -0,0 +1,2 @@
|
||||
web: bin/buildpack_start.sh
|
||||
postdeploy: python manage.py migrate
|
||||
15
bin/buildpack_postcompile.sh
Executable file
15
bin/buildpack_postcompile.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit # always exit on error
|
||||
set -o pipefail # don't ignore exit codes when piping output
|
||||
|
||||
echo "-----> Running post-compile script"
|
||||
|
||||
rm -rf docker docs env.d gitlint src/frontend/apps/e2e
|
||||
rm -rf src/frontend/apps
|
||||
rm -rf src/frontend/packages
|
||||
|
||||
# Remove some of the larger packages required by the frontend only
|
||||
rm -rf src/frontend/node_modules/@next src/frontend/node_modules/next src/frontend/node_modules/react-icons src/frontend/node_modules/@gouvfr-lasuite
|
||||
|
||||
# du -ch | sort -rh | head -n 100
|
||||
15
bin/buildpack_postfrontend.sh
Executable file
15
bin/buildpack_postfrontend.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit # always exit on error
|
||||
set -o pipefail # don't ignore exit codes when piping output
|
||||
|
||||
echo "-----> Running post-frontend script"
|
||||
|
||||
# Move the frontend build to the nginx root and clean up
|
||||
mkdir -p build/
|
||||
mv src/frontend/apps/impress/out build/frontend-out
|
||||
|
||||
mv src/backend/* ./
|
||||
mv src/nginx/* ./
|
||||
|
||||
echo "3.13" > .python-version
|
||||
18
bin/buildpack_start.sh
Executable file
18
bin/buildpack_start.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start the Django backend server
|
||||
gunicorn -b :8000 impress.wsgi:application --log-file - &
|
||||
|
||||
# Start the Y provider service
|
||||
cd src/frontend/servers/y-provider && PORT=4444 ../../.scalingo/node/bin/node dist/start-server.js &
|
||||
|
||||
# Start the Nginx server
|
||||
bin/run &
|
||||
|
||||
# if the current shell is killed, also terminate all its children
|
||||
trap "pkill SIGTERM -P $$" SIGTERM
|
||||
|
||||
# wait for a single child to finish,
|
||||
wait -n
|
||||
# then kill all the other tasks
|
||||
pkill -P $$
|
||||
@@ -16,6 +16,7 @@ from socket import gethostbyname, gethostname
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import dj_database_url
|
||||
import sentry_sdk
|
||||
from configurations import Configuration, values
|
||||
from sentry_sdk.integrations.django import DjangoIntegration
|
||||
@@ -74,7 +75,9 @@ class Base(Configuration):
|
||||
|
||||
# Database
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"default": dj_database_url.config()
|
||||
if os.environ.get("DATABASE_URL")
|
||||
else {
|
||||
"ENGINE": values.Value(
|
||||
"django.db.backends.postgresql_psycopg2",
|
||||
environ_name="DB_ENGINE",
|
||||
|
||||
@@ -29,6 +29,7 @@ dependencies = [
|
||||
"boto3==1.38.18",
|
||||
"Brotli==1.1.0",
|
||||
"celery[redis]==5.5.2",
|
||||
"dj-database-url==2.3.0",
|
||||
"django-configurations==2.5.1",
|
||||
"django-cors-headers==4.7.0",
|
||||
"django-countries==7.6.1",
|
||||
|
||||
@@ -16,7 +16,6 @@ import { useTranslation } from 'react-i18next';
|
||||
import { isAPIError } from '@/api';
|
||||
import { Box, Icon } from '@/components';
|
||||
import { useDocOptions, useDocStore } from '@/docs/doc-management/';
|
||||
import { useAnalytics } from '@/libs';
|
||||
|
||||
import {
|
||||
AITransformActions,
|
||||
@@ -218,44 +217,22 @@ const AIMenuItemTransform = ({
|
||||
children,
|
||||
icon,
|
||||
}: PropsWithChildren<AIMenuItemTransform>) => {
|
||||
const { trackEvent } = useAnalytics();
|
||||
const { mutateAsync: requestAI, isPending } = useDocAITransform();
|
||||
const editor = useBlockNoteEditor();
|
||||
|
||||
const requestAIAction = async (selectedBlocks: Block[]) => {
|
||||
const text = await editor.blocksToMarkdownLossy(selectedBlocks);
|
||||
|
||||
const requestStartTime = performance.now();
|
||||
const responseAI = await requestAI({
|
||||
text,
|
||||
action,
|
||||
docId,
|
||||
});
|
||||
const requestDuration = performance.now() - requestStartTime;
|
||||
|
||||
const eventProperties = {
|
||||
eventName: 'requestAIAction',
|
||||
action: action,
|
||||
docId: docId,
|
||||
requestLength: text.length,
|
||||
numberBlocks: selectedBlocks.length,
|
||||
requestDuration: requestDuration,
|
||||
};
|
||||
|
||||
if (!responseAI?.answer) {
|
||||
trackEvent({
|
||||
...eventProperties,
|
||||
status: 'error',
|
||||
});
|
||||
throw new Error('No response from AI');
|
||||
}
|
||||
|
||||
trackEvent({
|
||||
...eventProperties,
|
||||
status: 'success',
|
||||
responseLength: String(responseAI.answer),
|
||||
});
|
||||
|
||||
const markdown = await editor.tryParseMarkdownToBlocks(responseAI.answer);
|
||||
editor.replaceBlocks(selectedBlocks, markdown);
|
||||
};
|
||||
|
||||
@@ -8,14 +8,12 @@ import { useCreateDoc } from '@/docs/doc-management';
|
||||
import { DocSearchModal } from '@/docs/doc-search';
|
||||
import { useAuth } from '@/features/auth';
|
||||
import { useCmdK } from '@/hook/useCmdK';
|
||||
import { useAnalytics } from '@/libs';
|
||||
|
||||
import { useLeftPanelStore } from '../stores';
|
||||
|
||||
export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
const router = useRouter();
|
||||
const { authenticated } = useAuth();
|
||||
const { trackEvent } = useAnalytics();
|
||||
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false);
|
||||
|
||||
const openSearchModal = useCallback(() => {
|
||||
@@ -25,10 +23,8 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
return;
|
||||
}
|
||||
|
||||
trackEvent({ eventName: 'openSearchModal', position: 'LeftPanelHeader' });
|
||||
|
||||
setIsSearchModalOpen(true);
|
||||
}, [trackEvent]);
|
||||
}, []);
|
||||
|
||||
const closeSearchModal = useCallback(() => {
|
||||
setIsSearchModalOpen(false);
|
||||
@@ -50,7 +46,6 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
};
|
||||
|
||||
const createNewDoc = () => {
|
||||
trackEvent({ eventName: 'createNewDoc', position: 'LeftPanelHeader' });
|
||||
createDoc();
|
||||
};
|
||||
|
||||
|
||||
@@ -9,15 +9,7 @@ type AnalyticEventUser = {
|
||||
email: string;
|
||||
};
|
||||
|
||||
export type AnalyticEventGeneric = {
|
||||
eventName: string;
|
||||
[key: string]: string | number;
|
||||
};
|
||||
|
||||
export type AnalyticEvent =
|
||||
| AnalyticEventClick
|
||||
| AnalyticEventUser
|
||||
| AnalyticEventGeneric;
|
||||
export type AnalyticEvent = AnalyticEventClick | AnalyticEventUser;
|
||||
|
||||
export abstract class AbstractAnalytic {
|
||||
public constructor() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import posthog from 'posthog-js';
|
||||
import { PostHogProvider as PHProvider } from 'posthog-js/react';
|
||||
import { JSX, PropsWithChildren, ReactNode, useEffect } from 'react';
|
||||
|
||||
import { AbstractAnalytic, AnalyticEvent, AnalyticEventGeneric } from '@/libs/';
|
||||
import { AbstractAnalytic, AnalyticEvent } from '@/libs/';
|
||||
|
||||
export class PostHogAnalytic extends AbstractAnalytic {
|
||||
private conf?: PostHogConf = undefined;
|
||||
@@ -19,14 +19,8 @@ export class PostHogAnalytic extends AbstractAnalytic {
|
||||
}
|
||||
|
||||
public trackEvent(evt: AnalyticEvent): void {
|
||||
switch (evt.eventName) {
|
||||
case 'user':
|
||||
posthog.identify(evt.id as string, { email: evt.email });
|
||||
break;
|
||||
default:
|
||||
const { eventName, ...properties } = evt as AnalyticEventGeneric;
|
||||
posthog.capture(eventName, properties);
|
||||
break;
|
||||
if (evt.eventName === 'user') {
|
||||
posthog.identify(evt.id, { email: evt.email });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"app:build": "yarn APP_IMPRESS run build",
|
||||
"app:test": "yarn APP_IMPRESS run test",
|
||||
"ci:build": "yarn APP_IMPRESS run build:ci",
|
||||
"build": "yarn APP_IMPRESS run build && yarn COLLABORATION_SERVER run build",
|
||||
"e2e:test": "yarn APP_E2E run test",
|
||||
"lint": "yarn APP_IMPRESS run lint && yarn APP_E2E run lint && yarn workspace eslint-config-impress run lint && yarn I18N run lint && yarn COLLABORATION_SERVER run lint",
|
||||
"i18n:extract": "yarn I18N run extract-translation",
|
||||
|
||||
114
src/nginx/servers.conf.erb
Normal file
114
src/nginx/servers.conf.erb
Normal file
@@ -0,0 +1,114 @@
|
||||
# ERB templated nginx configuration
|
||||
# see https://doc.scalingo.com/platform/deployment/buildpacks/nginx
|
||||
|
||||
upstream backend_server {
|
||||
server localhost:8000 fail_timeout=0;
|
||||
}
|
||||
|
||||
upstream collaboration_server {
|
||||
server localhost:4444 fail_timeout=0;
|
||||
}
|
||||
|
||||
server {
|
||||
|
||||
listen <%= ENV["PORT"] %>;
|
||||
server_name _;
|
||||
|
||||
root /app/build/frontend-out;
|
||||
|
||||
error_page 404 /404.html;
|
||||
|
||||
location /collaboration/api/ {
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_pass http://collaboration_server;
|
||||
}
|
||||
|
||||
location /collaboration/ws/ {
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
|
||||
# Set appropriate timeout for WebSocketAdd commentMore actions
|
||||
proxy_read_timeout 86400;
|
||||
proxy_send_timeout 86400;
|
||||
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_pass http://collaboration_server;
|
||||
}
|
||||
|
||||
# Django rest framework
|
||||
location ^~ /api/ {
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_pass http://backend_server;
|
||||
}
|
||||
|
||||
# Django admin
|
||||
location ^~ /admin/ {
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_pass http://backend_server;
|
||||
}
|
||||
|
||||
# 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 Object Storage
|
||||
proxy_pass <%= ENV["AWS_S3_ENDPOINT_URL"] %>/<%= ENV["AWS_STORAGE_BUCKET_NAME"] %>/;
|
||||
proxy_set_header Host <%= ENV["AWS_S3_ENDPOINT_URL"].split("://")[1] %>;
|
||||
|
||||
add_header Content-Security-Policy "default-src 'none'" always;
|
||||
}
|
||||
|
||||
location /media-auth {
|
||||
proxy_pass http://backend_server/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 / {
|
||||
try_files $uri index.html $uri/ =404;
|
||||
}
|
||||
|
||||
location ~ "^/docs/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/?$" {
|
||||
try_files $uri /docs/[id]/index.html;
|
||||
}
|
||||
|
||||
location = /404.html {
|
||||
internal;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user