Server Functioning & Core Logic
Fonctionnement & logique principale
Démarrage
apps/server/src/index.ts :
- appelle
ensureDefaultRoles(), qui crée les rôles applicatifsadmin(is_super_admin, ordre 0) etuser(ordre 1) s'ils n'existent pas ; - monte les middlewares Hono (
logger,cors) ; - monte Better Auth sur
/api/auth/**; - monte le
RPCHandleroRPC sur/rpc/*, aveccreateContext(); - expose les sondes de santé et
GET /api/i18n.
CORS accepte localhost et 127.0.0.1 (tous ports) et l'origine CORS_ORIGIN, avec credentials: true.
Contexte et contrôle d'accès
lib/context.ts:createContext()lit la session avecauth.api.getSession({ headers }).lib/orpc.ts:publicProcedure: aucune vérification ;protectedProcedure: exige une session, sinonUNAUTHORIZED;adminProcedure: exigeuser.role === "admin"(rôle Better Auth), sinonFORBIDDEN.
middleware/update-last-connection.ts: sur les procédures protégées, met à jouruser.last_connectionsans bloquer la requête.
Le rôle Better Auth (user.role) et les rôles applicatifs (table roles) sont deux notions distinctes. Seul admin donne accès aux procédures admin. Voir Rôles & tags.
Procédures
Le nom des procédures est celui du code. Les entrées sont données dans leur forme principale ; les champs optionnels sont marqués ?.
Authentification (routers/auth.ts)
| Procédure | Accès | Entrée | Description |
|---|---|---|---|
getMe | public | aucune | Utilisateur courant ou null |
getAllUsers | protégé | aucune | Liste des utilisateurs |
getUserNames | protégé | { userIds: string[] } | Identifiants et noms |
getAllUsersWithDetails | admin | aucune | Liste détaillée (rôle, bannissement, dernière connexion) |
getUserbyId | admin | { userId: uuid } | Utilisateur par identifiant |
getUserbyEmail | admin | { userEmail: string } | Utilisateur par e-mail |
getUserLastConnection | admin | { userId: uuid } | Date de dernière connexion |
Fichiers (routers/naivi.ts)
| Procédure | Accès | Entrée | Description |
|---|---|---|---|
getFiles | protégé | { type: "regular" | "qa" } | Fichiers d'un type |
getFileName | protégé | { fileId } | Nom du fichier |
updateFileName | admin | { fileId, name } | Renomme ; l'ancien nom est conservé dans old_name |
getFileHistory | protégé | { fileId } | Ancien nom (old_name) |
updateFileStatus | admin | { fileId, status } | status : pending, in_progress ou completed |
deleteFile | admin | { fileId } | Supprime le fichier ; ses chunks et paires suivent (cascade) |
getFileProgress | protégé | { fileId } | Avancement de la catégorisation des chunks |
getQaFileProgress | protégé | { fileId } | Avancement de la catégorisation des paires Q&A |
processUploadedFile | admin | { fileContent, fileName, fileType?, chunkSize?, overlap? } | Lance l'ingestion d'un document (asynchrone, voir Parsing) |
processUploadedQA | admin | { fileContent, fileName, fileType? } | Lance l'extraction de paires Q&A à partir d'un fichier |
getDocumentProcessorJobStatus | protégé | { jobId } | Statut d'un job document-processor |
getDocumentProcessorJobStatusBatch | protégé | { jobIds: string[] } | Statuts de plusieurs jobs |
cancelDocumentProcessorJob | admin | { jobId } | Annule un job |
processUploadedFile et processUploadedQA renvoient { success, fileId, jobId } dès la soumission du job, puis { success: false, error } si l'enregistrement échoue. Le résultat final (chunks ou paires) n'est pas renvoyé par ces procédures : il est inséré en base quand le job se termine.
Chunks
| Procédure | Accès | Entrée | Description |
|---|---|---|---|
getFileChunks | protégé | { fileId } | Chunks d'un fichier, par rang |
insertChunk | admin | { fileId, rank, content, category } | Insère un chunk à un rang donné |
addManualChunk | admin | { content, category } | Chunk sans fichier parent ; catégorie dynamic, static ou not an info |
updateChunksCategory | admin | [{ chunkId, category }] | Catégorisation en lot |
updateChunkContent | admin | { chunkId, content } | Modifie le contenu ; l'ancien est conservé dans old_content |
getChunkCategory | protégé | { chunkId } | Catégorie |
getChunkHistory | protégé | { chunkId } | Ancien contenu (old_content) |
deleteChunk | admin | { chunkId } | Supprime un chunk |
Les règles des chunks et des paires Q&A vivent dans un seul module, apps/server/src/utils/curated-item.ts :
- Un élément
finetunedest en lecture seule : le serveur refuse de changer sa catégorie, de modifier son contenu et de le supprimer (BAD_REQUEST). La catégorie est posée hors du back office : aucune procédure ne l'accepte en création ni en changement. - Modifier le contenu d'un élément efface son embedding, recalculé à la prochaine régénération de l'index.
- Insérer, supprimer ou catégoriser un élément recalcule le statut de son fichier :
pendings'il n'a aucun élément ou si aucun n'est catégorisé,completedquand plus rien n'estnot categorized,in_progresssinon. - Une catégorisation en lot est vérifiée en entier avant toute écriture : un élément
finetunedou inconnu la rejette. Si un élément y figure deux fois, la dernière valeur s'applique.
Paires Q&A
| Procédure | Accès | Entrée | Description |
|---|---|---|---|
getQasFromFile | protégé | { fileId } | Paires d'un fichier, par rang |
insertQAPair | admin | { fileId, rank, question, answer, category } | Insère une paire à un rang donné |
addManualQAPair | admin | { question, answer, category } | Paire sans fichier parent ; catégorie dynamic, static ou not an info |
updateQaPair | admin | { id, question?, answer? } | Modifie la question et/ou la réponse ; les anciennes valeurs sont conservées |
updateQaPairCategory | admin | { id, category } | Catégorie d'une paire |
updateQaPairsCategory | admin | [{ id, category }] | Catégorisation en lot |
getQaPairHistory | protégé | { pairId } | Anciennes question et réponse |
deleteQaPair | admin | { id } | Supprime une paire |
Les paires suivent les mêmes règles que les chunks, ci-dessus.
Tags, rôles et associations
Voir Rôles & tags : getAllTags, createTag, updateTag, deleteTag, getAllRoles, createRole, updateRole, deleteRole, reorderRoles, getRoleTags, getAllRoleTags, getEffectiveRoleTags, setRoleTags, getItemTags, getItemTagsBatch, setItemTags, getFileTags, setFileTags, getUserRole, setUserRole.
Divers
| Procédure | Accès | Entrée | Description |
|---|---|---|---|
getLogo | public | aucune | Logo actif (hors logo_dark et logo_light), lu dans la base Chainlit, en base64 ; null si absent |
regenerateIndex | admin | aucune | POST ${INDEX_API_URL}/regenerate-index. Renvoie { success, message } ou { success: false, error } |
getAllFeedback | admin | aucune | Feedbacks et étapes associées, lus dans la base Chainlit |
transcribeAudio | protégé | { audioData, mimeType? } | Transcription audio via LiteLLM |
deleteUser | admin | { userId } | Ne supprime que l'entrée côté applicatif ; la suppression du compte est faite par le front via Better Auth |
Utilitaires métiers (utils/)
| Fichier | Responsabilité |
|---|---|
regular-processing.ts | Ingestion d'un document : job, attente du résultat, insertion des chunks et des tags |
qa-processing.ts | Extraction des paires Q&A via document-processor (pipeline: "qa") |
chunk-operations.ts | Catégorie, contenu, suppression et progression des chunks |
qa-operations.ts | Modification, catégorie et suppression des paires |
file-operations.ts | Renommage, statut de fichier |
manual-operations.ts | Ajout de chunks et paires sans fichier parent |
document-processor-client.ts | Client HTTP de document-processor (/jobs, /jobs/{id}) |
sanitize.ts | Nettoyage du texte avant insertion PostgreSQL |
user-operations.ts | Liste des utilisateurs |
Étendre le serveur
- Nouvelle procédure : l'ajouter dans un routeur de
apps/server/src/routers/, avec le bon niveau (publicProcedure,protectedProcedure,adminProcedure), puis la référencer dansrouters/index.ts. - Test d'accès obligatoire : chaque procédure doit figurer dans une seule liste de
apps/server/tests/authorization.test.ts:PUBLIC,SIGNED_INouADMIN_ONLY. - Accès base : passer par
db/index.tset les schémas dedb/schema/. - Erreurs : lever une
ORPCErroravec un code (BAD_REQUEST,FORBIDDEN, etc.).