mirror of
https://github.com/suitenumerique/docs.git
synced 2026-05-07 07:32:33 +02:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
302e5a503f | ||
|
|
a1c497ef27 | ||
|
|
89a44e4979 | ||
|
|
993394d91f | ||
|
|
8f386402e8 | ||
|
|
c7c252f9a1 | ||
|
|
5c0e7b9043 |
12
bin/Tiltfile
12
bin/Tiltfile
@@ -39,6 +39,18 @@ docker_build(
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
docker_build(
|
||||
'localhost:5001/impress-blocknote:latest',
|
||||
context='..',
|
||||
dockerfile='../src/blocknote/Dockerfile',
|
||||
only=['./src/blocknote', './docker', './.dockerignore'],
|
||||
target = 'production',
|
||||
live_update=[
|
||||
sync('../src/blocknote', '/home/blocknote'),
|
||||
]
|
||||
)
|
||||
|
||||
k8s_yaml(local('cd ../src/helm && helmfile -n impress -e dev template .'))
|
||||
|
||||
migration = '''
|
||||
|
||||
@@ -134,6 +134,16 @@ services:
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
||||
blocknote-converter:
|
||||
user: "${DOCKER_USER:-1000}"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./src/blocknote/Dockerfile
|
||||
target: production
|
||||
image: blocknote:blocknote-production
|
||||
ports:
|
||||
- "8081:8081"
|
||||
|
||||
dockerize:
|
||||
image: jwilder/dockerize
|
||||
|
||||
|
||||
5
src/blocknote/.prettierrc
Normal file
5
src/blocknote/.prettierrc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"semi": false,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true
|
||||
}
|
||||
28
src/blocknote/Dockerfile
Normal file
28
src/blocknote/Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
||||
FROM node:20-alpine AS dependencies
|
||||
|
||||
WORKDIR /home/blocknote
|
||||
|
||||
COPY ./src/blocknote/package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY .dockerignore ./.dockerignore
|
||||
COPY ./src/blocknote/ .
|
||||
|
||||
FROM dependencies AS blocknote-builder
|
||||
|
||||
WORKDIR /home/blocknote
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# ---- Blocknote image ----
|
||||
FROM blocknote-builder AS production
|
||||
|
||||
# Un-privileged user running the application
|
||||
ARG DOCKER_USER
|
||||
USER ${DOCKER_USER}
|
||||
|
||||
COPY ./docker/files/usr/local/bin/entrypoint /usr/local/bin/entrypoint
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint"]
|
||||
CMD ["npm", "run", "start"]
|
||||
5
src/blocknote/nodemon.json
Normal file
5
src/blocknote/nodemon.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"watch": ["src"],
|
||||
"ext": "ts",
|
||||
"exec": "concurrently \"npx tsc --watch\" \"ts-node src/index.ts\""
|
||||
}
|
||||
5084
src/blocknote/package-lock.json
generated
Normal file
5084
src/blocknote/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
src/blocknote/package.json
Normal file
35
src/blocknote/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "blocknote-server",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"keywords": [
|
||||
"nodejs",
|
||||
"bootstrap",
|
||||
"express"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npx tsc",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "nodemon src/index.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"format": "prettier --write ./src",
|
||||
"check": "prettier --check ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blocknote/server-util": "0.17.1",
|
||||
"dotenv": "16.4.5",
|
||||
"express": "4.21.1",
|
||||
"prettier": "3.3.3",
|
||||
"yjs": "13.6.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "5.0.0",
|
||||
"@types/node": "22.7.7",
|
||||
"concurrently": "9.0.1",
|
||||
"nodemon": "3.1.7",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.6.3",
|
||||
"prettier": "3.3.3"
|
||||
}
|
||||
}
|
||||
37
src/blocknote/src/index.ts
Normal file
37
src/blocknote/src/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import express, { Express, Request, Response } from 'express'
|
||||
import { asyncWrapper, convertMarkdown } from './utils'
|
||||
import dotenv from 'dotenv'
|
||||
import bodyParser from 'body-parser'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const app: Express = express()
|
||||
const router = express.Router()
|
||||
const port = process.env.PORT ?? 8081
|
||||
|
||||
app.use(bodyParser.json())
|
||||
app.use(bodyParser.urlencoded({ extended: true }))
|
||||
|
||||
// Logging middleware, logs the request method and path for each incoming request
|
||||
router.use(async function (req, res, next) {
|
||||
console.log(`/${req.method}`)
|
||||
next()
|
||||
})
|
||||
|
||||
// Liveness probe endpoint for Kubernetes health checks
|
||||
router.get('/__heartbeat__', (req: Request, res: Response) => {
|
||||
res.status(200).send({ status: 'OK' })
|
||||
})
|
||||
|
||||
// Load balancer heartbeat check, useful to detect app readiness
|
||||
router.get('/__lbheartbeat__', (req: Request, res: Response) => {
|
||||
res.status(200).send({ status: 'OK' })
|
||||
})
|
||||
|
||||
router.post('/', asyncWrapper(convertMarkdown))
|
||||
|
||||
app.use('/', router)
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`[server]: Server listening on port ${port}`)
|
||||
})
|
||||
60
src/blocknote/src/utils.ts
Normal file
60
src/blocknote/src/utils.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
// Utility functions for handling markdown conversion and related operations
|
||||
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { ServerBlockNoteEditor } from '@blocknote/server-util'
|
||||
import Y from 'yjs'
|
||||
|
||||
const toBase64 = function (str: Uint8Array) {
|
||||
return Buffer.from(str).toString('base64')
|
||||
}
|
||||
|
||||
export const asyncWrapper = (
|
||||
asyncFn: (req: Request, res: Response) => Promise<Response>
|
||||
) => {
|
||||
return function (req: Request, res: Response, next: NextFunction) {
|
||||
asyncFn(req, res).catch(next)
|
||||
}
|
||||
}
|
||||
|
||||
const validateContent = (content: string | undefined): string => {
|
||||
if (!content) {
|
||||
throw new Error('Content is required')
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
const parseMarkdownToBlocks = async (
|
||||
blockNoteEditor: ServerBlockNoteEditor,
|
||||
content: string
|
||||
) => {
|
||||
try {
|
||||
const blocks = await blockNoteEditor.tryParseMarkdownToBlocks(content)
|
||||
if (!blocks || blocks.length === 0) {
|
||||
throw new Error('No valid blocks generated')
|
||||
}
|
||||
return blocks
|
||||
} catch (error) {
|
||||
throw new Error('Failed to parse markdown content')
|
||||
}
|
||||
}
|
||||
|
||||
const processContentBlocks = (server: ServerBlockNoteEditor, blocks: any[]) => {
|
||||
try {
|
||||
const yDocument = server.blocksToYDoc(blocks, 'document-store')
|
||||
return toBase64(Y.encodeStateAsUpdate(yDocument))
|
||||
} catch (error) {
|
||||
throw new Error('Failed to process content blocks')
|
||||
}
|
||||
}
|
||||
|
||||
export const convertMarkdown = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const content = validateContent(req.body.content)
|
||||
const editor = ServerBlockNoteEditor.create()
|
||||
const blocks = await parseMarkdownToBlocks(editor, content)
|
||||
const encodedContent = processContentBlocks(editor, blocks)
|
||||
return res.send({ content: encodedContent })
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: (error as Error).message })
|
||||
}
|
||||
}
|
||||
11
src/blocknote/tsconfig.json
Normal file
11
src/blocknote/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,14 @@ yProvider:
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
blocknote:
|
||||
replicas: 1
|
||||
|
||||
image:
|
||||
repository: localhost:5001/impress-blocknote
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
host: impress.127.0.0.1.nip.io
|
||||
|
||||
@@ -157,6 +157,15 @@ Requires top level scope
|
||||
{{ include "impress.fullname" . }}-y-provider
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Full name for the blocknote
|
||||
|
||||
Requires top level scope
|
||||
*/}}
|
||||
{{- define "impress.blocknote.fullname" -}}
|
||||
{{ include "impress.fullname" . }}-blocknote
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Usage : {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" .Values.path.to.the.image1) }}
|
||||
*/}}
|
||||
|
||||
136
src/helm/impress/templates/blocknote_deployment.yaml
Normal file
136
src/helm/impress/templates/blocknote_deployment.yaml
Normal file
@@ -0,0 +1,136 @@
|
||||
{{- $envVars := include "impress.common.env" (list . .Values.blocknote) -}}
|
||||
{{- $fullName := include "impress.blocknote.fullname" . -}}
|
||||
{{- $component := "blocknote" -}}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.blocknote.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "impress.common.selectorLabels" (list . $component) | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
{{- with .Values.blocknote.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "impress.common.selectorLabels" (list . $component) | nindent 8 }}
|
||||
spec:
|
||||
{{- if $.Values.image.credentials }}
|
||||
imagePullSecrets:
|
||||
- name: {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" $.Values.image.credentials) }}
|
||||
{{- end}}
|
||||
shareProcessNamespace: {{ .Values.blocknote.shareProcessNamespace }}
|
||||
containers:
|
||||
{{- with .Values.blocknote.sidecars }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ (.Values.blocknote.image | default dict).repository | default .Values.image.repository }}:{{ (.Values.blocknote.image | default dict).tag | default .Values.image.tag }}"
|
||||
imagePullPolicy: {{ (.Values.blocknote.image | default dict).pullPolicy | default .Values.image.pullPolicy }}
|
||||
{{- with .Values.blocknote.command }}
|
||||
command:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.blocknote.args }}
|
||||
args:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
env:
|
||||
{{- if $envVars}}
|
||||
{{- $envVars | indent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.blocknote.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.blocknote.service.targetPort }}
|
||||
protocol: TCP
|
||||
{{- if .Values.blocknote.probes.liveness }}
|
||||
livenessProbe:
|
||||
{{- include "impress.probes.abstract" (merge .Values.blocknote.probes.liveness (dict "targetPort" .Values.blocknote.service.targetPort )) | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.blocknote.probes.readiness }}
|
||||
readinessProbe:
|
||||
{{- include "impress.probes.abstract" (merge .Values.blocknote.probes.readiness (dict "targetPort" .Values.blocknote.service.targetPort )) | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.blocknote.probes.startup }}
|
||||
startupProbe:
|
||||
{{- include "impress.probes.abstract" (merge .Values.blocknote.probes.startup (dict "targetPort" .Values.blocknote.service.targetPort )) | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.blocknote.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
volumeMounts:
|
||||
{{- range $index, $value := .Values.mountFiles }}
|
||||
- name: "files-{{ $index }}"
|
||||
mountPath: {{ $value.path }}
|
||||
subPath: content
|
||||
{{- end }}
|
||||
{{- range $name, $volume := .Values.blocknote.persistence }}
|
||||
- name: "{{ $name }}"
|
||||
mountPath: "{{ $volume.mountPath }}"
|
||||
{{- end }}
|
||||
{{- range .Values.blocknote.extraVolumeMounts }}
|
||||
- name: {{ .name }}
|
||||
mountPath: {{ .mountPath }}
|
||||
subPath: {{ .subPath | default "" }}
|
||||
readOnly: {{ .readOnly }}
|
||||
{{- end }}
|
||||
{{- with .Values.blocknote.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.blocknote.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.blocknote.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
{{- range $index, $value := .Values.mountFiles }}
|
||||
- name: "files-{{ $index }}"
|
||||
configMap:
|
||||
name: "{{ include "impress.fullname" $ }}-files-{{ $index }}"
|
||||
{{- end }}
|
||||
{{- range $name, $volume := .Values.blocknote.persistence }}
|
||||
- name: "{{ $name }}"
|
||||
{{- if eq $volume.type "emptyDir" }}
|
||||
emptyDir: {}
|
||||
{{- else }}
|
||||
persistentVolumeClaim:
|
||||
claimName: "{{ $fullName }}-{{ $name }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range .Values.blocknote.extraVolumes }}
|
||||
- name: {{ .name }}
|
||||
{{- if .existingClaim }}
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .existingClaim }}
|
||||
{{- else if .hostPath }}
|
||||
hostPath:
|
||||
{{ toYaml .hostPath | nindent 12 }}
|
||||
{{- else if .csi }}
|
||||
csi:
|
||||
{{- toYaml .csi | nindent 12 }}
|
||||
{{- else if .configMap }}
|
||||
configMap:
|
||||
{{- toYaml .configMap | nindent 12 }}
|
||||
{{- else if .emptyDir }}
|
||||
emptyDir:
|
||||
{{- toYaml .emptyDir | nindent 12 }}
|
||||
{{- else }}
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
21
src/helm/impress/templates/blocknote_svc.yaml
Normal file
21
src/helm/impress/templates/blocknote_svc.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
{{- $envVars := include "impress.common.env" (list . .Values.blocknote) -}}
|
||||
{{- $fullName := include "impress.blocknote.fullname" . -}}
|
||||
{{- $component := "blocknote" -}}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
|
||||
annotations:
|
||||
{{- toYaml $.Values.blocknote.service.annotations | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.blocknote.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.blocknote.service.port }}
|
||||
targetPort: {{ .Values.blocknote.service.targetPort }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "impress.common.selectorLabels" (list . $component) | nindent 4 }}
|
||||
@@ -412,4 +412,93 @@ yProvider:
|
||||
extraVolumeMounts: []
|
||||
|
||||
## @param yProvider.extraVolumes Additional volumes to mount on the yProvider.
|
||||
extraVolumes: []
|
||||
extraVolumes: []
|
||||
|
||||
## @section blocknote
|
||||
|
||||
blocknote:
|
||||
|
||||
## @param blocknote.command Override the blocknote container command
|
||||
command: []
|
||||
|
||||
## @param blocknote.args Override the blocknote container args
|
||||
args: []
|
||||
|
||||
## @param blocknote.replicas Amount of blocknote replicas
|
||||
replicas: 3
|
||||
|
||||
## @param blocknote.shareProcessNamespace Enable share process namespace between containers
|
||||
shareProcessNamespace: false
|
||||
|
||||
## @param blocknote.sidecars Add sidecars containers to blocknote deployment
|
||||
sidecars: []
|
||||
|
||||
## @param blocknote.securityContext Configure blocknote Pod security context
|
||||
securityContext: null
|
||||
|
||||
## @param blocknote.envVars Configure blocknote container environment variables
|
||||
## @extra blocknote.envVars.BY_VALUE Example environment variable by setting value directly
|
||||
## @extra blocknote.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
|
||||
## @extra blocknote.envVars.FROM_CONFIGMAP.configMapKeyRef.key Key within a ConfigMap when configuring env vars from a ConfigMap
|
||||
## @extra blocknote.envVars.FROM_SECRET.secretKeyRef.name Name of a Secret when configuring env vars from a Secret
|
||||
## @extra blocknote.envVars.FROM_SECRET.secretKeyRef.key Key within a Secret when configuring env vars from a Secret
|
||||
## @skip blocknote.envVars
|
||||
envVars:
|
||||
<<: *commonEnvVars
|
||||
|
||||
## @param blocknote.podAnnotations Annotations to add to the blocknote Pod
|
||||
podAnnotations: {}
|
||||
|
||||
## @param blocknote.service.type blocknote Service type
|
||||
## @param blocknote.service.port blocknote Service listening port
|
||||
## @param blocknote.service.targetPort blocknote container listening port
|
||||
## @param blocknote.service.annotations Annotations to add to the blocknote Service
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 8081
|
||||
annotations: {}
|
||||
|
||||
## @param blocknote.probes.liveness.path [nullable] Configure path for blocknote HTTP liveness probe
|
||||
## @param blocknote.probes.liveness.targetPort [nullable] Configure port for blocknote HTTP liveness probe
|
||||
## @param blocknote.probes.liveness.initialDelaySeconds [nullable] Configure initial delay for blocknote liveness probe
|
||||
## @param blocknote.probes.liveness.initialDelaySeconds [nullable] Configure timeout for blocknote liveness probe
|
||||
## @param blocknote.probes.startup.path [nullable] Configure path for blocknote HTTP startup probe
|
||||
## @param blocknote.probes.startup.targetPort [nullable] Configure port for blocknote HTTP startup probe
|
||||
## @param blocknote.probes.startup.initialDelaySeconds [nullable] Configure initial delay for blocknote startup probe
|
||||
## @param blocknote.probes.startup.initialDelaySeconds [nullable] Configure timeout for blocknote startup probe
|
||||
## @param blocknote.probes.readiness.path [nullable] Configure path for blocknote HTTP readiness probe
|
||||
## @param blocknote.probes.readiness.targetPort [nullable] Configure port for blocknote HTTP readiness probe
|
||||
## @param blocknote.probes.readiness.initialDelaySeconds [nullable] Configure initial delay for blocknote readiness probe
|
||||
## @param blocknote.probes.readiness.initialDelaySeconds [nullable] Configure timeout for blocknote readiness probe
|
||||
probes:
|
||||
liveness:
|
||||
path: /__heartbeat__
|
||||
initialDelaySeconds: 10
|
||||
readiness:
|
||||
path: /__lbheartbeat__
|
||||
initialDelaySeconds: 10
|
||||
|
||||
## @param blocknote.resources Resource requirements for the blocknote container
|
||||
resources: {}
|
||||
|
||||
## @param blocknote.nodeSelector Node selector for the blocknote Pod
|
||||
nodeSelector: {}
|
||||
|
||||
## @param blocknote.tolerations Tolerations for the blocknote Pod
|
||||
tolerations: []
|
||||
|
||||
## @param blocknote.affinity Affinity for the blocknote Pod
|
||||
affinity: {}
|
||||
|
||||
## @param blocknote.persistence Additional volumes to create and mount on the blocknote. Used for debugging purposes
|
||||
## @extra blocknote.persistence.volume-name.size Size of the additional volume
|
||||
## @extra blocknote.persistence.volume-name.type Type of the additional volume, persistentVolumeClaim or emptyDir
|
||||
## @extra blocknote.persistence.volume-name.mountPath Path where the volume should be mounted to
|
||||
persistence: {}
|
||||
|
||||
## @param blocknote.extraVolumeMounts Additional volumes to mount on the blocknote.
|
||||
extraVolumeMounts: []
|
||||
|
||||
## @param blocknote.extraVolumes Additional volumes to mount on the blocknote.
|
||||
extraVolumes: []
|
||||
|
||||
Reference in New Issue
Block a user