Kalli
Server

Server Functioning & Core Logic

Fonctionnement & logique principale

Démarrage

apps/server/src/index.ts :

  1. appelle ensureDefaultRoles(), qui crée les rôles applicatifs admin (is_super_admin, ordre 0) et user (ordre 1) s'ils n'existent pas ;
  2. monte les middlewares Hono (logger, cors) ;
  3. monte Better Auth sur /api/auth/** ;
  4. monte le RPCHandler oRPC sur /rpc/*, avec createContext() ;
  5. 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 avec auth.api.getSession({ headers }).
  • lib/orpc.ts :
    • publicProcedure : aucune vérification ;
    • protectedProcedure : exige une session, sinon UNAUTHORIZED ;
    • adminProcedure : exige user.role === "admin" (rôle Better Auth), sinon FORBIDDEN.
  • middleware/update-last-connection.ts : sur les procédures protégées, met à jour user.last_connection sans 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édureAccèsEntréeDescription
getMepublicaucuneUtilisateur courant ou null
getAllUsersprotégéaucuneListe des utilisateurs
getUserNamesprotégé{ userIds: string[] }Identifiants et noms
getAllUsersWithDetailsadminaucuneListe détaillée (rôle, bannissement, dernière connexion)
getUserbyIdadmin{ userId: uuid }Utilisateur par identifiant
getUserbyEmailadmin{ userEmail: string }Utilisateur par e-mail
getUserLastConnectionadmin{ userId: uuid }Date de dernière connexion

Fichiers (routers/naivi.ts)

ProcédureAccèsEntréeDescription
getFilesprotégé{ type: "regular" | "qa" }Fichiers d'un type
getFileNameprotégé{ fileId }Nom du fichier
updateFileNameadmin{ fileId, name }Renomme ; l'ancien nom est conservé dans old_name
getFileHistoryprotégé{ fileId }Ancien nom (old_name)
updateFileStatusadmin{ fileId, status }status : pending, in_progress ou completed
deleteFileadmin{ fileId }Supprime le fichier ; ses chunks et paires suivent (cascade)
getFileProgressprotégé{ fileId }Avancement de la catégorisation des chunks
getQaFileProgressprotégé{ fileId }Avancement de la catégorisation des paires Q&A
processUploadedFileadmin{ fileContent, fileName, fileType?, chunkSize?, overlap? }Lance l'ingestion d'un document (asynchrone, voir Parsing)
processUploadedQAadmin{ fileContent, fileName, fileType? }Lance l'extraction de paires Q&A à partir d'un fichier
getDocumentProcessorJobStatusprotégé{ jobId }Statut d'un job document-processor
getDocumentProcessorJobStatusBatchprotégé{ jobIds: string[] }Statuts de plusieurs jobs
cancelDocumentProcessorJobadmin{ 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édureAccèsEntréeDescription
getFileChunksprotégé{ fileId }Chunks d'un fichier, par rang
insertChunkadmin{ fileId, rank, content, category }Insère un chunk à un rang donné
addManualChunkadmin{ content, category }Chunk sans fichier parent ; catégorie dynamic, static ou not an info
updateChunksCategoryadmin[{ chunkId, category }]Catégorisation en lot
updateChunkContentadmin{ chunkId, content }Modifie le contenu ; l'ancien est conservé dans old_content
getChunkCategoryprotégé{ chunkId }Catégorie
getChunkHistoryprotégé{ chunkId }Ancien contenu (old_content)
deleteChunkadmin{ 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 finetuned est 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 : pending s'il n'a aucun élément ou si aucun n'est catégorisé, completed quand plus rien n'est not categorized, in_progress sinon.
  • Une catégorisation en lot est vérifiée en entier avant toute écriture : un élément finetuned ou inconnu la rejette. Si un élément y figure deux fois, la dernière valeur s'applique.

Paires Q&A

ProcédureAccèsEntréeDescription
getQasFromFileprotégé{ fileId }Paires d'un fichier, par rang
insertQAPairadmin{ fileId, rank, question, answer, category }Insère une paire à un rang donné
addManualQAPairadmin{ question, answer, category }Paire sans fichier parent ; catégorie dynamic, static ou not an info
updateQaPairadmin{ id, question?, answer? }Modifie la question et/ou la réponse ; les anciennes valeurs sont conservées
updateQaPairCategoryadmin{ id, category }Catégorie d'une paire
updateQaPairsCategoryadmin[{ id, category }]Catégorisation en lot
getQaPairHistoryprotégé{ pairId }Anciennes question et réponse
deleteQaPairadmin{ 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édureAccèsEntréeDescription
getLogopublicaucuneLogo actif (hors logo_dark et logo_light), lu dans la base Chainlit, en base64 ; null si absent
regenerateIndexadminaucunePOST ${INDEX_API_URL}/regenerate-index. Renvoie { success, message } ou { success: false, error }
getAllFeedbackadminaucuneFeedbacks et étapes associées, lus dans la base Chainlit
transcribeAudioprotégé{ audioData, mimeType? }Transcription audio via LiteLLM
deleteUseradmin{ 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/)

FichierResponsabilité
regular-processing.tsIngestion d'un document : job, attente du résultat, insertion des chunks et des tags
qa-processing.tsExtraction des paires Q&A via document-processor (pipeline: "qa")
chunk-operations.tsCatégorie, contenu, suppression et progression des chunks
qa-operations.tsModification, catégorie et suppression des paires
file-operations.tsRenommage, statut de fichier
manual-operations.tsAjout de chunks et paires sans fichier parent
document-processor-client.tsClient HTTP de document-processor (/jobs, /jobs/{id})
sanitize.tsNettoyage du texte avant insertion PostgreSQL
user-operations.tsListe 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 dans routers/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_IN ou ADMIN_ONLY.
  • Accès base : passer par db/index.ts et les schémas de db/schema/.
  • Erreurs : lever une ORPCError avec un code (BAD_REQUEST, FORBIDDEN, etc.).