Compare commits
14
Commits
f74e4d1043
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5863858b09 | ||
|
|
535603f6d3 | ||
|
|
058370830f | ||
|
|
bedb6c98e7 | ||
|
|
51bbb45b32 | ||
|
|
a970bfa991 | ||
|
|
509a3b14c8 | ||
|
|
345129c7bf | ||
|
|
87eeb41364 | ||
|
|
0a6ffab050 | ||
|
|
a36f1231cd | ||
|
|
b3be1a81e6 | ||
|
|
1b5abb566e | ||
|
|
a92cc76a9a |
No files matched your search
@@ -0,0 +1,110 @@
|
||||
# Lassistanoque
|
||||
|
||||
Assistant IA auto-hébergé : backend Go (API + SPA embarquée), base SQLite, connecteurs LLM multiples (via [any-llm](https://github.com/mozilla-ai/any-llm-go)), authentification par mot de passe ou OpenID Connect.
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Go >= 1.26
|
||||
- Node.js + npm (build du frontend)
|
||||
- (optionnel) un serveur OpenID Connect, ex. [Keycloak](https://www.keycloak.org/)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# 1. frontend : build la SPA et l'écrit dans backend/internal/http/web
|
||||
cd backend/web
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
# 2. backend
|
||||
cd ..
|
||||
go build -o lassistanoque .
|
||||
```
|
||||
|
||||
Le `Makefile` du backend enchaîne les deux et produit les binaires multi-plateformes dans `build/` :
|
||||
|
||||
```bash
|
||||
make build # linux/amd64, linux/arm64, windows, darwin...
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
La configuration se fait par variables d'environnement, chargeables depuis un fichier `.env` placé dans le répertoire de lancement (backend) :
|
||||
|
||||
| Variable | Défaut | Description |
|
||||
|----------------------------|--------------------|----------------------------------------------------|
|
||||
| `LASSISTANOQUE_DB_TYPE` | `turso` | Type de base (seul `turso`/SQLite est implémenté) |
|
||||
| `LASSISTANOQUE_DB_URL` | `lassistanoque.db` | Chemin du fichier SQLite |
|
||||
| `LASSISTANOQUE_HTTP_PORT` | `3000` | Port du serveur HTTP |
|
||||
| `LASSISTANOQUE_JWT_SECRET` | *(obligatoire)* | Secret de signature des tokens, ex. `openssl rand -base64 32` |
|
||||
| `LASSISTANOQUE_STORAGE_TYPE`| `database` | Stockage des fichiers (seul `database` est implémenté) |
|
||||
| `LASSISTANOQUE_SERVER_ID` | `default` | Identifiant du serveur |
|
||||
|
||||
Exemple `backend/.env` :
|
||||
|
||||
```env
|
||||
LASSISTANOQUE_JWT_SECRET=<secret généré>
|
||||
LASSISTANOQUE_HTTP_PORT=3000
|
||||
```
|
||||
|
||||
## Lancement
|
||||
|
||||
```bash
|
||||
./lassistanoque serve
|
||||
```
|
||||
|
||||
- Les migrations de la base sont appliquées automatiquement au démarrage (ou manuellement avec `./lassistanoque migrate`).
|
||||
- L'application est servie sur `http://localhost:3000` (SPA + API `/api`).
|
||||
- Créez le premier compte depuis la page d'inscription (`/register`), puis configurez un fournisseur LLM et sa clé d'API dans les réglages de l'espace (`settings/models`).
|
||||
|
||||
## Authentification OpenID Connect
|
||||
|
||||
Les fournisseurs OIDC sont enregistrés dans la table `oidc` :
|
||||
|
||||
```sql
|
||||
insert into oidc (id, label, domain, client_id, client_secret, wellknown_url, _date_created, _version)
|
||||
values (
|
||||
'keycloak', -- identifiant du fournisseur
|
||||
'Keycloak', -- libellé affiché sur la page de connexion
|
||||
'sso.example.com', -- domaine du fournisseur
|
||||
'lassistanoque', -- client_id
|
||||
'<client_secret>',
|
||||
'https://<keycloak>/realms/<realm>/.well-known/openid-configuration',
|
||||
datetime('now'),
|
||||
'v1'
|
||||
);
|
||||
```
|
||||
|
||||
Côté fournisseur (Keycloak) :
|
||||
|
||||
- client de type *confidential*, *Standard Flow* (authorization code) activé ;
|
||||
- *Valid Redirect URIs* : `http://localhost:3000/api/auth/oidc/callback` (l'URL de callback est reconstruite depuis le `Host` de la requête ; derrière un reverse proxy, transmettre `X-Forwarded-Proto` et `X-Forwarded-Host`).
|
||||
|
||||
Flux : les fournisseurs configurés apparaissent comme boutons sur la page de connexion → redirection vers le fournisseur → au retour, le backend valide le `state` signé, échange le code, vérifie le token ID (signature JWKS, émetteur, audience) puis délivre les tokens applicatifs. À la première connexion, l'utilisateur est créé automatiquement avec son espace personnel.
|
||||
|
||||
## Développement
|
||||
|
||||
```bash
|
||||
# backend (API sur :3000)
|
||||
cd backend
|
||||
go run . serve
|
||||
|
||||
# frontend en mode dev (Vite sur :5173, API proxyfiée vers :3000)
|
||||
cd web
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Structure :
|
||||
|
||||
```
|
||||
backend/
|
||||
├── cmd/ # CLI (serve, migrate, version) et assemblage IoC
|
||||
├── internal/
|
||||
│ ├── adapter/ # implémentations : db (turso/SQLite), auth (password, oidc), llm, tools, security (JWT)
|
||||
│ ├── domain/ # entités et interfaces des repositories
|
||||
│ ├── http/ # serveur HTTP, routes, SPA embarquée
|
||||
│ └── service/ # logique métier (auth, chat, agents, tasks...)
|
||||
└── web/ # frontend SvelteKit
|
||||
```
|
||||
|
||||
Tests : `go test ./backend/...`
|
||||
@@ -0,0 +1,102 @@
|
||||
<mxfile host="Electron">
|
||||
<diagram name="Page-1" id="6ee6LexvoJe0i-CFFPA3">
|
||||
<mxGraphModel dx="1067" dy="848" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-29" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=light-dark(#FFFFFF,transparent);strokeColor=#7D7D7D;" value="" vertex="1">
|
||||
<mxGeometry height="270" width="220" x="540" y="140" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-15" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-11">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-1" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ChatService" vertex="1">
|
||||
<mxGeometry height="120" width="140" x="290" y="425" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-2" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewChat -&gt; Chat" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-3" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="AddMessage" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-22" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="getChat" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-16" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-11">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-5" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="BrainHandler" vertex="1">
|
||||
<mxGeometry height="150" width="140" x="590" y="450" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-6" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="ProcessResponse" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-24" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="subscribe" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-7" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Start" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-8" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Stop" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-10" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-11" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ChatRepository" vertex="1">
|
||||
<mxGeometry height="90" width="140" x="400" y="710" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-12" parent="Nf3PCM6Bg_LUHkPQi3Rq-11" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="CreateChat" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-13" parent="Nf3PCM6Bg_LUHkPQi3Rq-11" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="CreateMessage" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-21" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-23" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-22">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-17" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="WebHandler" vertex="1">
|
||||
<mxGeometry height="120" width="140" x="30" y="425" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-18" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewChat" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-25" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewMessage" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-19" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="GetChatWithMessages" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-26" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-2">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="240" y="400" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-27" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-24">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-28" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-38" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="RunningSession" vertex="1">
|
||||
<mxGeometry height="120" width="140" x="570" y="230" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-39" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 1" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-40" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 2" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-41" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 3" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-42" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Goroutine" vertex="1">
|
||||
<mxGeometry height="30" width="60" x="560" y="150" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,108 @@
|
||||
<mxfile host="Electron">
|
||||
<diagram name="Page-1" id="6ee6LexvoJe0i-CFFPA3">
|
||||
<mxGraphModel dx="1067" dy="848" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-43" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
|
||||
<mxGeometry height="250" width="510" x="260" y="380" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-29" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=light-dark(#FFFFFF,transparent);strokeColor=#7D7D7D;" value="" vertex="1">
|
||||
<mxGeometry height="270" width="220" x="520" y="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-15" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-11">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-1" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ChatService" vertex="1">
|
||||
<mxGeometry height="120" width="140" x="290" y="425" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-2" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewChat -&gt; Chat" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-3" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="AddMessage" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-22" parent="Nf3PCM6Bg_LUHkPQi3Rq-1" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="getChat" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-16" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-11">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-5" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="BrainHandler" vertex="1">
|
||||
<mxGeometry height="150" width="140" x="590" y="450" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-6" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="ProcessResponse" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-24" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="subscribe" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-7" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Start" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-8" parent="Nf3PCM6Bg_LUHkPQi3Rq-5" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Stop" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-10" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-11" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ChatRepository" vertex="1">
|
||||
<mxGeometry height="90" width="140" x="400" y="710" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-12" parent="Nf3PCM6Bg_LUHkPQi3Rq-11" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="CreateChat" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-13" parent="Nf3PCM6Bg_LUHkPQi3Rq-11" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="CreateMessage" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-21" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-23" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-22">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-17" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="WebHandler" vertex="1">
|
||||
<mxGeometry height="120" width="140" x="30" y="425" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-18" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewChat" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-25" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="NewMessage" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-19" parent="Nf3PCM6Bg_LUHkPQi3Rq-17" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="GetChatWithMessages" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-26" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-2">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="240" y="400" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-27" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-24">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-28" edge="1" parent="1" source="Nf3PCM6Bg_LUHkPQi3Rq-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" target="Nf3PCM6Bg_LUHkPQi3Rq-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-38" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="RunningSession" vertex="1">
|
||||
<mxGeometry height="120" width="140" x="550" y="140" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-39" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 1" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-40" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 2" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-41" parent="Nf3PCM6Bg_LUHkPQi3Rq-38" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;" value="Item 3" vertex="1">
|
||||
<mxGeometry height="30" width="140" y="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-42" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Goroutine" vertex="1">
|
||||
<mxGeometry height="30" width="60" x="540" y="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Nf3PCM6Bg_LUHkPQi3Rq-44" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Fusionner" vertex="1">
|
||||
<mxGeometry height="30" width="60" x="480" y="380" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,163 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/auth/oidc"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/auth/password"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/database"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/file"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/llm"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/security"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/tools/browser"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/tools/scheduler"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/tools/searxng"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/tools/weather"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/agent"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/chat"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/provider"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/storage"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/task"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/tool"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/user"
|
||||
)
|
||||
|
||||
var db database.Database
|
||||
|
||||
// adapters
|
||||
var storeAdapter storage.StorageProvider
|
||||
var llmengine domain.LLMEngine
|
||||
|
||||
// services
|
||||
var authService *auth.Service
|
||||
var userService *user.Service
|
||||
var storageService *storage.Service
|
||||
var providerService *provider.Service
|
||||
var agentService *agent.Service
|
||||
var toolService *tool.Service
|
||||
var chatService *chat.Service
|
||||
var taskService *task.Service
|
||||
var tokenManager auth.TokenManager
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
db, err = database.GetDatabase()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = db.Migrate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Close() {
|
||||
db.Close()
|
||||
}
|
||||
|
||||
// Adapters
|
||||
func getStoreAdapter() storage.StorageProvider {
|
||||
if storeAdapter == nil {
|
||||
var err error
|
||||
storeAdapter, err = file.GetStorageProvider(db.FileRepository())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return storeAdapter
|
||||
}
|
||||
|
||||
func getAuthenticators() map[string]auth.Authenticator {
|
||||
authenticators := make(map[string]auth.Authenticator)
|
||||
pwdAuth := password.NewPasswordAuthenticator(GetTokenManager(), db.UserRepository())
|
||||
authenticators["password"] = pwdAuth
|
||||
oidcAuth := oidc.NewOIDCAuthenticator(GetTokenManager(), db.OIDCProviderRepository(), db.UserRepository())
|
||||
authenticators["oidc"] = oidcAuth
|
||||
return authenticators
|
||||
}
|
||||
|
||||
func GetTokenManager() auth.TokenManager {
|
||||
if tokenManager == nil {
|
||||
tokenManager = security.NewJwtTokenManager(12*time.Hour, 7*24*time.Hour, "lassistanoque")
|
||||
}
|
||||
return tokenManager
|
||||
}
|
||||
|
||||
func GetLLMAdapter() domain.LLMEngine {
|
||||
if llmengine == nil {
|
||||
llmengine = llm.NewAnyLLMEngine()
|
||||
}
|
||||
return llmengine
|
||||
}
|
||||
|
||||
// Services
|
||||
func GetStorageService() *storage.Service {
|
||||
if storageService == nil {
|
||||
storageService = storage.NewService(getStoreAdapter())
|
||||
}
|
||||
return storageService
|
||||
}
|
||||
|
||||
func GetAuthService() *auth.Service {
|
||||
if authService == nil {
|
||||
authService = auth.NewService(db.SettingsRepository(), db.UserRepository(), db.OIDCProviderRepository(), GetTokenManager(), getAuthenticators())
|
||||
}
|
||||
return authService
|
||||
}
|
||||
|
||||
func GetUserService() *user.Service {
|
||||
if userService == nil {
|
||||
userService = user.NewService(db.UserRepository())
|
||||
}
|
||||
return userService
|
||||
}
|
||||
func GetProviderService() *provider.Service {
|
||||
if providerService == nil {
|
||||
providerService = provider.NewService(db.ProviderRepository(), db.UserRepository(), GetLLMAdapter())
|
||||
}
|
||||
return providerService
|
||||
}
|
||||
|
||||
func GetAgentService() *agent.Service {
|
||||
if agentService == nil {
|
||||
agentService = agent.NewService(db.AgentRepository(), db.UserRepository())
|
||||
}
|
||||
return agentService
|
||||
}
|
||||
func GetChatService() *chat.Service {
|
||||
if chatService == nil {
|
||||
chatService = chat.NewService(
|
||||
db.UserRepository(),
|
||||
db.AgentRepository(),
|
||||
db.ProviderRepository(),
|
||||
db.ChatRepository(),
|
||||
GetLLMAdapter(),
|
||||
GetToolService(),
|
||||
)
|
||||
// l'exécution des tâches planifiées passe par le service de chat
|
||||
GetTaskService().SetChatService(chatService)
|
||||
}
|
||||
return chatService
|
||||
}
|
||||
|
||||
func GetTaskService() *task.Service {
|
||||
if taskService == nil {
|
||||
taskService = task.NewService(db.TaskRepository())
|
||||
}
|
||||
return taskService
|
||||
}
|
||||
|
||||
func GetToolService() *tool.Service {
|
||||
if toolService == nil {
|
||||
toolService = tool.NewService(db.ToolRepository(), db.UserRepository())
|
||||
toolService.RegisterToolImpl(&weather.WeatherTool{})
|
||||
toolService.RegisterToolImpl(&searxng.SearxngTool{})
|
||||
toolService.RegisterToolImpl(&browser.BrowserTool{})
|
||||
toolService.RegisterToolImpl(scheduler.New(GetTaskService()))
|
||||
}
|
||||
return toolService
|
||||
}
|
||||
+23
-59
@@ -11,18 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/auth/password"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/database"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/file"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/llm"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/security"
|
||||
"trankilou.fr/lassistanoque/backend/internal/gateway"
|
||||
"trankilou.fr/lassistanoque/backend/internal/http"
|
||||
"trankilou.fr/lassistanoque/backend/internal/llmserver"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/agent"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/provider"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/storage"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/user"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -45,67 +35,41 @@ func newServeCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
func runServe() {
|
||||
// database
|
||||
db, err := database.GetDatabase()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = db.Migrate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// storage
|
||||
store, err := file.GetStorageProvider(db.FileRepository())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Adapters
|
||||
tokenManager := security.NewJwtTokenManager(12*time.Hour, 7*24*time.Hour, "lassistanoque")
|
||||
pwdAuth := password.NewPasswordAuthenticator(tokenManager, db.UserRepository())
|
||||
llmEngine := llm.NewAnyLLMEngine()
|
||||
|
||||
// services
|
||||
authService := auth.NewService(
|
||||
db.SettingsRepository(),
|
||||
db.UserRepository(),
|
||||
map[string]auth.Authenticator{
|
||||
"password": pwdAuth,
|
||||
},
|
||||
)
|
||||
userService := user.NewService(db.UserRepository())
|
||||
storageService := storage.NewService(store)
|
||||
providerService := provider.NewService(db.ProviderRepository(), db.UserRepository(), llmEngine)
|
||||
agentService := agent.NewService(db.AgentRepository(), db.UserRepository())
|
||||
defer Close()
|
||||
|
||||
// http server
|
||||
router := http.NewRouter(http.Dependencies{
|
||||
StorageService: storageService,
|
||||
AuthService: authService,
|
||||
UserService: userService,
|
||||
TokenManager: tokenManager,
|
||||
ProviderService: providerService,
|
||||
AgentService: agentService,
|
||||
httpRouter := http.NewRouter(http.Dependencies{
|
||||
StorageService: GetStorageService(),
|
||||
AuthService: GetAuthService(),
|
||||
UserService: GetUserService(),
|
||||
ProviderService: GetProviderService(),
|
||||
AgentService: GetAgentService(),
|
||||
ChatService: GetChatService(),
|
||||
ToolService: GetToolService(),
|
||||
TaskService: GetTaskService(),
|
||||
TokenManager: GetTokenManager(),
|
||||
})
|
||||
|
||||
llms := llmserver.NewLLMServer(llmserver.Dependencies{})
|
||||
gatewayRouter := gateway.NewRouter(gateway.Dependencies{})
|
||||
|
||||
// task scheduler : replanifie les tâches persistées
|
||||
GetTaskService().Start()
|
||||
defer GetTaskService().Stop()
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
// start LLM service
|
||||
// start Brain router
|
||||
go func() {
|
||||
if err := llms.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) {
|
||||
if err := gatewayRouter.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) {
|
||||
slog.Error("failed to start llm server", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// start HTTP service
|
||||
// start HTTP router
|
||||
go func() {
|
||||
if err := router.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) {
|
||||
if err := httpRouter.Start(); err != nil && !errors.Is(err, nethttp.ErrServerClosed) {
|
||||
slog.Error("failed to start http server", "error", err)
|
||||
}
|
||||
}()
|
||||
@@ -115,7 +79,7 @@ func runServe() {
|
||||
// stop HTTP service
|
||||
ctx, cancel1 := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel1()
|
||||
if err := router.Stop(ctx); err != nil {
|
||||
if err := httpRouter.Stop(ctx); err != nil {
|
||||
slog.Error("failed to stop http server", "error", err)
|
||||
}
|
||||
slog.Info("HTTP server stopped gracefully")
|
||||
@@ -123,7 +87,7 @@ func runServe() {
|
||||
// stop LLM service
|
||||
ctx, cancel2 := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel2()
|
||||
if err := llms.Stop(ctx); err != nil {
|
||||
if err := gatewayRouter.Stop(ctx); err != nil {
|
||||
slog.Error("failed to stop llm server", "error", err)
|
||||
}
|
||||
slog.Info("LLM server stopped gracefully")
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
// providerMetadata est le document de découverte OpenID
|
||||
// (.well-known/openid-configuration).
|
||||
type providerMetadata struct {
|
||||
Issuer string `json:"issuer"`
|
||||
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||
TokenEndpoint string `json:"token_endpoint"`
|
||||
JwksURI string `json:"jwks_uri"`
|
||||
}
|
||||
|
||||
type metadataCacheEntry struct {
|
||||
metadata *providerMetadata
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
// discovery maintient un cache des documents de découverte par fournisseur.
|
||||
type discovery struct {
|
||||
httpClient *http.Client
|
||||
mu sync.Mutex
|
||||
entries map[string]*metadataCacheEntry
|
||||
}
|
||||
|
||||
func newDiscovery() *discovery {
|
||||
return &discovery{
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
entries: make(map[string]*metadataCacheEntry),
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata retourne les endpoints du fournisseur, avec mise en cache.
|
||||
func (d *discovery) Metadata(provider *domain.OIDCProvider) (*providerMetadata, error) {
|
||||
d.mu.Lock()
|
||||
entry, ok := d.entries[provider.ID]
|
||||
if ok && time.Now().Before(entry.expiresAt) {
|
||||
d.mu.Unlock()
|
||||
return entry.metadata, nil
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
metadata, err := d.fetch(provider.WellknownURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching provider discovery document: %s", err)
|
||||
}
|
||||
if metadata.Issuer == "" || metadata.AuthorizationEndpoint == "" || metadata.TokenEndpoint == "" || metadata.JwksURI == "" {
|
||||
return nil, fmt.Errorf("incomplete discovery document for provider %s", provider.ID)
|
||||
}
|
||||
|
||||
d.mu.Lock()
|
||||
d.entries[provider.ID] = &metadataCacheEntry{
|
||||
metadata: metadata,
|
||||
expiresAt: time.Now().Add(1 * time.Hour),
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func (d *discovery) fetch(wellknownURL string) (*providerMetadata, error) {
|
||||
res, err := d.httpClient.Get(wellknownURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status %d", res.StatusCode)
|
||||
}
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metadata := &providerMetadata{}
|
||||
if err := json.Unmarshal(body, metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
// jwk est une clé du document JWKS du fournisseur.
|
||||
type jwk struct {
|
||||
Kid string `json:"kid"`
|
||||
Kty string `json:"kty"`
|
||||
Alg string `json:"alg"`
|
||||
Use string `json:"use"`
|
||||
N string `json:"n"`
|
||||
E string `json:"e"`
|
||||
Crv string `json:"crv"`
|
||||
X string `json:"x"`
|
||||
Y string `json:"y"`
|
||||
}
|
||||
|
||||
type jwksDocument struct {
|
||||
Keys []jwk `json:"keys"`
|
||||
}
|
||||
|
||||
type jwksCacheEntry struct {
|
||||
keys map[string]interface{}
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
// jwksStore maintient un cache des clés publiques de signature par fournisseur.
|
||||
type jwksStore struct {
|
||||
httpClient *http.Client
|
||||
mu sync.Mutex
|
||||
entries map[string]*jwksCacheEntry
|
||||
}
|
||||
|
||||
func newJwksStore() *jwksStore {
|
||||
return &jwksStore{
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
entries: make(map[string]*jwksCacheEntry),
|
||||
}
|
||||
}
|
||||
|
||||
// KeyFunc retourne une fonction de résolution de clé pour golang-jwt,
|
||||
// valable pour un fournisseur donné.
|
||||
func (s *jwksStore) KeyFunc(providerID string, jwksURI string) jwt.Keyfunc {
|
||||
return func(t *jwt.Token) (interface{}, error) {
|
||||
kid, ok := t.Header["kid"].(string)
|
||||
if !ok || kid == "" {
|
||||
return nil, fmt.Errorf("id token: kid manquant")
|
||||
}
|
||||
key, err := s.key(providerID, jwksURI, kid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
|
||||
// key retourne la clé publique correspondant au kid, avec mise en cache.
|
||||
// Si le kid est inconnu, le cache est invalidé et le JWKS re-téléchargé
|
||||
// (rotation de clés côté fournisseur).
|
||||
func (s *jwksStore) key(providerID string, jwksURI string, kid string) (interface{}, error) {
|
||||
fetch := func() (map[string]interface{}, error) {
|
||||
keys, err := s.fetch(jwksURI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching provider JWKS: %s", err)
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.entries[providerID] = &jwksCacheEntry{
|
||||
keys: keys,
|
||||
expiresAt: time.Now().Add(1 * time.Hour),
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
entry, ok := s.entries[providerID]
|
||||
valid := ok && time.Now().Before(entry.expiresAt)
|
||||
s.mu.Unlock()
|
||||
|
||||
if valid {
|
||||
if key, ok := entry.keys[kid]; ok {
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
|
||||
keys, err := fetch()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, ok := keys[kid]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("id token: aucune clé JWKS ne correspond au kid %s", kid)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (s *jwksStore) fetch(jwksURI string) (map[string]interface{}, error) {
|
||||
res, err := s.httpClient.Get(jwksURI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected status %d", res.StatusCode)
|
||||
}
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
document := &jwksDocument{}
|
||||
if err := json.Unmarshal(body, document); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keys := make(map[string]interface{})
|
||||
for _, k := range document.Keys {
|
||||
if k.Use != "" && k.Use != "sig" {
|
||||
continue
|
||||
}
|
||||
switch k.Kty {
|
||||
case "RSA":
|
||||
key, err := rsaKey(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keys[k.Kid] = key
|
||||
case "EC":
|
||||
key, err := ecKey(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keys[k.Kid] = key
|
||||
}
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func rsaKey(k jwk) (*rsa.PublicKey, error) {
|
||||
if k.N == "" || k.E == "" {
|
||||
return nil, fmt.Errorf("clé RSA JWKS incomplète (kid %s)", k.Kid)
|
||||
}
|
||||
nBytes, err := base64.RawURLEncoding.DecodeString(k.N)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("module RSA invalide: %s", err)
|
||||
}
|
||||
eBytes, err := base64.RawURLEncoding.DecodeString(k.E)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("exposant RSA invalide: %s", err)
|
||||
}
|
||||
return &rsa.PublicKey{
|
||||
N: new(big.Int).SetBytes(nBytes),
|
||||
E: int(new(big.Int).SetBytes(eBytes).Int64()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ecKey(k jwk) (*ecdsa.PublicKey, error) {
|
||||
if k.X == "" || k.Y == "" {
|
||||
return nil, fmt.Errorf("clé EC JWKS incomplète (kid %s)", k.Kid)
|
||||
}
|
||||
var curve elliptic.Curve
|
||||
switch strings.ToUpper(k.Crv) {
|
||||
case "P-256":
|
||||
curve = elliptic.P256()
|
||||
case "P-384":
|
||||
curve = elliptic.P384()
|
||||
case "P-521":
|
||||
curve = elliptic.P521()
|
||||
default:
|
||||
return nil, fmt.Errorf("courbe EC non supportée: %s", k.Crv)
|
||||
}
|
||||
xBytes, err := base64.RawURLEncoding.DecodeString(k.X)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coordonnée x invalide: %s", err)
|
||||
}
|
||||
yBytes, err := base64.RawURLEncoding.DecodeString(k.Y)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coordonnée y invalide: %s", err)
|
||||
}
|
||||
return &ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: new(big.Int).SetBytes(xBytes),
|
||||
Y: new(big.Int).SetBytes(yBytes),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRegistrationUnsupported = errors.New("registration via OIDC is not supported: users are provisioned at login")
|
||||
ErrNoEmail = errors.New("id token does not contain an email claim")
|
||||
ErrUserDisabled = errors.New("user is disabled")
|
||||
)
|
||||
|
||||
// tokenResponse est la réponse de l'endpoint token du fournisseur.
|
||||
type tokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
IDToken string `json:"id_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
// idTokenClaims sont les claims du token ID émis par le fournisseur.
|
||||
type idTokenClaims struct {
|
||||
Email string `json:"email"`
|
||||
GivenName string `json:"given_name"`
|
||||
FamilyName string `json:"family_name"`
|
||||
Name string `json:"name"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Picture string `json:"picture"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
type OIDCAuthenticator struct {
|
||||
tokenManager auth.TokenManager
|
||||
oidcRepository domain.OIDCProviderRepository
|
||||
userRepository domain.UserRepository
|
||||
discovery *discovery
|
||||
jwks *jwksStore
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func NewOIDCAuthenticator(
|
||||
tokenManager auth.TokenManager,
|
||||
oidcRepository domain.OIDCProviderRepository,
|
||||
userRepository domain.UserRepository,
|
||||
) *OIDCAuthenticator {
|
||||
return &OIDCAuthenticator{
|
||||
tokenManager: tokenManager,
|
||||
oidcRepository: oidcRepository,
|
||||
userRepository: userRepository,
|
||||
discovery: newDiscovery(),
|
||||
jwks: newJwksStore(),
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// AuthorizeURL construit l'URL d'autorisation vers laquelle rediriger l'utilisateur.
|
||||
func (s *OIDCAuthenticator) AuthorizeURL(ctx context.Context, provider *domain.OIDCProvider, redirectURI string, state string) (string, error) {
|
||||
metadata, err := s.discovery.Metadata(provider)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("client_id", provider.ClientID)
|
||||
params.Set("redirect_uri", redirectURI)
|
||||
params.Set("response_type", "code")
|
||||
params.Set("scope", "openid profile email")
|
||||
params.Set("state", state)
|
||||
|
||||
return metadata.AuthorizationEndpoint + "?" + params.Encode(), nil
|
||||
}
|
||||
|
||||
// ExchangeCode échange le code d'autorisation contre un token ID, le valide,
|
||||
// puis délivre une session applicative.
|
||||
func (s *OIDCAuthenticator) ExchangeCode(ctx context.Context, provider *domain.OIDCProvider, code string, redirectURI string) (*auth.Session, error) {
|
||||
metadata, err := s.discovery.Metadata(provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokens, err := s.requestTokens(metadata.TokenEndpoint, provider, code, redirectURI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokens.IDToken == "" {
|
||||
return nil, fmt.Errorf("provider %s did not return an id token", provider.ID)
|
||||
}
|
||||
|
||||
claims, err := s.validateIDToken(provider, metadata, tokens.IDToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if claims.Email == "" {
|
||||
return nil, ErrNoEmail
|
||||
}
|
||||
|
||||
user, err := s.provisionUser(claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessToken, expiresAt, err := s.tokenManager.GenerateAccessToken(user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate access token: %s", err)
|
||||
}
|
||||
refreshToken, _, err := s.tokenManager.GenerateRefreshToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate refresh token: %s", err)
|
||||
}
|
||||
|
||||
return &auth.Session{
|
||||
User: user,
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresAt: expiresAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Authenticate implémente auth.Authenticator: le flux OIDC passe par le state
|
||||
// signé qui transporte l'id du fournisseur, puis l'échange du code.
|
||||
func (s *OIDCAuthenticator) Authenticate(ctx context.Context, creds auth.Credentials) (*auth.Session, error) {
|
||||
providerID, err := s.tokenManager.ParseStateToken(creds.OIDCState)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid oidc state: %s", err)
|
||||
}
|
||||
provider, err := s.oidcRepository.FindOIDCProvider(providerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unknown oidc provider: %s", providerID)
|
||||
}
|
||||
return s.ExchangeCode(ctx, provider, creds.OIDCCode, creds.RedirectURI)
|
||||
}
|
||||
|
||||
func (s *OIDCAuthenticator) Register(ctx context.Context, registration auth.Registration) (*auth.Session, error) {
|
||||
return nil, ErrRegistrationUnsupported
|
||||
}
|
||||
|
||||
func (s *OIDCAuthenticator) requestTokens(tokenEndpoint string, provider *domain.OIDCProvider, code string, redirectURI string) (*tokenResponse, error) {
|
||||
form := url.Values{}
|
||||
form.Set("grant_type", "authorization_code")
|
||||
form.Set("code", code)
|
||||
form.Set("redirect_uri", redirectURI)
|
||||
form.Set("client_id", provider.ClientID)
|
||||
form.Set("client_secret", provider.ClientSecret)
|
||||
|
||||
res, err := s.httpClient.Post(tokenEndpoint, "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error calling token endpoint: %s", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("token endpoint returned status %d: %s", res.StatusCode, string(body))
|
||||
}
|
||||
|
||||
tokens := &tokenResponse{}
|
||||
if err := json.Unmarshal(body, tokens); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func (s *OIDCAuthenticator) validateIDToken(provider *domain.OIDCProvider, metadata *providerMetadata, idToken string) (*idTokenClaims, error) {
|
||||
claims := &idTokenClaims{}
|
||||
_, err := jwt.ParseWithClaims(idToken, claims, s.jwks.KeyFunc(provider.ID, metadata.JwksURI),
|
||||
jwt.WithValidMethods([]string{"RS256", "RS384", "RS512", "ES256", "ES384", "ES521"}),
|
||||
jwt.WithIssuer(metadata.Issuer),
|
||||
jwt.WithAudience(provider.ClientID),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid id token: %s", err)
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (s *OIDCAuthenticator) provisionUser(claims *idTokenClaims) (*domain.User, error) {
|
||||
user, err := s.userRepository.FindUserByEmail(claims.Email)
|
||||
if err == nil {
|
||||
if !user.Enabled {
|
||||
return nil, ErrUserDisabled
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
firstname := claims.GivenName
|
||||
if firstname == "" {
|
||||
firstname = claims.PreferredUsername
|
||||
}
|
||||
user, err = s.userRepository.CreateUser(&domain.User{
|
||||
Email: claims.Email,
|
||||
Firstname: firstname,
|
||||
Lastname: claims.FamilyName,
|
||||
Enabled: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating oidc user %s: %s", claims.Email, err)
|
||||
}
|
||||
|
||||
_, err = s.userRepository.CreateTeam(user.ID, &domain.Team{
|
||||
Label: "Espace personnel",
|
||||
DateCreated: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating personal team for oidc user %s: %s", claims.Email, err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/security"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type mockOIDCRepo struct {
|
||||
domain.OIDCProviderRepository
|
||||
provider *domain.OIDCProvider
|
||||
}
|
||||
|
||||
func (m *mockOIDCRepo) FindOIDCProvider(id string) (*domain.OIDCProvider, error) {
|
||||
return m.provider, nil
|
||||
}
|
||||
|
||||
type mockUserRepo struct {
|
||||
domain.UserRepository
|
||||
created *domain.User
|
||||
}
|
||||
|
||||
func (m *mockUserRepo) FindUserByEmail(email string) (*domain.User, error) {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (m *mockUserRepo) CreateUser(user *domain.User) (*domain.User, error) {
|
||||
user.ID = "user-1"
|
||||
m.created = user
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (m *mockUserRepo) CreateTeam(userid string, team *domain.Team) (*domain.Team, error) {
|
||||
return team, nil
|
||||
}
|
||||
|
||||
// fakeIdP simule un fournisseur OpenID Connect (Keycloak-like):
|
||||
// document de découverte, JWKS et endpoint token.
|
||||
type fakeIdP struct {
|
||||
server *httptest.Server
|
||||
key *rsa.PrivateKey
|
||||
kid string
|
||||
idToken string
|
||||
provider *domain.OIDCProvider
|
||||
redirectURI string
|
||||
tokenCalls int
|
||||
}
|
||||
|
||||
func newFakeIdP(t *testing.T) *fakeIdP {
|
||||
t.Helper()
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
idp := &fakeIdP{key: key, kid: "test-key-1", redirectURI: "http://localhost:3000/api/auth/oidc/callback"}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
idp.server = server
|
||||
|
||||
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"issuer": server.URL,
|
||||
"authorization_endpoint": server.URL + "/authorize",
|
||||
"token_endpoint": server.URL + "/token",
|
||||
"jwks_uri": server.URL + "/jwks",
|
||||
})
|
||||
})
|
||||
|
||||
mux.HandleFunc("/jwks", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string][]map[string]string{
|
||||
"keys": {{
|
||||
"kid": idp.kid,
|
||||
"kty": "RSA",
|
||||
"alg": "RS256",
|
||||
"use": "sig",
|
||||
"n": base64.RawURLEncoding.EncodeToString(key.PublicKey.N.Bytes()),
|
||||
"e": base64.RawURLEncoding.EncodeToString(big.NewInt(int64(key.PublicKey.E)).Bytes()),
|
||||
}},
|
||||
})
|
||||
})
|
||||
|
||||
mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
||||
idp.tokenCalls++
|
||||
if err := r.ParseForm(); err != nil {
|
||||
t.Errorf("invalid token request form: %s", err)
|
||||
}
|
||||
if r.Form.Get("grant_type") != "authorization_code" {
|
||||
t.Errorf("unexpected grant_type: %s", r.Form.Get("grant_type"))
|
||||
}
|
||||
if r.Form.Get("redirect_uri") != idp.redirectURI {
|
||||
t.Errorf("unexpected redirect_uri: %s", r.Form.Get("redirect_uri"))
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"access_token": "opaque-access-token",
|
||||
"id_token": idp.idToken,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 300,
|
||||
})
|
||||
})
|
||||
|
||||
idp.provider = &domain.OIDCProvider{
|
||||
ID: "kc",
|
||||
Label: "Keycloak",
|
||||
ClientID: "lassistanoque",
|
||||
ClientSecret: "secret",
|
||||
WellknownURL: server.URL + "/.well-known/openid-configuration",
|
||||
}
|
||||
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *fakeIdP) signIDToken(t *testing.T, claims jwt.MapClaims) string {
|
||||
t.Helper()
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
token.Header["kid"] = idp.kid
|
||||
signed, err := token.SignedString(idp.key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return signed
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Setenv("LASSISTANOQUE_JWT_SECRET", "test-secret")
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func newAuthenticator(idp *fakeIdP) *OIDCAuthenticator {
|
||||
tokenManager := security.NewJwtTokenManager(time.Hour, 24*time.Hour, "lassistanoque")
|
||||
return NewOIDCAuthenticator(tokenManager, &mockOIDCRepo{provider: idp.provider}, &mockUserRepo{})
|
||||
}
|
||||
|
||||
func TestAuthorizeURL(t *testing.T) {
|
||||
idp := newFakeIdP(t)
|
||||
defer idp.server.Close()
|
||||
|
||||
authenticator := newAuthenticator(idp)
|
||||
state, err := authenticator.tokenManager.GenerateStateToken(idp.provider.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
url, err := authenticator.AuthorizeURL(context.Background(), idp.provider, idp.redirectURI, state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(url, idp.server.URL+"/authorize?") {
|
||||
t.Errorf("unexpected authorize url: %s", url)
|
||||
}
|
||||
for _, expected := range []string{
|
||||
"client_id=lassistanoque",
|
||||
"response_type=code",
|
||||
"scope=openid+profile+email",
|
||||
"state=",
|
||||
} {
|
||||
if !strings.Contains(url, expected) {
|
||||
t.Errorf("authorize url misses %s: %s", expected, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeCodeCreatesUserAndSession(t *testing.T) {
|
||||
idp := newFakeIdP(t)
|
||||
defer idp.server.Close()
|
||||
|
||||
idp.idToken = idp.signIDToken(t, jwt.MapClaims{
|
||||
"iss": idp.server.URL,
|
||||
"aud": idp.provider.ClientID,
|
||||
"exp": time.Now().Add(5 * time.Minute).Unix(),
|
||||
"email": "fabien@example.com",
|
||||
"given_name": "Fabien",
|
||||
"family_name": "Dupont",
|
||||
})
|
||||
|
||||
userRepo := &mockUserRepo{}
|
||||
tokenManager := security.NewJwtTokenManager(time.Hour, 24*time.Hour, "lassistanoque")
|
||||
authenticator := NewOIDCAuthenticator(tokenManager, &mockOIDCRepo{provider: idp.provider}, userRepo)
|
||||
|
||||
session, err := authenticator.ExchangeCode(context.Background(), idp.provider, "auth-code", idp.redirectURI)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if idp.tokenCalls != 1 {
|
||||
t.Errorf("expected 1 token endpoint call, got %d", idp.tokenCalls)
|
||||
}
|
||||
if userRepo.created == nil {
|
||||
t.Fatal("user was not provisioned")
|
||||
}
|
||||
if userRepo.created.Email != "fabien@example.com" ||
|
||||
userRepo.created.Firstname != "Fabien" ||
|
||||
userRepo.created.Lastname != "Dupont" {
|
||||
t.Errorf("provisioned user mismatch: %+v", userRepo.created)
|
||||
}
|
||||
if !userRepo.created.Enabled {
|
||||
t.Error("provisioned user should be enabled")
|
||||
}
|
||||
if session.AccessToken == "" || session.RefreshToken == "" {
|
||||
t.Error("session tokens are empty")
|
||||
}
|
||||
if session.User == nil || session.User.Email != "fabien@example.com" {
|
||||
t.Errorf("session user mismatch: %+v", session.User)
|
||||
}
|
||||
|
||||
// le token applicatif doit être valide
|
||||
user, err := tokenManager.ParseAndValidate(session.AccessToken)
|
||||
if err != nil {
|
||||
t.Fatalf("generated access token invalid: %s", err)
|
||||
}
|
||||
if user.ID != "user-1" {
|
||||
t.Errorf("unexpected user id in access token: %s", user.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeCodeRejectsForgedIDToken(t *testing.T) {
|
||||
idp := newFakeIdP(t)
|
||||
defer idp.server.Close()
|
||||
|
||||
// token signé par une autre clé que celle du JWKS
|
||||
forgedKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
|
||||
"iss": idp.server.URL,
|
||||
"aud": idp.provider.ClientID,
|
||||
"exp": time.Now().Add(5 * time.Minute).Unix(),
|
||||
"email": "attacker@example.com",
|
||||
})
|
||||
token.Header["kid"] = idp.kid
|
||||
idp.idToken, err = token.SignedString(forgedKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
authenticator := newAuthenticator(idp)
|
||||
if _, err := authenticator.ExchangeCode(context.Background(), idp.provider, "auth-code", idp.redirectURI); err == nil {
|
||||
t.Fatal("exchange should fail with a forged id token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeCodeRejectsWrongIssuer(t *testing.T) {
|
||||
idp := newFakeIdP(t)
|
||||
defer idp.server.Close()
|
||||
|
||||
idp.idToken = idp.signIDToken(t, jwt.MapClaims{
|
||||
"iss": "https://evil.example.com",
|
||||
"aud": idp.provider.ClientID,
|
||||
"exp": time.Now().Add(5 * time.Minute).Unix(),
|
||||
"email": "attacker@example.com",
|
||||
})
|
||||
|
||||
authenticator := newAuthenticator(idp)
|
||||
if _, err := authenticator.ExchangeCode(context.Background(), idp.provider, "auth-code", idp.redirectURI); err == nil {
|
||||
t.Fatal("exchange should fail with a wrong issuer")
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,13 @@ import (
|
||||
type Database interface {
|
||||
SettingsRepository() domain.SettingsRepository
|
||||
UserRepository() domain.UserRepository
|
||||
OIDCProviderRepository() domain.OIDCProviderRepository
|
||||
FileRepository() domain.FileRepository
|
||||
ProviderRepository() domain.ProviderRepository
|
||||
AgentRepository() domain.AgentRepository
|
||||
ChatRepository() domain.ChatRepository
|
||||
ToolRepository() domain.ToolRepository
|
||||
TaskRepository() domain.TaskRepository
|
||||
Migrate() error
|
||||
Close()
|
||||
}
|
||||
|
||||
@@ -41,12 +41,14 @@ func NewTable[T any](db *sql.DB, dialect Dialect, tablename string) (Table[T], e
|
||||
|
||||
fields := make([]dbfield, 0)
|
||||
for field := range t.Fields() {
|
||||
f := dbfield{
|
||||
name: field.Name,
|
||||
stype: field.Type.String(),
|
||||
dbname: field.Tag.Get("db"),
|
||||
if field.Tag.Get("db") != "-" {
|
||||
f := dbfield{
|
||||
name: field.Name,
|
||||
stype: field.Type.String(),
|
||||
dbname: field.Tag.Get("db"),
|
||||
}
|
||||
fields = append(fields, f)
|
||||
}
|
||||
fields = append(fields, f)
|
||||
}
|
||||
|
||||
return Table[T]{
|
||||
@@ -187,7 +189,8 @@ func (t Table[T]) Select(options ...Option) ([]*T, error) {
|
||||
}
|
||||
if q.pagesize != 0 {
|
||||
query += fmt.Sprintf(" limit %d", q.pagesize)
|
||||
if q.pagenum != 0 {
|
||||
if q.pagenum > 0 {
|
||||
// pages en base 0 : la page 0 est la première page
|
||||
query += fmt.Sprintf(" offset %d", q.pagesize*q.pagenum)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,24 +10,24 @@ import (
|
||||
|
||||
type TursoAgentRepository struct {
|
||||
db *sql.DB
|
||||
AgentTable orm.Table[domain.Agent]
|
||||
agentTable orm.Table[domain.Agent]
|
||||
}
|
||||
|
||||
func NewTursoAgentRepository(db *sql.DB) *TursoAgentRepository {
|
||||
dialect := orm.NewSqliteDialect()
|
||||
AgentTable, err := orm.NewTable[domain.Agent](db, dialect, "agents")
|
||||
agentTable, err := orm.NewTable[domain.Agent](db, dialect, "agents")
|
||||
if err != nil {
|
||||
log.Fatalf("error creating lasebuche agent table")
|
||||
}
|
||||
|
||||
return &TursoAgentRepository{
|
||||
db: db,
|
||||
AgentTable: AgentTable,
|
||||
agentTable: agentTable,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TursoAgentRepository) ListAgents(userID string, teamID string) ([]*domain.Agent, error) {
|
||||
return r.AgentTable.Select(
|
||||
return r.agentTable.Select(
|
||||
orm.WithWhere(
|
||||
"team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)",
|
||||
teamID,
|
||||
@@ -37,7 +37,7 @@ func (r *TursoAgentRepository) ListAgents(userID string, teamID string) ([]*doma
|
||||
}
|
||||
|
||||
func (r *TursoAgentRepository) GetAgent(userID string, teamID string, id string) (*domain.Agent, error) {
|
||||
return r.AgentTable.SelectOne(
|
||||
return r.agentTable.SelectOne(
|
||||
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
@@ -46,15 +46,15 @@ func (r *TursoAgentRepository) GetAgent(userID string, teamID string, id string)
|
||||
}
|
||||
|
||||
func (r *TursoAgentRepository) CreateAgent(userID string, agent *domain.Agent) (*domain.Agent, error) {
|
||||
return r.AgentTable.Insert(agent)
|
||||
return r.agentTable.Insert(agent)
|
||||
}
|
||||
|
||||
func (r *TursoAgentRepository) UpdateAgent(userID string, agent *domain.Agent) (*domain.Agent, error) {
|
||||
return r.AgentTable.Update(agent)
|
||||
return r.agentTable.Update(agent)
|
||||
}
|
||||
|
||||
func (r *TursoAgentRepository) DeleteAgent(userID string, teamID string, id string) error {
|
||||
return r.AgentTable.DeleteWhere(
|
||||
return r.agentTable.DeleteWhere(
|
||||
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package turso
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/database/orm"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type TursoChatRepository struct {
|
||||
db *sql.DB
|
||||
chatTable orm.Table[domain.Chat]
|
||||
messageTable orm.Table[domain.Message]
|
||||
}
|
||||
|
||||
func NewTursoChatRepository(db *sql.DB) *TursoChatRepository {
|
||||
dialect := orm.NewSqliteDialect()
|
||||
chatTable, err := orm.NewTable[domain.Chat](db, dialect, "chats")
|
||||
messageTable, err := orm.NewTable[domain.Message](db, dialect, "chat_messages")
|
||||
if err != nil {
|
||||
log.Fatalf("error creating lasebuche agent table")
|
||||
}
|
||||
|
||||
return &TursoChatRepository{
|
||||
db: db,
|
||||
chatTable: chatTable,
|
||||
messageTable: messageTable,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TursoChatRepository) ListChats(userID string, teamID string, page int, query string, taskID string) ([]*domain.Chat, error) {
|
||||
where := "team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)"
|
||||
args := []any{teamID, userID}
|
||||
if query != "" {
|
||||
// % et _ sont des jokers LIKE : on les neutralise
|
||||
escaped := strings.ReplaceAll(query, `\`, `\\`)
|
||||
escaped = strings.ReplaceAll(escaped, `%`, `\%`)
|
||||
escaped = strings.ReplaceAll(escaped, `_`, `\_`)
|
||||
where += " and title like $3 escape '\\'"
|
||||
args = append(args, "%"+escaped+"%")
|
||||
}
|
||||
if taskID != "" {
|
||||
where += fmt.Sprintf(" and task_id=$%d", len(args)+1)
|
||||
args = append(args, taskID)
|
||||
}
|
||||
return r.chatTable.Select(
|
||||
orm.WithWhere(where, args...),
|
||||
orm.WithOrder("_date_created desc"),
|
||||
orm.WithPagination(page, 20),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoChatRepository) ListChatsWithUnfreshTitles(teamID string) ([]*domain.Chat, error) {
|
||||
return r.chatTable.Select(
|
||||
orm.WithWhere(
|
||||
"team_id=$1 and fresh_title=0",
|
||||
teamID,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoChatRepository) GetChat(userID string, teamID string, id string) (*domain.Chat, error) {
|
||||
return r.chatTable.SelectOne(
|
||||
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoChatRepository) CreateChat(userID string, chat *domain.Chat) (*domain.Chat, error) {
|
||||
return r.chatTable.Insert(chat)
|
||||
}
|
||||
|
||||
func (r *TursoChatRepository) UpdateChat(userID string, chat *domain.Chat) (*domain.Chat, error) {
|
||||
return r.chatTable.Update(chat)
|
||||
}
|
||||
|
||||
func (r *TursoChatRepository) DeleteChat(userID string, teamID string, id string) error {
|
||||
// les messages n'ont pas de clé étrangère cascade : suppression explicite
|
||||
err := r.messageTable.DeleteWhere(
|
||||
"chat_id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.chatTable.DeleteWhere(
|
||||
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoChatRepository) GetChatMessages(userID string, teamID string, chatID string, withToolCallResponses bool) ([]*domain.Message, error) {
|
||||
var messages []*domain.Message
|
||||
var err error
|
||||
if !withToolCallResponses {
|
||||
messages, err = r.messageTable.Select(
|
||||
orm.WithWhere(
|
||||
`chat_id=$1
|
||||
and team_id=$2
|
||||
and team_id in (select team_id from user_teams where user_id=$3)
|
||||
and (tool_call_id='' or tool_call_id is null)`,
|
||||
chatID,
|
||||
teamID,
|
||||
userID,
|
||||
),
|
||||
orm.WithOrder("_date_created asc"),
|
||||
)
|
||||
} else {
|
||||
messages, err = r.messageTable.Select(
|
||||
orm.WithWhere(
|
||||
"chat_id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
chatID,
|
||||
teamID,
|
||||
userID,
|
||||
),
|
||||
orm.WithOrder("_date_created asc"),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range messages {
|
||||
m.Json2toolCall()
|
||||
}
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func (r *TursoChatRepository) CreateChatMessage(userID string, message *domain.Message) (*domain.Message, error) {
|
||||
message.ToolCall2Json()
|
||||
return r.messageTable.Insert(message)
|
||||
}
|
||||
@@ -24,7 +24,7 @@ type TursoDB struct {
|
||||
func NewTursoDB(cfg *config.Config) (*TursoDB, error) {
|
||||
db, err := sql.Open("turso", cfg.DatabaseURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error connecting to db %: %s", cfg.DatabaseURL, err)
|
||||
return nil, fmt.Errorf("error connecting to db %s: %s", cfg.DatabaseURL, err)
|
||||
}
|
||||
return &TursoDB{
|
||||
DB: db,
|
||||
@@ -64,6 +64,10 @@ func (db *TursoDB) SettingsRepository() domain.SettingsRepository {
|
||||
return NewTursoSettingsRepository(db.DB)
|
||||
}
|
||||
|
||||
func (db *TursoDB) OIDCProviderRepository() domain.OIDCProviderRepository {
|
||||
return NewTursoOIDCProviderRepository(db.DB)
|
||||
}
|
||||
|
||||
func (db *TursoDB) FileRepository() domain.FileRepository {
|
||||
return NewTursoFileRepository(db.DB)
|
||||
}
|
||||
@@ -75,3 +79,15 @@ func (db *TursoDB) ProviderRepository() domain.ProviderRepository {
|
||||
func (db *TursoDB) AgentRepository() domain.AgentRepository {
|
||||
return NewTursoAgentRepository(db.DB)
|
||||
}
|
||||
|
||||
func (db *TursoDB) ChatRepository() domain.ChatRepository {
|
||||
return NewTursoChatRepository(db.DB)
|
||||
}
|
||||
|
||||
func (db *TursoDB) ToolRepository() domain.ToolRepository {
|
||||
return NewTursoToolRepository(db.DB)
|
||||
}
|
||||
|
||||
func (db *TursoDB) TaskRepository() domain.TaskRepository {
|
||||
return NewTursoTaskRepository(db.DB)
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package turso
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
func TestListChatsFilter(t *testing.T) {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
schema := `
|
||||
create table chats (
|
||||
id text not null primary key, team_id text not null, start_datetime numeric,
|
||||
end_datetime numeric, agent_id text, user_id text, task_id text, title text,
|
||||
fresh_title numeric, _date_created numeric not null, _date_updated numeric,
|
||||
_version text not null);
|
||||
create table user_teams (user_id text, team_id text);
|
||||
insert into chats (id, team_id, user_id, agent_id, task_id, title, fresh_title, _date_created, _version)
|
||||
values ('c1','t1','u1','','','Problème de timeout',0,'2026-01-01 10:00:00','v'),
|
||||
('c2','t1','u1','','task-9','Discussion navigateur',0,'2026-01-02 10:00:00','v'),
|
||||
('c3','t1','u1','','','Recette de cuisine 100%',0,'2026-01-03 10:00:00','v');
|
||||
insert into user_teams (user_id, team_id) values ('u1','t1');
|
||||
insert into chats (id, team_id, user_id, agent_id, task_id, title, fresh_title, _date_created, _version)
|
||||
select 'bulk-'||(n+1), 't1','u1','','','Conversation '||(n+1), 0,
|
||||
datetime('2026-01-01','+'||(n+10)||' minutes'), 'v'
|
||||
from (with recursive cnt(n) as (select 0 union all select n+1 from cnt where n < 24) select n from cnt);
|
||||
`
|
||||
if _, err := db.Exec(schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
repo := NewTursoChatRepository(db)
|
||||
|
||||
// sans filtre, page 0 : 20 conversations (28 en base)
|
||||
p0, err := repo.ListChats("u1", "t1", 0, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(p0) != 20 {
|
||||
t.Fatalf("page 0: expected 20, got %d", len(p0))
|
||||
}
|
||||
p1, err := repo.ListChats("u1", "t1", 1, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(p1) != 8 {
|
||||
t.Fatalf("page 1: expected 8, got %d", len(p1))
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, c := range append(append([]*domain.Chat{}, p0...), p1...) {
|
||||
if seen[c.ID] {
|
||||
t.Fatalf("chat %s appears twice across pages", c.ID)
|
||||
}
|
||||
seen[c.ID] = true
|
||||
}
|
||||
p2, err := repo.ListChats("u1", "t1", 2, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(p2) != 0 {
|
||||
t.Fatalf("page 2: expected 0, got %d", len(p2))
|
||||
}
|
||||
|
||||
// filtre simple
|
||||
res, err := repo.ListChats("u1", "t1", 0, "timeout", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(res) != 1 || res[0].ID != "c1" {
|
||||
t.Fatalf("filter 'timeout': %+v", res)
|
||||
}
|
||||
|
||||
// jokers LIKE neutralisés : '100%' ne doit rien matcher de spécial
|
||||
res, err = repo.ListChats("u1", "t1", 0, "100%", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(res) != 1 || res[0].ID != "c3" {
|
||||
t.Fatalf("filter '100%%': %+v", res)
|
||||
}
|
||||
|
||||
// pas de résultat
|
||||
res, err = repo.ListChats("u1", "t1", 0, "inexistant", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(res) != 0 {
|
||||
t.Fatalf("expected 0, got %d", len(res))
|
||||
}
|
||||
|
||||
// filtre par tâche
|
||||
taskRes, err := repo.ListChats("u1", "t1", 0, "", "task-9")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(taskRes) != 1 || taskRes[0].ID != "c2" {
|
||||
t.Fatalf("filter task-9: expected [c2], got %v", taskRes)
|
||||
}
|
||||
|
||||
_ = domain.Chat{}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ drop table user_teams;
|
||||
drop table channels;
|
||||
drop table tools;
|
||||
drop table tasks;
|
||||
drop table history;
|
||||
drop table chats;
|
||||
drop table chat_contents;
|
||||
drop table files;
|
||||
drop table agents;
|
||||
@@ -95,9 +95,8 @@ create table tools (
|
||||
id text not null primary key,
|
||||
team_id text not null,
|
||||
name text not null,
|
||||
type text not null,
|
||||
enabled numeric not null default 1,
|
||||
configuration text not null default '{}',
|
||||
configuration text not null default '',
|
||||
_date_created numeric not null default current_timestamp,
|
||||
_date_updated numeric,
|
||||
_version text not null
|
||||
@@ -117,14 +116,31 @@ create table tasks (
|
||||
_version text not null
|
||||
);
|
||||
|
||||
create table history (
|
||||
create table chats (
|
||||
id text not null primary key,
|
||||
task_id text not null,
|
||||
start_datetime numeric,
|
||||
team_id text not null,
|
||||
start_datetime numeric not null,
|
||||
end_datetime numeric,
|
||||
prompt text not null,
|
||||
log text not null,
|
||||
response text not null,
|
||||
agent_id text,
|
||||
user_id text,
|
||||
task_id text,
|
||||
title text,
|
||||
fresh_title numeric default 0,
|
||||
_date_created numeric not null default current_timestamp,
|
||||
_date_updated numeric,
|
||||
_version text not null
|
||||
);
|
||||
|
||||
create table chat_messages (
|
||||
id text not null primary key,
|
||||
team_id text not null,
|
||||
chat_id text not null,
|
||||
content_datetime numeric,
|
||||
role text not null,
|
||||
content text,
|
||||
agent_id text,
|
||||
tool_call_id text,
|
||||
tool_calls text,
|
||||
_date_created numeric not null default current_timestamp,
|
||||
_date_updated numeric,
|
||||
_version text not null
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
alter table tasks drop column repeat;
|
||||
@@ -0,0 +1 @@
|
||||
alter table tasks add column repeat integer not null default 1;
|
||||
+1
@@ -0,0 +1 @@
|
||||
alter table tasks drop column last_datetime;
|
||||
+1
@@ -0,0 +1 @@
|
||||
alter table tasks add column last_datetime numeric;
|
||||
@@ -0,0 +1,34 @@
|
||||
package turso
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/database/orm"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type TursoOIDCProviderRepository struct {
|
||||
db *sql.DB
|
||||
OIDCTable orm.Table[domain.OIDCProvider]
|
||||
}
|
||||
|
||||
func NewTursoOIDCProviderRepository(db *sql.DB) *TursoOIDCProviderRepository {
|
||||
dialect := orm.NewSqliteDialect()
|
||||
oidcTable, err := orm.NewTable[domain.OIDCProvider](db, dialect, "oidc")
|
||||
if err != nil {
|
||||
log.Fatalf("error creating oidc provider table")
|
||||
}
|
||||
return &TursoOIDCProviderRepository{
|
||||
db: db,
|
||||
OIDCTable: oidcTable,
|
||||
}
|
||||
}
|
||||
|
||||
func (or *TursoOIDCProviderRepository) ListOIDCProviders() ([]*domain.OIDCProvider, error) {
|
||||
return or.OIDCTable.Select(orm.WithOrder("label asc"))
|
||||
}
|
||||
|
||||
func (or *TursoOIDCProviderRepository) FindOIDCProvider(id string) (*domain.OIDCProvider, error) {
|
||||
return or.OIDCTable.Get(id)
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package turso
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/database/orm"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type TursoTaskRepository struct {
|
||||
db *sql.DB
|
||||
taskTable orm.Table[domain.Task]
|
||||
}
|
||||
|
||||
func NewTursoTaskRepository(db *sql.DB) *TursoTaskRepository {
|
||||
dialect := orm.NewSqliteDialect()
|
||||
taskTable, err := orm.NewTable[domain.Task](db, dialect, "tasks")
|
||||
if err != nil {
|
||||
log.Fatalf("error creating task table")
|
||||
}
|
||||
|
||||
return &TursoTaskRepository{
|
||||
db: db,
|
||||
taskTable: taskTable,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TursoTaskRepository) ListTasks(userID string, teamID string) ([]*domain.Task, error) {
|
||||
return r.taskTable.Select(
|
||||
orm.WithWhere(
|
||||
"team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)",
|
||||
teamID,
|
||||
userID,
|
||||
),
|
||||
orm.WithOrder("_date_created desc"),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoTaskRepository) ListActiveTasks() ([]*domain.Task, error) {
|
||||
return r.taskTable.Select(
|
||||
orm.WithWhere("status=$1", domain.TaskStatusActive),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoTaskRepository) GetTaskByID(userID string, teamID string, id string) (*domain.Task, error) {
|
||||
return r.taskTable.SelectOne(
|
||||
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoTaskRepository) CreateTask(task *domain.Task) (*domain.Task, error) {
|
||||
return r.taskTable.Insert(task)
|
||||
}
|
||||
|
||||
func (r *TursoTaskRepository) UpdateTask(task *domain.Task) (*domain.Task, error) {
|
||||
return r.taskTable.Update(task)
|
||||
}
|
||||
|
||||
func (r *TursoTaskRepository) DeleteTask(userID string, teamID string, id string) error {
|
||||
return r.taskTable.DeleteWhere(
|
||||
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
|
||||
// FirstUserOfTeam retourne le premier utilisateur d'une équipe. Les tâches
|
||||
// planifiées ne portent pas d'utilisateur : c'est lui qui porte la conversation
|
||||
// générée lors de l'exécution.
|
||||
func (r *TursoTaskRepository) FirstUserOfTeam(teamID string) (string, error) {
|
||||
var userID string
|
||||
err := r.db.QueryRow(
|
||||
"select user_id from user_teams where team_id=$1 order by _date_created asc limit 1",
|
||||
teamID,
|
||||
).Scan(&userID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package turso
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/adapter/database/orm"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type TursoToolRepository struct {
|
||||
db *sql.DB
|
||||
ToolTable orm.Table[domain.Tool]
|
||||
}
|
||||
|
||||
func NewTursoToolRepository(db *sql.DB) *TursoToolRepository {
|
||||
dialect := orm.NewSqliteDialect()
|
||||
ToolTable, err := orm.NewTable[domain.Tool](db, dialect, "tools")
|
||||
if err != nil {
|
||||
log.Fatalf("error creating lasebuche Tool table")
|
||||
}
|
||||
|
||||
return &TursoToolRepository{
|
||||
db: db,
|
||||
ToolTable: ToolTable,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) ListTools(userID string, teamID string) ([]*domain.Tool, error) {
|
||||
return r.ToolTable.Select(
|
||||
orm.WithWhere(
|
||||
"team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)",
|
||||
teamID,
|
||||
userID,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) GetToolByName(userID string, teamID string, name string) (*domain.Tool, error) {
|
||||
return r.ToolTable.SelectOne(
|
||||
"name=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
name,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) GetToolByID(userID string, teamID string, id string) (*domain.Tool, error) {
|
||||
return r.ToolTable.SelectOne(
|
||||
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
id,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) CreateTool(userID string, Tool *domain.Tool) (*domain.Tool, error) {
|
||||
return r.ToolTable.Insert(Tool)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) UpdateTool(userID string, Tool *domain.Tool) (*domain.Tool, error) {
|
||||
return r.ToolTable.Update(Tool)
|
||||
}
|
||||
|
||||
func (r *TursoToolRepository) DeleteTool(userID string, teamID string, name string) error {
|
||||
return r.ToolTable.DeleteWhere(
|
||||
"name=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
|
||||
name,
|
||||
teamID,
|
||||
userID,
|
||||
)
|
||||
}
|
||||
@@ -2,9 +2,12 @@ package llm
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
anyllm "github.com/mozilla-ai/any-llm-go"
|
||||
"github.com/mozilla-ai/any-llm-go/providers"
|
||||
"github.com/mozilla-ai/any-llm-go/providers/anthropic"
|
||||
"github.com/mozilla-ai/any-llm-go/providers/ollama"
|
||||
"github.com/mozilla-ai/any-llm-go/providers/openai"
|
||||
@@ -79,13 +82,16 @@ func (e *AnyLLMEngine) Stream(
|
||||
ctx context.Context,
|
||||
provider *domain.Provider,
|
||||
modelID string,
|
||||
params *domain.StreamParams,
|
||||
params *domain.LLMParams,
|
||||
messages []*domain.Message,
|
||||
) error {
|
||||
) *domain.Message {
|
||||
|
||||
p, err := providerFactory(provider)
|
||||
if err != nil {
|
||||
return err
|
||||
return &domain.Message{
|
||||
Role: string(domain.RoleAssistant),
|
||||
Content: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
anyllmMessages := make([]anyllm.Message, 0)
|
||||
@@ -96,26 +102,152 @@ func (e *AnyLLMEngine) Stream(
|
||||
})
|
||||
}
|
||||
|
||||
if params.ReasoningEffort == "" {
|
||||
params.ReasoningEffort = "none"
|
||||
}
|
||||
|
||||
tools := make([]providers.Tool, len(params.Tools))
|
||||
for i, t := range params.Tools {
|
||||
tools[i] = toolConvert(t)
|
||||
}
|
||||
|
||||
toolCalling := false
|
||||
toolCalls := make([]domain.ToolCall, 0)
|
||||
|
||||
chunkChan, errChan := p.CompletionStream(ctx, anyllm.CompletionParams{
|
||||
Model: modelID,
|
||||
Messages: anyllmMessages,
|
||||
Stream: true,
|
||||
Model: modelID,
|
||||
Messages: anyllmMessages,
|
||||
Stream: true,
|
||||
ReasoningEffort: providers.ReasoningEffort(params.ReasoningEffort),
|
||||
Tools: tools,
|
||||
})
|
||||
|
||||
fullContent := ""
|
||||
|
||||
for chunk := range chunkChan {
|
||||
if len(chunk.Choices) > 0 {
|
||||
|
||||
content := chunk.Choices[0].Delta.Content
|
||||
reasoning := chunk.Choices[0].Delta.Reasoning
|
||||
if params.OnChunk != nil {
|
||||
params.OnChunk(&domain.Chunk{
|
||||
Content: content,
|
||||
})
|
||||
if content != "" {
|
||||
fullContent += content
|
||||
params.OnChunk(&domain.Chunk{
|
||||
Done: false,
|
||||
Role: anyllm.RoleAssistant,
|
||||
Content: content,
|
||||
Reasoning: false,
|
||||
})
|
||||
} else if reasoning != nil {
|
||||
params.OnChunk(&domain.Chunk{
|
||||
Done: false,
|
||||
Role: anyllm.RoleAssistant,
|
||||
Content: reasoning.Content,
|
||||
Reasoning: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range chunk.Choices[0].Delta.ToolCalls {
|
||||
toolCalling = true
|
||||
slog.Info("Model is calling tool", "tool", tc.Function.Name, "arguments", tc.Function.Arguments)
|
||||
|
||||
extra := make(map[string]map[string]any)
|
||||
for k, v := range tc.Extra {
|
||||
m := map[string]any(v)
|
||||
extra[k] = m
|
||||
}
|
||||
|
||||
toolCalls = append(toolCalls, domain.ToolCall{
|
||||
ID: tc.ID,
|
||||
Type: tc.Type,
|
||||
Function: domain.ToolCallFunction{
|
||||
Name: tc.Function.Name,
|
||||
Arguments: tc.Function.Arguments,
|
||||
},
|
||||
Extra: extra,
|
||||
})
|
||||
|
||||
if params.OnChunk != nil {
|
||||
params.OnChunk(&domain.Chunk{
|
||||
Done: false,
|
||||
ToolName: tc.Function.Name,
|
||||
Role: anyllm.RoleAssistant,
|
||||
Content: tc.Function.Arguments,
|
||||
Reasoning: false,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if !toolCalling {
|
||||
if params.OnChunk != nil {
|
||||
params.OnChunk(&domain.Chunk{
|
||||
Done: true,
|
||||
Role: anyllm.RoleAssistant,
|
||||
Content: "",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := <-errChan; err != nil {
|
||||
return err
|
||||
return &domain.Message{
|
||||
Role: string(domain.RoleAssistant),
|
||||
Content: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return &domain.Message{
|
||||
Role: string(domain.RoleAssistant),
|
||||
Content: fullContent,
|
||||
ToolCalls: toolCalls,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *AnyLLMEngine) Generate(
|
||||
ctx context.Context,
|
||||
provider *domain.Provider,
|
||||
modelID string,
|
||||
messages []*domain.Message,
|
||||
) (string, error) {
|
||||
|
||||
p, err := providerFactory(provider)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
anyllmMessages := make([]anyllm.Message, 0)
|
||||
for _, m := range messages {
|
||||
anyllmMessages = append(anyllmMessages, anyllm.Message{
|
||||
Role: m.Role,
|
||||
Content: m.Content,
|
||||
})
|
||||
}
|
||||
|
||||
response, err := p.Completion(ctx, anyllm.CompletionParams{
|
||||
Model: modelID,
|
||||
Messages: anyllmMessages,
|
||||
Stream: false,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.Choices[0].Message.Content.(string), nil
|
||||
|
||||
}
|
||||
|
||||
func toolConvert(domainTool *domain.ToolDefinition) providers.Tool {
|
||||
return providers.Tool{
|
||||
Type: domainTool.Type,
|
||||
Function: providers.Function{
|
||||
Name: domainTool.Function.Name,
|
||||
Description: domainTool.Function.Description,
|
||||
Parameters: domainTool.Function.Parameters,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,38 @@ func (tm *JwtTokenManager) GenerateRefreshToken(userID string) (string, time.Tim
|
||||
return signed, expiresAt, err
|
||||
}
|
||||
|
||||
// GenerateStateToken crée un JWT court (10 min) utilisé comme paramètre state
|
||||
// du flux OIDC, porteur d'une donnée arbitraire (ex: l'id du fournisseur).
|
||||
func (tm *JwtTokenManager) GenerateStateToken(data string) (string, error) {
|
||||
claims := jwt.RegisteredClaims{
|
||||
Subject: data,
|
||||
Issuer: tm.issuer,
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(10 * time.Minute)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(tm.secret)
|
||||
}
|
||||
|
||||
// ParseStateToken vérifie un state généré par GenerateStateToken et retourne
|
||||
// la donnée qu'il transporte.
|
||||
func (tm *JwtTokenManager) ParseStateToken(state string) (string, error) {
|
||||
claims := &jwt.RegisteredClaims{}
|
||||
token, err := jwt.ParseWithClaims(state, claims, func(t *jwt.Token) (interface{}, error) {
|
||||
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, errors.New("méthode de signature inattendue")
|
||||
}
|
||||
return tm.secret, nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !token.Valid {
|
||||
return "", errors.New("token invalide")
|
||||
}
|
||||
return claims.Subject, nil
|
||||
}
|
||||
|
||||
// ParseAndValidate décode et vérifie la signature + l'expiration d'un JWT.
|
||||
func (tm *JwtTokenManager) ParseAndValidate(tokenString string) (*domain.User, error) {
|
||||
claims := &Claims{}
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
package browser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/launcher"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
"github.com/go-rod/stealth"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type BrowserConfig struct {
|
||||
// Headless est un pointeur pour distinguer "non renseigné" (défaut : true) de "explicitement false".
|
||||
Headless *bool `yaml:"headless"`
|
||||
TimeoutSeconds int `yaml:"timeoutSeconds"`
|
||||
}
|
||||
|
||||
// BrowserTool maintains a persistent browser session across LLM tool calls.
|
||||
type BrowserTool struct{}
|
||||
|
||||
var (
|
||||
browserInstance *rod.Browser
|
||||
currentPage *rod.Page
|
||||
browserMu sync.Mutex
|
||||
launchedHeadless bool
|
||||
)
|
||||
|
||||
func (t *BrowserTool) Name() string {
|
||||
return "browser"
|
||||
}
|
||||
|
||||
func (t *BrowserTool) Description() string {
|
||||
return "Control a web browser to navigate pages and interact with content. " +
|
||||
"Maintains session state across calls (cookies, current page). " +
|
||||
"Actions: 'navigate' (go to URL), 'click' (click element by CSS selector), " +
|
||||
"'fill' (type into an input field), 'content' (get current page text), " +
|
||||
"'links' (list all links on current page)."
|
||||
}
|
||||
|
||||
func (t *BrowserTool) Params() []domain.ToolParam {
|
||||
return []domain.ToolParam{
|
||||
{JsonName: "headless", Type: "bool", Mandatory: true, Description: "Headless"},
|
||||
{JsonName: "timeoutSeconds", Type: "int", Mandatory: true, Description: "Timeout en secondes"},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *BrowserTool) Definition(ctx context.Context) *domain.ToolDefinition {
|
||||
return &domain.ToolDefinition{
|
||||
Type: "function",
|
||||
Function: &domain.ToolFunction{
|
||||
Name: t.Name(),
|
||||
Description: t.Description(),
|
||||
Parameters: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"action": map[string]any{
|
||||
"type": "string",
|
||||
"enum": []string{"navigate", "click", "fill", "content", "links"},
|
||||
"description": "The browser action to perform.",
|
||||
},
|
||||
"url": map[string]any{
|
||||
"type": "string",
|
||||
"description": "URL to navigate to. Required for action='navigate'.",
|
||||
},
|
||||
"selector": map[string]any{
|
||||
"type": "string",
|
||||
"description": "CSS selector of the element to interact with. Required for 'click' and 'fill'.",
|
||||
},
|
||||
"value": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Text value to type into the field. Required for action='fill'.",
|
||||
},
|
||||
},
|
||||
"required": []string{"action"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *BrowserTool) Execute(_ context.Context, input []byte, configuration string) ([]byte, error) {
|
||||
|
||||
slog.Debug("[browser]", "input", string(input))
|
||||
|
||||
var params struct {
|
||||
Action string `json:"action"`
|
||||
URL string `json:"url"`
|
||||
Selector string `json:"selector"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
if err := json.Unmarshal(input, ¶ms); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
var toolConfig BrowserConfig
|
||||
err := json.Unmarshal([]byte(configuration), &toolConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
timeout := time.Duration(toolConfig.TimeoutSeconds) * time.Second
|
||||
|
||||
// Headless est nil quand la configuration ne le précise pas : défaut à true.
|
||||
headless := true
|
||||
if toolConfig.Headless != nil {
|
||||
headless = *toolConfig.Headless
|
||||
}
|
||||
|
||||
if timeout <= 0 {
|
||||
timeout = 60 * time.Second
|
||||
}
|
||||
|
||||
browserMu.Lock()
|
||||
defer browserMu.Unlock()
|
||||
|
||||
if err := ensureBrowser(headless); err != nil {
|
||||
return nil, fmt.Errorf("browser init failed: %w", err)
|
||||
}
|
||||
|
||||
switch params.Action {
|
||||
case "navigate":
|
||||
if params.URL == "" {
|
||||
return nil, fmt.Errorf("'url' is required for action 'navigate'")
|
||||
}
|
||||
return t.navigate(params.URL, timeout)
|
||||
|
||||
case "click":
|
||||
if params.Selector == "" {
|
||||
return nil, fmt.Errorf("'selector' is required for action 'click'")
|
||||
}
|
||||
return t.click(params.Selector, timeout)
|
||||
|
||||
case "fill":
|
||||
if params.Selector == "" || params.Value == "" {
|
||||
return nil, fmt.Errorf("'selector' and 'value' are required for action 'fill'")
|
||||
}
|
||||
return t.fill(params.Selector, params.Value, timeout)
|
||||
|
||||
case "content":
|
||||
return t.pageContent(timeout)
|
||||
|
||||
case "links":
|
||||
return t.pageLinks(timeout)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown action %q", params.Action)
|
||||
}
|
||||
}
|
||||
|
||||
func browserProfileDir() string {
|
||||
if dir, err := os.UserCacheDir(); err == nil {
|
||||
return filepath.Join(dir, "lassistanoque", "browser-profile")
|
||||
}
|
||||
return filepath.Join(os.TempDir(), "lassistanoque-browser-profile")
|
||||
}
|
||||
|
||||
// ensureBrowser lance le navigateur au premier appel et le relance si le mode
|
||||
// headless a changé dans la configuration.
|
||||
func ensureBrowser(headless bool) error {
|
||||
if browserInstance != nil {
|
||||
if headless == launchedHeadless {
|
||||
return nil
|
||||
}
|
||||
_ = browserInstance.Close()
|
||||
browserInstance = nil
|
||||
currentPage = nil
|
||||
}
|
||||
if err := initBrowser(headless); err != nil {
|
||||
return err
|
||||
}
|
||||
launchedHeadless = headless
|
||||
return nil
|
||||
}
|
||||
|
||||
func initBrowser(headless bool) error {
|
||||
path, _ := launcher.LookPath()
|
||||
controlURL, err := launcher.New().
|
||||
Bin(path).
|
||||
Headless(headless).
|
||||
UserDataDir(browserProfileDir()).
|
||||
Delete("enable-automation").
|
||||
Launch()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to launch browser: %w", err)
|
||||
}
|
||||
b := rod.New().ControlURL(controlURL)
|
||||
if err := b.Connect(); err != nil {
|
||||
return fmt.Errorf("failed to connect to browser: %w", err)
|
||||
}
|
||||
browserInstance = b
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensurePage(timeout time.Duration) error {
|
||||
if currentPage == nil {
|
||||
page, err := stealth.Page(browserInstance.Timeout(timeout))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open page: %w", err)
|
||||
}
|
||||
currentPage = page
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BrowserTool) navigate(rawURL string, timeout time.Duration) ([]byte, error) {
|
||||
if err := ensurePage(timeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := currentPage.Timeout(timeout).Navigate(rawURL); err != nil {
|
||||
return nil, fmt.Errorf("navigation failed: %w", err)
|
||||
}
|
||||
if err := currentPage.Timeout(timeout).WaitLoad(); err != nil {
|
||||
return nil, fmt.Errorf("page load failed: %w", err)
|
||||
}
|
||||
return t.pageContent(timeout)
|
||||
}
|
||||
|
||||
func (t *BrowserTool) click(selector string, timeout time.Duration) ([]byte, error) {
|
||||
if err := ensurePage(timeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
el, err := currentPage.Timeout(timeout).Element(selector)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("element %q not found: %w", selector, err)
|
||||
}
|
||||
if err := el.Click(proto.InputMouseButtonLeft, 1); err != nil {
|
||||
return nil, fmt.Errorf("click failed: %w", err)
|
||||
}
|
||||
if err := currentPage.WaitIdle(timeout); err != nil {
|
||||
return nil, fmt.Errorf("wait after click failed: %w", err)
|
||||
}
|
||||
return t.pageContent(timeout)
|
||||
}
|
||||
|
||||
func (t *BrowserTool) fill(selector, value string, timeout time.Duration) ([]byte, error) {
|
||||
if err := ensurePage(timeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
el, err := currentPage.Timeout(timeout).Element(selector)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("element %q not found: %w", selector, err)
|
||||
}
|
||||
if err := el.SelectAllText(); err != nil {
|
||||
return nil, fmt.Errorf("failed to select text: %w", err)
|
||||
}
|
||||
if err := el.Input(value); err != nil {
|
||||
return nil, fmt.Errorf("fill failed: %w", err)
|
||||
}
|
||||
return t.pageContent(timeout)
|
||||
}
|
||||
|
||||
func (t *BrowserTool) pageContent(timeout time.Duration) ([]byte, error) {
|
||||
if currentPage == nil {
|
||||
return []byte("No page loaded."), nil
|
||||
}
|
||||
|
||||
info, err := currentPage.Info()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get page info: %w", err)
|
||||
}
|
||||
|
||||
titleRes, err := currentPage.Eval("() => document.title")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get page title: %w", err)
|
||||
}
|
||||
title := titleRes.Value.String()
|
||||
|
||||
body, err := currentPage.Timeout(timeout).Element("body")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get page body: %w", err)
|
||||
}
|
||||
text, err := body.Text()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract page text: %w", err)
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "Page: %s\nURL: %s\n\n%s", title, info.URL, text)
|
||||
return []byte(sb.String()), nil
|
||||
}
|
||||
|
||||
func (t *BrowserTool) pageLinks(timeout time.Duration) ([]byte, error) {
|
||||
if currentPage == nil {
|
||||
return []byte("No page loaded."), nil
|
||||
}
|
||||
|
||||
elements, err := currentPage.Timeout(timeout).Elements("a[href]")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get links: %w", err)
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "Links on current page (%d):\n\n", len(elements))
|
||||
for i, el := range elements {
|
||||
href, _ := el.Attribute("href")
|
||||
text, _ := el.Text()
|
||||
if href == nil {
|
||||
continue
|
||||
}
|
||||
label := strings.TrimSpace(text)
|
||||
if label == "" {
|
||||
label = "(no text)"
|
||||
}
|
||||
fmt.Fprintf(&sb, "%d. %s — %s\n", i+1, label, *href)
|
||||
}
|
||||
return []byte(sb.String()), nil
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/task"
|
||||
)
|
||||
|
||||
// SchedulerTool permet à l'agent de planifier des tâches pour l'utilisateur :
|
||||
// exécution d'un prompt selon une spécification cron, avec ou sans répétition.
|
||||
type SchedulerTool struct {
|
||||
taskService *task.Service
|
||||
}
|
||||
|
||||
func New(taskService *task.Service) *SchedulerTool {
|
||||
return &SchedulerTool{taskService: taskService}
|
||||
}
|
||||
|
||||
func (t *SchedulerTool) Name() string {
|
||||
return "scheduler"
|
||||
}
|
||||
|
||||
func (t *SchedulerTool) Description() string {
|
||||
return "Schedule tasks that run automatically at specified times, with or without repetition. " +
|
||||
"Use this tool whenever the user asks to do something later or on a recurring basis " +
|
||||
"(e.g. 'every day at 8am', 'every Monday at 9am', 'tomorrow at 6pm', 'every hour'). " +
|
||||
"Actions: 'create' (schedule a new task), 'list' (list scheduled tasks), " +
|
||||
"'update' (modify a task's title, prompt, cron or repetition), " +
|
||||
"'delete' (delete a task by its id)."
|
||||
}
|
||||
|
||||
func (t *SchedulerTool) Params() []domain.ToolParam {
|
||||
return []domain.ToolParam{}
|
||||
}
|
||||
|
||||
func (t *SchedulerTool) Definition(ctx context.Context) *domain.ToolDefinition {
|
||||
return &domain.ToolDefinition{
|
||||
Type: "function",
|
||||
Function: &domain.ToolFunction{
|
||||
Name: t.Name(),
|
||||
Description: t.Description(),
|
||||
Parameters: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"action": map[string]any{
|
||||
"type": "string",
|
||||
"enum": []string{"create", "list", "update", "delete"},
|
||||
"description": "The scheduling action to perform.",
|
||||
},
|
||||
"title": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Short task title, e.g. 'Météo du matin'. Required for 'create'.",
|
||||
},
|
||||
"prompt": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Self-contained prompt to execute at the scheduled time. " +
|
||||
"It will run in a fresh conversation with no memory of this one: " +
|
||||
"include all needed context. Required for 'create'.",
|
||||
},
|
||||
"cron": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Cron spec, 5 fields 'minute hour day-of-month month day-of-week' " +
|
||||
"(e.g. '0 8 * * *' every day at 08:00, '0 9 * * 1' every Monday at 09:00, " +
|
||||
"'30 18 1 * *' on the 1st at 18:30) or a descriptor like '@daily', '@hourly', '@weekly'. " +
|
||||
"Required for 'create'.",
|
||||
},
|
||||
"repeat": map[string]any{
|
||||
"type": "boolean",
|
||||
"description": "true: the task repeats on schedule. " +
|
||||
"false: it runs only once, at the first time matching the cron spec, then is marked done. " +
|
||||
"Default true. Use false for one-shot requests like 'tomorrow at 8am'.",
|
||||
},
|
||||
"id": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Task id, as returned by 'list'. Required for 'update' and 'delete'.",
|
||||
},
|
||||
},
|
||||
"required": []string{"action"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SchedulerTool) Execute(ctx context.Context, input []byte, configuration string) ([]byte, error) {
|
||||
var params struct {
|
||||
Action string `json:"action"`
|
||||
Title string `json:"title"`
|
||||
Prompt string `json:"prompt"`
|
||||
Cron string `json:"cron"`
|
||||
Repeat *bool `json:"repeat"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
if err := json.Unmarshal(input, ¶ms); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
userID, ok := domain.UserIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("scheduler tool: missing user context")
|
||||
}
|
||||
teamID, ok := domain.TeamIDFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("scheduler tool: missing team context")
|
||||
}
|
||||
modelID, _ := domain.ModelIDFromContext(ctx)
|
||||
|
||||
switch params.Action {
|
||||
case "create":
|
||||
repeat := true
|
||||
if params.Repeat != nil {
|
||||
repeat = *params.Repeat
|
||||
}
|
||||
taskModel, err := t.taskService.CreateTask(userID, teamID, &domain.Task{
|
||||
ModelID: modelID,
|
||||
Label: params.Title,
|
||||
Prompt: params.Prompt,
|
||||
Cron: params.Cron,
|
||||
Repeat: repeat,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := json.Marshal(taskModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case "list":
|
||||
tasks, err := t.taskService.ListTasks(userID, teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := json.Marshal(tasks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case "update":
|
||||
if params.ID == "" {
|
||||
return nil, fmt.Errorf("'id' is required for action 'update'")
|
||||
}
|
||||
current, err := t.taskService.GetTask(userID, teamID, params.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// mise à jour partielle : seuls les champs fournis changent
|
||||
if params.Title != "" {
|
||||
current.Label = params.Title
|
||||
}
|
||||
if params.Prompt != "" {
|
||||
current.Prompt = params.Prompt
|
||||
}
|
||||
if params.Cron != "" {
|
||||
current.Cron = params.Cron
|
||||
}
|
||||
if params.Repeat != nil {
|
||||
current.Repeat = *params.Repeat
|
||||
}
|
||||
updated, err := t.taskService.UpdateTask(userID, teamID, current)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := json.Marshal(updated)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case "delete":
|
||||
if params.ID == "" {
|
||||
return nil, fmt.Errorf("'id' is required for action 'delete'")
|
||||
}
|
||||
if err := t.taskService.DeleteTask(userID, teamID, params.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte("task deleted"), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown action %q", params.Action)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package searxng
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type SearxngConfig struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type SearxngTool struct {
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Name() string {
|
||||
return "searxng"
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Description() string {
|
||||
return "Search the web using a SearXNG instance. Returns a list of relevant results with title, URL, and snippet."
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Params() []domain.ToolParam {
|
||||
return []domain.ToolParam{
|
||||
{JsonName: "url", Type: "string", Mandatory: true, Description: "Searxng URL"},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Definition(ctx context.Context) *domain.ToolDefinition {
|
||||
return &domain.ToolDefinition{
|
||||
Type: "function",
|
||||
Function: &domain.ToolFunction{
|
||||
Name: t.Name(),
|
||||
Description: t.Description(),
|
||||
Parameters: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"query": map[string]any{
|
||||
"type": "string",
|
||||
"description": "The search query",
|
||||
},
|
||||
"categories": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Comma-separated list of categories: general, news, images, science, it, social_media. Defaults to 'general'.",
|
||||
},
|
||||
"language": map[string]any{
|
||||
"type": "string",
|
||||
"description": "Language code for results, e.g. 'fr', 'en'. Defaults to 'all'.",
|
||||
},
|
||||
},
|
||||
"required": []string{"query"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type searxngResponse struct {
|
||||
Results []searxngResult `json:"results"`
|
||||
}
|
||||
|
||||
type searxngResult struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Content string `json:"content"`
|
||||
Engine string `json:"engine"`
|
||||
}
|
||||
|
||||
func (t *SearxngTool) Execute(ctx context.Context, input []byte, configuration string) ([]byte, error) {
|
||||
|
||||
slog.Debug("[searxng]", "input", string(input))
|
||||
|
||||
var params struct {
|
||||
Query string `json:"query"`
|
||||
Categories string `json:"categories"`
|
||||
Language string `json:"language"`
|
||||
}
|
||||
if err := json.Unmarshal(input, ¶ms); err != nil {
|
||||
return nil, fmt.Errorf("invalid parameters: %w", err)
|
||||
}
|
||||
|
||||
var toolCfg SearxngConfig
|
||||
err := json.Unmarshal([]byte(configuration), &toolCfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("searxng tool not configured: %w", err)
|
||||
}
|
||||
if toolCfg.URL == "" {
|
||||
return nil, fmt.Errorf("searxng tool config missing 'url' field")
|
||||
}
|
||||
baseURL := toolCfg.URL
|
||||
|
||||
categories := "general"
|
||||
if params.Categories != "" {
|
||||
categories = params.Categories
|
||||
}
|
||||
language := "all"
|
||||
if params.Language != "" {
|
||||
language = params.Language
|
||||
}
|
||||
|
||||
reqURL := fmt.Sprintf("%s/search?q=%s&format=json&categories=%s&language=%s",
|
||||
strings.TrimRight(baseURL, "/"),
|
||||
url.QueryEscape(params.Query),
|
||||
url.QueryEscape(categories),
|
||||
url.QueryEscape(language),
|
||||
)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build request: %w", err)
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("searxng request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("searxng returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var searchResp searxngResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&searchResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
|
||||
if len(searchResp.Results) == 0 {
|
||||
return []byte("No results found."), nil
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "Search results for \"%s\":\n\n", params.Query)
|
||||
for i, r := range searchResp.Results {
|
||||
fmt.Fprintf(&sb, "%d. %s\n", i+1, r.Title)
|
||||
fmt.Fprintf(&sb, " URL: %s\n", r.URL)
|
||||
if r.Content != "" {
|
||||
fmt.Fprintf(&sb, " %s\n", r.Content)
|
||||
}
|
||||
fmt.Fprintf(&sb, "\n")
|
||||
}
|
||||
|
||||
return []byte(sb.String()), nil
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package weather
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
)
|
||||
|
||||
type WeatherTool struct {
|
||||
}
|
||||
|
||||
type WeatherConfig struct {
|
||||
}
|
||||
|
||||
func (t *WeatherTool) Name() string {
|
||||
return "weather"
|
||||
}
|
||||
|
||||
func (t *WeatherTool) Description() string {
|
||||
return "Get the current weather for a given location"
|
||||
}
|
||||
|
||||
func (t *WeatherTool) Params() []domain.ToolParam {
|
||||
return []domain.ToolParam{}
|
||||
}
|
||||
|
||||
func (t *WeatherTool) Definition(ctx context.Context) *domain.ToolDefinition {
|
||||
return &domain.ToolDefinition{
|
||||
Type: "function",
|
||||
Function: &domain.ToolFunction{
|
||||
Name: t.Name(),
|
||||
Description: t.Description(),
|
||||
Parameters: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"location": map[string]any{
|
||||
"type": "string",
|
||||
"description": "The city name, e.g., 'Paris' or 'New York'",
|
||||
},
|
||||
},
|
||||
"required": []string{"location"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *WeatherTool) Execute(ctx context.Context, input []byte, configuration string) ([]byte, error) {
|
||||
|
||||
var params struct {
|
||||
Location string `json:"location"`
|
||||
}
|
||||
err := json.Unmarshal(input, ¶ms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.Get("https://geocoding-api.open-meteo.com/v1/search?name=" + url.QueryEscape(params.Location))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var location struct {
|
||||
Results []struct {
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
} `json:"results"`
|
||||
}
|
||||
err = json.NewDecoder(resp.Body).Decode(&location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(location.Results) == 0 {
|
||||
return nil, fmt.Errorf("location not found : %s", input)
|
||||
}
|
||||
|
||||
lon := location.Results[0].Longitude
|
||||
lat := location.Results[0].Latitude
|
||||
|
||||
weatherResp, err := http.Get(fmt.Sprintf("https://api.open-meteo.com/v1/forecast?latitude=%f&longitude=%f¤t_weather=true&daily=weather_code,temperature_2m_max,temperature_2m_min,rain_sum,wind_speed_10m_max&hourly=temperature_2m&forecast_days=15", lat, lon))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer weatherResp.Body.Close()
|
||||
|
||||
var weatherData struct {
|
||||
CurrentWeatherUnits struct {
|
||||
Temperature string `json:"temperature"`
|
||||
WindSpeed string `json:"windspeed"`
|
||||
} `json:"current_weather_units"`
|
||||
CurrentWeather struct {
|
||||
Time string `json:"time"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
WeatherCode int `json:"weathercode"`
|
||||
} `json:"current_weather"`
|
||||
DailyUnits struct {
|
||||
TemperatureMax string `json:"temperature_2m_max"`
|
||||
TemperatureMin string `json:"temperature_2m_min"`
|
||||
Rain string `json:"rain_sum"`
|
||||
WindSpeed string `json:"wind_speed_10m_max"`
|
||||
} `json:"daily_units"`
|
||||
Daily struct {
|
||||
Time []string `json:"time"`
|
||||
TemperatureMax []float64 `json:"temperature_2m_max"`
|
||||
TemperatureMin []float64 `json:"temperature_2m_min"`
|
||||
Rain []float64 `json:"rain_sum"`
|
||||
WindSpeed []float64 `json:"wind_speed_10m_max"`
|
||||
} `json:"daily"`
|
||||
}
|
||||
|
||||
err = json.NewDecoder(weatherResp.Body).Decode(&weatherData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
|
||||
fmt.Fprintf(&builder, "Current weather in %s\n", params.Location)
|
||||
fmt.Fprintf(&builder, "- Time: %s\n", weatherData.CurrentWeather.Time)
|
||||
fmt.Fprintf(&builder, "- Temperature: %.2f %s\n", weatherData.CurrentWeather.Temperature, weatherData.CurrentWeatherUnits.Temperature)
|
||||
fmt.Fprintf(&builder, "- Weather code: %d - %s\n", weatherData.CurrentWeather.WeatherCode, wmoCodeToDescription(weatherData.CurrentWeather.WeatherCode))
|
||||
if len(weatherData.Daily.WindSpeed) > 0 {
|
||||
fmt.Fprintf(&builder, "- Wind speed: %.2f %s\n", weatherData.Daily.WindSpeed[0], weatherData.DailyUnits.WindSpeed)
|
||||
}
|
||||
if len(weatherData.Daily.TemperatureMax) > 0 {
|
||||
fmt.Fprintf(&builder, "- Max temperature: %.2f %s\n", weatherData.Daily.TemperatureMax[0], weatherData.DailyUnits.TemperatureMax)
|
||||
}
|
||||
if len(weatherData.Daily.TemperatureMin) > 0 {
|
||||
fmt.Fprintf(&builder, "- Min temperature: %.2f %s\n", weatherData.Daily.TemperatureMin[0], weatherData.DailyUnits.TemperatureMin)
|
||||
}
|
||||
if len(weatherData.Daily.Rain) > 0 {
|
||||
fmt.Fprintf(&builder, "- Rainfall: %.2f %s\n", weatherData.Daily.Rain[0], weatherData.DailyUnits.Rain)
|
||||
}
|
||||
|
||||
fmt.Fprintf(&builder, "\nForecast for the next days:\n")
|
||||
|
||||
// Iterate over the daily data and append to the repor
|
||||
for i := 0; i < len(weatherData.Daily.Time); i++ {
|
||||
fmt.Fprintf(&builder, "%s: Max Temp: %.2f %s, Min Temp: %.2f %s, Rain: %.2f %s, Wind Speed: %.2f %s\n",
|
||||
weatherData.Daily.Time[i],
|
||||
weatherData.Daily.TemperatureMax[i], weatherData.DailyUnits.TemperatureMax,
|
||||
weatherData.Daily.TemperatureMin[i], weatherData.DailyUnits.TemperatureMin,
|
||||
weatherData.Daily.Rain[i], weatherData.DailyUnits.Rain,
|
||||
weatherData.Daily.WindSpeed[i], weatherData.DailyUnits.WindSpeed)
|
||||
}
|
||||
|
||||
return []byte(builder.String()), nil
|
||||
}
|
||||
|
||||
func wmoCodeToDescription(code int) string {
|
||||
switch code {
|
||||
case 0:
|
||||
return "Clear sky"
|
||||
case 1, 2, 3:
|
||||
return "Mainly clear, partly cloudy, and overcast"
|
||||
case 45, 48:
|
||||
return "Fog and depositing rime fog"
|
||||
case 51, 53, 55:
|
||||
return "Drizzle: Light, moderate, and dense intensity"
|
||||
case 56, 57:
|
||||
return "Freezing Drizzle: Light and dense intensity"
|
||||
case 61, 63, 65:
|
||||
return "Rain: Slight, moderate and heavy intensity"
|
||||
case 66, 67:
|
||||
return "Freezing Rain: Light and heavy intensity"
|
||||
case 71, 73, 75:
|
||||
return "Snowfall: Slight, moderate, and heavy intensity"
|
||||
case 77:
|
||||
return "Snow grains"
|
||||
case 80, 81, 82:
|
||||
return "Rain showers: Slight, moderate, and violent"
|
||||
case 85, 86:
|
||||
return "Snow showers slight and heavy"
|
||||
case 95:
|
||||
return "Thunderstorm: Slight and heavy intensity"
|
||||
case 96, 99:
|
||||
return "Thunderstorm with slight and heavy hail"
|
||||
default:
|
||||
return "N/A"
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ type Config struct {
|
||||
StorageS3AccessKeySecret string // S3
|
||||
StorageS3UseSSL string // S3
|
||||
ServerID string
|
||||
SearXngUrl string
|
||||
}
|
||||
|
||||
var config *Config
|
||||
|
||||
+127
-32
@@ -1,49 +1,121 @@
|
||||
package domain
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LLMModelDetail struct {
|
||||
ParentModel string `json:"parent_model"`
|
||||
Format string `json:"format"`
|
||||
Family string `json:"family"`
|
||||
Families []string `json:"families"`
|
||||
ParameterSize string `json:"parameter_size"`
|
||||
QuantizationLevel string `json:"quantization_level"`
|
||||
}
|
||||
|
||||
type LLMModel struct {
|
||||
Name string `json:"name"`
|
||||
Model string `json:"model"`
|
||||
ModifiedAt int64 `json:"modified_at"`
|
||||
Size int64 `json:"size"`
|
||||
Digest string `json:"digest"`
|
||||
Detail *LLMModelDetail `json:"detail"`
|
||||
type Chat struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
TeamID string `db:"team_id" json:"teamId"`
|
||||
StartDatetime *time.Time `db:"start_datetime" json:"startDatetime"`
|
||||
EndDatetime *time.Time `db:"end_datetime" json:"endDatetime"`
|
||||
AgentID string `db:"agent_id" json:"agentId"`
|
||||
UserID string `db:"user_id" json:"userId"`
|
||||
TaskID string `db:"task_id" json:"taskId,omitempty"`
|
||||
Title string `db:"title" json:"title"`
|
||||
FreshTitle bool `db:"fresh_title" json:"freshTitle"`
|
||||
DateCreated time.Time `db:"_date_created" json:"_date_created"`
|
||||
DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"`
|
||||
VersionId string `db:"_version" json:"_version"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Role string
|
||||
Content string
|
||||
ToolCalls []ToolCall
|
||||
ToolCallID string
|
||||
|
||||
//Reasoning *Reasoning
|
||||
ID string `db:"id" json:"id"`
|
||||
TeamID string `db:"team_id" json:"teamId"`
|
||||
ChatID string `db:"chat_id" json:"chatId"`
|
||||
Role string `db:"role" json:"role"`
|
||||
Content string `db:"content" json:"content"`
|
||||
ToolCallID string `db:"tool_call_id" json:"toolCallId"`
|
||||
ToolCalls []ToolCall `db:"-" json:"toolcalls"`
|
||||
ToolCallsJson string `db:"tool_calls" json:"-"`
|
||||
DateCreated time.Time `db:"_date_created" json:"_date_created"`
|
||||
DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"`
|
||||
VersionId string `db:"_version" json:"_version"`
|
||||
}
|
||||
|
||||
type Tool struct {
|
||||
func (msg *Message) ToolCall2Json() error {
|
||||
if msg.ToolCalls != nil {
|
||||
toolCallJson, err := json.Marshal(&msg.ToolCalls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg.ToolCallsJson = string(toolCallJson)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg *Message) Json2toolCall() error {
|
||||
if msg.ToolCallsJson != "" {
|
||||
err := json.Unmarshal([]byte(msg.ToolCallsJson), &msg.ToolCalls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ChatWithMessages struct {
|
||||
Chat *Chat `json:"chat"`
|
||||
Messages []*Message `json:"messages"`
|
||||
}
|
||||
|
||||
type ChatParams struct {
|
||||
ProviderID string
|
||||
ModelID string
|
||||
ContextMaxTokens int // taille de contexte du modèle ; 0 = défaut
|
||||
MaxResponseTokens int // réserve de réponse ; 0 = défaut
|
||||
OnChunk StreamCallback
|
||||
}
|
||||
|
||||
type ToolParam struct {
|
||||
JsonName string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Mandatory bool `json:"mandatory"`
|
||||
}
|
||||
|
||||
type ITool interface {
|
||||
Name() string
|
||||
Description() string
|
||||
Params() []ToolParam
|
||||
Definition(ctx context.Context) *ToolDefinition
|
||||
Execute(ctx context.Context, input []byte, configuration string) ([]byte, error)
|
||||
}
|
||||
|
||||
type ToolDefinition struct {
|
||||
Type string
|
||||
Function *ToolFunction
|
||||
}
|
||||
|
||||
type ToolFunction struct {
|
||||
Name string
|
||||
Description string
|
||||
Parameters map[string]any
|
||||
}
|
||||
|
||||
type ToolCall struct {
|
||||
ID string
|
||||
Type string
|
||||
Function ToolCallFunction
|
||||
Extra map[string]map[string]any
|
||||
}
|
||||
|
||||
type ToolCallFunction struct {
|
||||
Name string
|
||||
Arguments string
|
||||
}
|
||||
|
||||
type Chunk struct {
|
||||
Content string
|
||||
Done bool `json:"done"`
|
||||
ToolName string `json:"toolName,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
Reasoning bool `json:"reasoning,omitempty"`
|
||||
}
|
||||
|
||||
type StreamParams struct {
|
||||
Tools []*Tool
|
||||
OnChunk func(chunk *Chunk)
|
||||
OnDebug func()
|
||||
}
|
||||
type StreamCallback func(chunk *Chunk)
|
||||
|
||||
type Role string
|
||||
|
||||
@@ -54,6 +126,12 @@ const (
|
||||
RoleTool Role = "tool"
|
||||
)
|
||||
|
||||
type LLMParams struct {
|
||||
Tools []*ToolDefinition
|
||||
OnChunk StreamCallback
|
||||
ReasoningEffort string
|
||||
}
|
||||
|
||||
type LLMEngine interface {
|
||||
ListProviderTypes() []Item
|
||||
ListModelsFromProvider(ctx context.Context, provider *Provider) ([]string, error)
|
||||
@@ -61,7 +139,24 @@ type LLMEngine interface {
|
||||
ctx context.Context,
|
||||
provider *Provider,
|
||||
modelID string,
|
||||
params *StreamParams,
|
||||
params *LLMParams,
|
||||
messages []*Message,
|
||||
) error
|
||||
) *Message
|
||||
Generate(
|
||||
ctx context.Context,
|
||||
provider *Provider,
|
||||
modelID string,
|
||||
messages []*Message,
|
||||
) (string, error)
|
||||
}
|
||||
|
||||
type ChatRepository interface {
|
||||
ListChats(userID string, teamID string, page int, query string, taskID string) ([]*Chat, error)
|
||||
ListChatsWithUnfreshTitles(teamID string) ([]*Chat, error)
|
||||
GetChat(userID string, teamID string, id string) (*Chat, error)
|
||||
CreateChat(userID string, chat *Chat) (*Chat, error)
|
||||
UpdateChat(userID string, chat *Chat) (*Chat, error)
|
||||
DeleteChat(userID string, teamID string, id string) error
|
||||
GetChatMessages(userID string, teamID string, id string, withToolCallResponses bool) ([]*Message, error)
|
||||
CreateChatMessage(userID string, message *Message) (*Message, error)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
// OIDCProvider décrit un fournisseur OpenID Connect enregistré dans la table oidc.
|
||||
type OIDCProvider struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
Label string `db:"label" json:"label"`
|
||||
Domain string `db:"domain" json:"domain"`
|
||||
ClientID string `db:"client_id" json:"clientId"`
|
||||
ClientSecret string `db:"client_secret" json:"-"`
|
||||
WellknownURL string `db:"wellknown_url" json:"wellknownUrl"`
|
||||
DateCreated time.Time `db:"_date_created" json:"_date_created"`
|
||||
DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"`
|
||||
VersionId string `db:"_version" json:"_version"`
|
||||
}
|
||||
|
||||
type OIDCProviderRepository interface {
|
||||
ListOIDCProviders() ([]*OIDCProvider, error)
|
||||
FindOIDCProvider(id string) (*OIDCProvider, error)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
// statuts d'une tâche planifiée
|
||||
TaskStatusActive = "active"
|
||||
TaskStatusDone = "done"
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
TeamID string `db:"team_id" json:"teamId"`
|
||||
ModelID string `db:"model_id" json:"modelId"`
|
||||
Label string `db:"label" json:"label"`
|
||||
Prompt string `db:"prompt" json:"prompt"`
|
||||
Cron string `db:"cron" json:"cron"`
|
||||
Repeat bool `db:"repeat" json:"repeat"`
|
||||
Status string `db:"status" json:"status"`
|
||||
NextDatetime *time.Time `db:"next_datetime" json:"nextDatetime"`
|
||||
LastDatetime *time.Time `db:"last_datetime" json:"lastDatetime"`
|
||||
DateCreated time.Time `db:"_date_created" json:"_date_created"`
|
||||
DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"`
|
||||
VersionId string `db:"_version" json:"_version"`
|
||||
}
|
||||
|
||||
type TaskRepository interface {
|
||||
ListTasks(userID string, teamID string) ([]*Task, error)
|
||||
ListActiveTasks() ([]*Task, error)
|
||||
GetTaskByID(userID string, teamID string, id string) (*Task, error)
|
||||
CreateTask(task *Task) (*Task, error)
|
||||
UpdateTask(task *Task) (*Task, error)
|
||||
DeleteTask(userID string, teamID string, id string) error
|
||||
FirstUserOfTeam(teamID string) (string, error)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Clés de contexte passées aux outils lors de l'exécution : identité de
|
||||
// l'appelant et modèle en cours, pour les outils qui en dépendent
|
||||
// (ex. scheduler crée une tâche pour l'équipe courante).
|
||||
type contextKey int
|
||||
|
||||
const (
|
||||
contextKeyUserID contextKey = iota
|
||||
contextKeyTeamID
|
||||
contextKeyModelID
|
||||
)
|
||||
|
||||
func WithToolContext(ctx context.Context, userID string, teamID string, modelID string) context.Context {
|
||||
ctx = context.WithValue(ctx, contextKeyUserID, userID)
|
||||
ctx = context.WithValue(ctx, contextKeyTeamID, teamID)
|
||||
return context.WithValue(ctx, contextKeyModelID, modelID)
|
||||
}
|
||||
|
||||
func UserIDFromContext(ctx context.Context) (string, bool) {
|
||||
v, ok := ctx.Value(contextKeyUserID).(string)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func TeamIDFromContext(ctx context.Context) (string, bool) {
|
||||
v, ok := ctx.Value(contextKeyTeamID).(string)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func ModelIDFromContext(ctx context.Context) (string, bool) {
|
||||
v, ok := ctx.Value(contextKeyModelID).(string)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
type Tool struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
TeamID string `db:"team_id" json:"teamId"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Enabled bool `db:"enabled" json:"enabled"`
|
||||
Configuration string `db:"configuration" json:"configuration"`
|
||||
DateCreated time.Time `db:"_date_created" json:"_date_created"`
|
||||
DateUpdated *time.Time `db:"_date_updated" json:"_date_updated"`
|
||||
VersionId string `db:"_version" json:"_version"`
|
||||
}
|
||||
|
||||
type ToolRepository interface {
|
||||
ListTools(userID string, teamID string) ([]*Tool, error)
|
||||
GetToolByName(userID string, teamID string, name string) (*Tool, error)
|
||||
GetToolByID(userID string, teamID string, id string) (*Tool, error)
|
||||
CreateTool(userID string, agent *Tool) (*Tool, error)
|
||||
UpdateTool(userID string, agent *Tool) (*Tool, error)
|
||||
DeleteTool(userID string, teamID string, name string) error
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
package llmserver
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/utility"
|
||||
)
|
||||
|
||||
type Dependencies struct {
|
||||
ChatRepository domain.ChatRepository
|
||||
}
|
||||
|
||||
type LLMServer struct {
|
||||
type Router struct {
|
||||
started bool
|
||||
mu sync.Mutex
|
||||
sessions map[string]*Session // running sessions
|
||||
@@ -19,8 +21,8 @@ type LLMServer struct {
|
||||
closeChan chan bool
|
||||
}
|
||||
|
||||
func NewLLMServer(deps Dependencies) *LLMServer {
|
||||
return &LLMServer{
|
||||
func NewRouter(deps Dependencies) *Router {
|
||||
return &Router{
|
||||
started: true,
|
||||
sessions: make(map[string]*Session),
|
||||
newSessionChan: make(chan string),
|
||||
@@ -28,7 +30,7 @@ func NewLLMServer(deps Dependencies) *LLMServer {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LLMServer) Start() error {
|
||||
func (s *Router) Start() error {
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -40,13 +42,13 @@ func (s *LLMServer) Start() error {
|
||||
|
||||
}
|
||||
|
||||
func (s *LLMServer) Stop(ctx context.Context) error {
|
||||
func (s *Router) Stop(ctx context.Context) error {
|
||||
s.closeChan <- true
|
||||
s.started = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LLMServer) StartSession() (*Session, error) {
|
||||
func (s *Router) StartSession() (*Session, error) {
|
||||
sid := utility.GenID()
|
||||
session := &Session{}
|
||||
|
||||
@@ -56,3 +58,9 @@ func (s *LLMServer) StartSession() (*Session, error) {
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (s *Router) Process(ctx context.Context, Chat *domain.ChatWithMessages) error {
|
||||
s.closeChan <- true
|
||||
s.started = false
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package gateway
|
||||
|
||||
type Session struct {
|
||||
serverID string
|
||||
chatID string
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func (h *AgentHandler) ListAgents(c *echo.Context) error {
|
||||
teamID := c.Param("space")
|
||||
agents, err := h.agentService.ListAgents(userID, teamID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error listing agents", err)
|
||||
c.Logger().Error("error listing agents", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error listing agents")
|
||||
}
|
||||
return c.JSON(http.StatusOK, agents)
|
||||
@@ -54,7 +54,7 @@ func (h *AgentHandler) GetAgent(c *echo.Context) error {
|
||||
agentID := c.Param("agent")
|
||||
agent, err := h.agentService.GetAgent(userID, teamID, agentID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error getting agent: %s", err)
|
||||
c.Logger().Error("error getting agent", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error getting agent")
|
||||
}
|
||||
return c.JSON(http.StatusOK, agent)
|
||||
@@ -66,13 +66,13 @@ func (h *AgentHandler) CreateAgent(c *echo.Context) error {
|
||||
var agent domain.Agent
|
||||
err := c.Bind(&agent)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding agent : %s", err)
|
||||
c.Logger().Error("error binding agent", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding agent")
|
||||
}
|
||||
agent.TeamID = teamID
|
||||
updagent, err := h.agentService.CreateAgent(userID, &agent)
|
||||
if err != nil {
|
||||
c.Logger().Error("error creating agent: %s", err)
|
||||
c.Logger().Error("error creating agent", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating agent")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updagent)
|
||||
@@ -84,13 +84,13 @@ func (h *AgentHandler) UpdateAgent(c *echo.Context) error {
|
||||
var agent domain.Agent
|
||||
err := c.Bind(&agent)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding agent : %s", err)
|
||||
c.Logger().Error("error binding agent", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding agent")
|
||||
}
|
||||
agent.TeamID = teamID
|
||||
updagent, err := h.agentService.UpdateAgent(userID, &agent)
|
||||
if err != nil {
|
||||
c.Logger().Error("error updating agent: %s", err)
|
||||
c.Logger().Error("error updating agent", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error updating agent")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updagent)
|
||||
@@ -102,7 +102,7 @@ func (h *AgentHandler) DeleteAgent(c *echo.Context) error {
|
||||
agentID := c.Param("agent")
|
||||
err := h.agentService.DeleteAgent(userID, teamID, agentID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error creating agent: %s", err)
|
||||
c.Logger().Error("error creating agent", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating agent")
|
||||
}
|
||||
return c.JSON(http.StatusOK, agentID)
|
||||
|
||||
@@ -17,6 +17,8 @@ func NewAuthGroup(prefix string, e *echo.Group, service *auth.Service) *echo.Gro
|
||||
}
|
||||
auth := e.Group(prefix)
|
||||
auth.POST("/login", authHandler.Login)
|
||||
auth.GET("/oidc/providers", authHandler.OIDCProviders)
|
||||
auth.GET("/oidc/login", authHandler.OIDCLogin)
|
||||
auth.GET("/oidc/callback", authHandler.OIDCCallback)
|
||||
auth.POST("/register", authHandler.Register)
|
||||
return auth
|
||||
@@ -69,8 +71,77 @@ func (h AuthHandler) Login(c *echo.Context) error {
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// oidcRedirectURI reconstruit l'URL de callback du serveur à partir de la
|
||||
// requête. Elle doit être identique entre /oidc/login et /oidc/callback.
|
||||
func oidcRedirectURI(c *echo.Context) string {
|
||||
scheme := "http"
|
||||
if proto := c.Request().Header.Get("X-Forwarded-Proto"); proto != "" {
|
||||
scheme = proto
|
||||
} else if c.Request().TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
host := c.Request().Header.Get("X-Forwarded-Host")
|
||||
if host == "" {
|
||||
host = c.Request().Host
|
||||
}
|
||||
return scheme + "://" + host + "/api/auth/oidc/callback"
|
||||
}
|
||||
|
||||
func (h AuthHandler) OIDCProviders(c *echo.Context) error {
|
||||
providers, err := h.authService.ListOIDCProviders()
|
||||
if err != nil {
|
||||
c.Logger().Error(fmt.Sprintf("error listing oidc providers: %s", err))
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "error listing oidc providers")
|
||||
}
|
||||
return c.JSON(http.StatusOK, providers)
|
||||
}
|
||||
|
||||
func (h AuthHandler) OIDCLogin(c *echo.Context) error {
|
||||
providerID := c.QueryParam("provider")
|
||||
if providerID == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "provider query parameter is required")
|
||||
}
|
||||
|
||||
authorizeURL, err := h.authService.OIDCAuthorizeURL(context.Background(), providerID, oidcRedirectURI(c))
|
||||
if err != nil {
|
||||
c.Logger().Error(fmt.Sprintf("error building oidc authorize url: %s", err))
|
||||
return c.Redirect(http.StatusFound, "/login?error=oidc")
|
||||
}
|
||||
return c.Redirect(http.StatusFound, authorizeURL)
|
||||
}
|
||||
|
||||
func (h AuthHandler) OIDCCallback(c *echo.Context) error {
|
||||
return nil
|
||||
if errParam := c.QueryParam("error"); errParam != "" {
|
||||
c.Logger().Error(fmt.Sprintf("oidc provider returned an error: %s", errParam))
|
||||
return c.Redirect(http.StatusFound, "/login?error=oidc")
|
||||
}
|
||||
code := c.QueryParam("code")
|
||||
state := c.QueryParam("state")
|
||||
if code == "" || state == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "code and state are required")
|
||||
}
|
||||
|
||||
session, err := h.authService.OIDCCallback(context.Background(), state, code, oidcRedirectURI(c))
|
||||
if err != nil {
|
||||
c.Logger().Error(fmt.Sprintf("error while oidc callback: %s", err))
|
||||
return c.Redirect(http.StatusFound, "/login?error=oidc")
|
||||
}
|
||||
|
||||
// L'application SPA est servie par ce même serveur (même origine) et lit
|
||||
// ses tokens dans localStorage: on les dépose ici puis on redirige vers
|
||||
// l'application, sans jamais les faire transiter dans une URL.
|
||||
page := fmt.Sprintf(`<!doctype html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"><title>Connexion en cours...</title></head>
|
||||
<body>
|
||||
<script>
|
||||
localStorage.setItem("accessToken", %q);
|
||||
localStorage.setItem("refreshToken", %q);
|
||||
location.replace("/");
|
||||
</script>
|
||||
</body>
|
||||
</html>`, session.AccessToken, session.RefreshToken)
|
||||
return c.Blob(http.StatusOK, "text/html; charset=utf-8", []byte(page))
|
||||
}
|
||||
|
||||
func (h AuthHandler) Register(c *echo.Context) error {
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/chat"
|
||||
)
|
||||
|
||||
func NewChatGroup(prefix string, e *echo.Group, service *chat.Service, middlewares ...echo.MiddlewareFunc) *echo.Group {
|
||||
chatHandler := &ChatHandler{
|
||||
chatService: service,
|
||||
}
|
||||
auth := e.Group(prefix, middlewares...)
|
||||
|
||||
auth.POST("/:space", chatHandler.NewChat)
|
||||
auth.POST("/:space/:id", chatHandler.NewChatMessage)
|
||||
auth.GET("/:space/all/:page", chatHandler.List)
|
||||
auth.GET("/:space/:id", chatHandler.Get)
|
||||
auth.GET("/:space/:id/subscribe", chatHandler.Subscribe)
|
||||
auth.DELETE("/:space/:id", chatHandler.Delete)
|
||||
return auth
|
||||
}
|
||||
|
||||
type ChatHandler struct {
|
||||
chatService *chat.Service
|
||||
}
|
||||
|
||||
type ChatRequest struct {
|
||||
Prompt string `json:"prompt"`
|
||||
AgentID string `json:"agentID"`
|
||||
ProviderID string `json:"providerID"`
|
||||
ModelID string `json:"modelID"`
|
||||
ReasoningLevel string `json:"reasoningLevel"`
|
||||
}
|
||||
|
||||
func (h *ChatHandler) NewChat(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
var request ChatRequest
|
||||
err := c.Bind(&request)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding request", "error", err.Error())
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding request")
|
||||
}
|
||||
chat, err := h.chatService.NewChat(
|
||||
userID,
|
||||
teamID,
|
||||
request.AgentID,
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
c.Logger().Error("error creating chat", "error", err.Error())
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating chat")
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, chat)
|
||||
}
|
||||
|
||||
func (h *ChatHandler) NewChatMessage(c *echo.Context) error {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
chatID := c.Param("id")
|
||||
var request ChatRequest
|
||||
err := c.Bind(&request)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding request", "error", err.Error())
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding request")
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(c.Response())
|
||||
chunkChan := make(chan *domain.Chunk)
|
||||
|
||||
_, err = h.chatService.AddChatMessage(
|
||||
ctx,
|
||||
userID,
|
||||
teamID,
|
||||
chatID,
|
||||
request.AgentID,
|
||||
request.Prompt,
|
||||
&domain.ChatParams{
|
||||
OnChunk: func(chunk *domain.Chunk) {
|
||||
chunkChan <- chunk
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
c.Logger().Error("error creating chat message", "error", err.Error())
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating chat message")
|
||||
}
|
||||
|
||||
enc.Encode(&domain.Chunk{
|
||||
Done: false,
|
||||
Role: string(domain.RoleUser),
|
||||
Content: request.Prompt,
|
||||
})
|
||||
http.NewResponseController(c.Response()).Flush()
|
||||
|
||||
for {
|
||||
chunk := <-chunkChan
|
||||
enc.Encode(chunk)
|
||||
http.NewResponseController(c.Response()).Flush()
|
||||
if chunk.Done {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (h *ChatHandler) List(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
page := c.Param("page")
|
||||
pageInt, _ := strconv.Atoi(page)
|
||||
query := c.QueryParam("q")
|
||||
taskID := c.QueryParam("task")
|
||||
chats, err := h.chatService.ListChats(userID, teamID, pageInt, query, taskID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error listing chats", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error listing chats")
|
||||
}
|
||||
// évite "null" dans la réponse quand il n'y a pas de conversation
|
||||
if chats == nil {
|
||||
chats = []*domain.Chat{}
|
||||
}
|
||||
return c.JSON(http.StatusOK, chats)
|
||||
}
|
||||
|
||||
func (h *ChatHandler) Get(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
chatID := c.Param("id")
|
||||
chatWithMessages, err := h.chatService.GetChatWithMessages(userID, teamID, chatID)
|
||||
|
||||
if err != nil {
|
||||
c.Logger().Error("error getting chat with messages", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error getting chat")
|
||||
}
|
||||
return c.JSON(http.StatusOK, chatWithMessages)
|
||||
}
|
||||
|
||||
func (h *ChatHandler) Delete(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
chatID := c.Param("id")
|
||||
err := h.chatService.DeleteChat(userID, teamID, chatID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error deleting chat", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error deleting chat")
|
||||
}
|
||||
return c.JSON(http.StatusOK, "chat deleted")
|
||||
}
|
||||
|
||||
func (h *ChatHandler) Subscribe(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
chatID := c.Param("id")
|
||||
|
||||
enc := json.NewEncoder(c.Response())
|
||||
chunkChan := make(chan *domain.Chunk)
|
||||
|
||||
err := h.chatService.Subscribe(
|
||||
userID,
|
||||
teamID,
|
||||
chatID,
|
||||
func(chunk *domain.Chunk) {
|
||||
chunkChan <- chunk
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
c.Logger().Error("error subscribing to chat", "error", err.Error())
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating chat")
|
||||
}
|
||||
|
||||
for {
|
||||
chunk := <-chunkChan
|
||||
if chunk.Done {
|
||||
return nil
|
||||
}
|
||||
enc.Encode(chunk)
|
||||
http.NewResponseController(c.Response()).Flush()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func (h *ModelHandler) ListProviders(c *echo.Context) error {
|
||||
teamID := c.Param("space")
|
||||
providers, err := h.providerService.ListProviders(userID, teamID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error listing providers: %s", err)
|
||||
c.Logger().Error("error listing providers", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error listing providers")
|
||||
}
|
||||
return c.JSON(http.StatusOK, providers)
|
||||
@@ -52,7 +52,7 @@ func (h *ModelHandler) GetProvider(c *echo.Context) error {
|
||||
providerID := c.Param("provider")
|
||||
provider, err := h.providerService.GetProvider(userID, teamID, providerID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error getting provider: %s", err)
|
||||
c.Logger().Error("error getting provider", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error getting provider")
|
||||
}
|
||||
return c.JSON(http.StatusOK, provider)
|
||||
@@ -64,13 +64,13 @@ func (h *ModelHandler) CreateProvider(c *echo.Context) error {
|
||||
var provider domain.Provider
|
||||
err := c.Bind(&provider)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding provider: %s", err)
|
||||
c.Logger().Error("error binding provider", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding provider")
|
||||
}
|
||||
provider.TeamID = teamID
|
||||
updprovider, err := h.providerService.CreateProvider(userID, &provider)
|
||||
if err != nil {
|
||||
c.Logger().Error("error creating provider: %s", err)
|
||||
c.Logger().Error("error creating provider", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating provider")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updprovider)
|
||||
@@ -82,13 +82,13 @@ func (h *ModelHandler) UpdateProvider(c *echo.Context) error {
|
||||
var provider domain.Provider
|
||||
err := c.Bind(&provider)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding provider: %s", err)
|
||||
c.Logger().Error("error binding provider", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding provider")
|
||||
}
|
||||
provider.TeamID = teamID
|
||||
updprovider, err := h.providerService.UpdateProvider(userID, &provider)
|
||||
if err != nil {
|
||||
c.Logger().Error("error updating provider: %s", err)
|
||||
c.Logger().Error("error updating provider", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating provider")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updprovider)
|
||||
@@ -100,7 +100,7 @@ func (h *ModelHandler) DeleteProvider(c *echo.Context) error {
|
||||
providerID := c.Param("provider")
|
||||
err := h.providerService.DeleteProvider(userID, teamID, providerID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error creating provider: %s", err)
|
||||
c.Logger().Error("error creating provider", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating provider")
|
||||
}
|
||||
return c.JSON(http.StatusOK, providerID)
|
||||
@@ -111,13 +111,13 @@ func (h *ModelHandler) ListAvailableModels(c *echo.Context) error {
|
||||
var provider domain.Provider
|
||||
err := c.Bind(&provider)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding provider: %s", err)
|
||||
c.Logger().Error("error binding provider", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding provider")
|
||||
}
|
||||
provider.TeamID = teamID
|
||||
list, err := h.providerService.ListAvailableModels(c.Request().Context(), &provider)
|
||||
if err != nil {
|
||||
c.Logger().Error("error listing available models: %s", err)
|
||||
c.Logger().Error("error listing available models", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error listing available models")
|
||||
}
|
||||
return c.JSON(http.StatusOK, list)
|
||||
@@ -128,7 +128,7 @@ func (h *ModelHandler) ListProvidersModels(c *echo.Context) error {
|
||||
teamID := c.Param("space")
|
||||
list, err := h.providerService.ListProvidersModels(userID, teamID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error listing providers models: %s", err)
|
||||
c.Logger().Error("error listing providers models", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error listing providers models")
|
||||
}
|
||||
return c.JSON(http.StatusOK, list)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/task"
|
||||
)
|
||||
|
||||
type TaskHandler struct {
|
||||
taskService *task.Service
|
||||
}
|
||||
|
||||
func NewTaskGroup(prefix string, e *echo.Group, service *task.Service, middlewares ...echo.MiddlewareFunc) *echo.Group {
|
||||
taskHandler := &TaskHandler{taskService: service}
|
||||
|
||||
auth := e.Group(prefix, middlewares...)
|
||||
auth.GET("/:space", taskHandler.List)
|
||||
auth.PUT("/:space/:id", taskHandler.Update)
|
||||
auth.DELETE("/:space/:id", taskHandler.Delete)
|
||||
return auth
|
||||
}
|
||||
|
||||
func (h *TaskHandler) List(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
tasks, err := h.taskService.ListTasks(userID, teamID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error listing tasks", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error listing tasks")
|
||||
}
|
||||
// évite "null" dans la réponse quand il n'y a pas de tâche
|
||||
if tasks == nil {
|
||||
tasks = []*domain.Task{}
|
||||
}
|
||||
return c.JSON(http.StatusOK, tasks)
|
||||
}
|
||||
|
||||
func (h *TaskHandler) Update(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
taskID := c.Param("id")
|
||||
|
||||
var taskModel domain.Task
|
||||
if err := c.Bind(&taskModel); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding request")
|
||||
}
|
||||
taskModel.ID = taskID
|
||||
|
||||
updated, err := h.taskService.UpdateTask(userID, teamID, &taskModel)
|
||||
if err != nil {
|
||||
c.Logger().Error("error updating task", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error updating task")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updated)
|
||||
}
|
||||
|
||||
func (h *TaskHandler) Delete(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
taskID := c.Param("id")
|
||||
if err := h.taskService.DeleteTask(userID, teamID, taskID); err != nil {
|
||||
c.Logger().Error("error deleting task", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error deleting task")
|
||||
}
|
||||
return c.JSON(http.StatusOK, "task deleted")
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"trankilou.fr/lassistanoque/backend/internal/domain"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/tool"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/user"
|
||||
)
|
||||
|
||||
func NewToolGroup(
|
||||
prefix string,
|
||||
e *echo.Group,
|
||||
ToolService *tool.Service,
|
||||
userService *user.Service,
|
||||
middlewares ...echo.MiddlewareFunc) *echo.Group {
|
||||
|
||||
ToolHandler := &ToolHandler{
|
||||
ToolService: ToolService,
|
||||
userService: userService,
|
||||
}
|
||||
Tool := e.Group(prefix, middlewares...)
|
||||
|
||||
Tool.GET("/:space", ToolHandler.ListTools)
|
||||
Tool.GET("/:space/:tool", ToolHandler.GetTool)
|
||||
Tool.GET("/:space/:tool/params", ToolHandler.GetToolParams)
|
||||
Tool.PUT("/:space", ToolHandler.UpdateTool)
|
||||
Tool.POST("/:space", ToolHandler.CreateTool)
|
||||
//Tool.DELETE("/:space/:tool", ToolHandler.DeleteTool)
|
||||
|
||||
return Tool
|
||||
}
|
||||
|
||||
type ToolHandler struct {
|
||||
ToolService *tool.Service
|
||||
userService *user.Service
|
||||
}
|
||||
|
||||
func (h *ToolHandler) ListTools(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
Tools, err := h.ToolService.ListTools(userID, teamID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error listing Tools", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error listing Tools")
|
||||
}
|
||||
return c.JSON(http.StatusOK, Tools)
|
||||
}
|
||||
|
||||
func (h *ToolHandler) GetTool(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
ToolID := c.Param("tool")
|
||||
Tool, err := h.ToolService.GetTool(userID, teamID, ToolID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error getting Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error getting Tool")
|
||||
}
|
||||
return c.JSON(http.StatusOK, Tool)
|
||||
}
|
||||
|
||||
func (h *ToolHandler) GetToolParams(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
toolID := c.Param("tool")
|
||||
tool, err := h.ToolService.GetTool(userID, teamID, toolID)
|
||||
if err != nil {
|
||||
c.Logger().Error("error getting Tool", "error", err, "teamid", teamID, "toolID", toolID)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error getting Tool")
|
||||
}
|
||||
toolImpl := h.ToolService.GetToolImpl(tool.Name)
|
||||
return c.JSON(http.StatusOK, toolImpl.Params())
|
||||
}
|
||||
|
||||
func (h *ToolHandler) CreateTool(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
var Tool domain.Tool
|
||||
err := c.Bind(&Tool)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding Tool")
|
||||
}
|
||||
Tool.TeamID = teamID
|
||||
updTool, err := h.ToolService.CreateTool(userID, &Tool)
|
||||
if err != nil {
|
||||
c.Logger().Error("error creating Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error creating Tool")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updTool)
|
||||
}
|
||||
|
||||
func (h *ToolHandler) UpdateTool(c *echo.Context) error {
|
||||
userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
teamID := c.Param("space")
|
||||
var Tool domain.Tool
|
||||
err := c.Bind(&Tool)
|
||||
if err != nil {
|
||||
c.Logger().Error("error binding Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error binding Tool")
|
||||
}
|
||||
Tool.TeamID = teamID
|
||||
updTool, err := h.ToolService.UpdateTool(userID, &Tool)
|
||||
if err != nil {
|
||||
c.Logger().Error("error updating Tool", "error", err)
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "error updating Tool")
|
||||
}
|
||||
return c.JSON(http.StatusOK, updTool)
|
||||
}
|
||||
|
||||
// func (h *ToolHandler) DeleteTool(c *echo.Context) error {
|
||||
// userID := c.Get(auth.ContextUserIDKey).(string)
|
||||
// teamID := c.Param("space")
|
||||
// ToolID := c.Param("tool")
|
||||
// err := h.ToolService.DeleteTool(userID, teamID, ToolID)
|
||||
// if err != nil {
|
||||
// c.Logger().Error("error creating Tool", "error", err)
|
||||
// return echo.NewHTTPError(http.StatusBadRequest, "error creating Tool")
|
||||
// }
|
||||
// return c.JSON(http.StatusOK, ToolID)
|
||||
// }
|
||||
@@ -13,8 +13,11 @@ import (
|
||||
"trankilou.fr/lassistanoque/backend/internal/http/handlers"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/agent"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/auth"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/chat"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/provider"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/storage"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/task"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/tool"
|
||||
"trankilou.fr/lassistanoque/backend/internal/service/user"
|
||||
)
|
||||
|
||||
@@ -40,6 +43,9 @@ type Dependencies struct {
|
||||
UserService *user.Service
|
||||
ProviderService *provider.Service
|
||||
AgentService *agent.Service
|
||||
ChatService *chat.Service
|
||||
ToolService *tool.Service
|
||||
TaskService *task.Service
|
||||
TokenManager auth.TokenManager
|
||||
}
|
||||
|
||||
@@ -80,7 +86,10 @@ func NewRouter(deps Dependencies) *Router {
|
||||
_ = handlers.NewMiscGroup("/misc", api, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewModelGroup("/provider", api, deps.ProviderService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewAgentGroup("/agent", api, deps.AgentService, deps.UserService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewToolGroup("/tool", api, deps.ToolService, deps.UserService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewTeamGroup("/team", api, deps.UserService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewChatGroup("/chat", api, deps.ChatService, deps.TokenManager.TokenMiddleware)
|
||||
_ = handlers.NewTaskGroup("/task", api, deps.TaskService, deps.TokenManager.TokenMiddleware)
|
||||
|
||||
server := http.Server{
|
||||
Addr: fmt.Sprintf(":%d", cfg.HttpPort),
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--tw-duration:initial;--tw-leading:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-ease:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-green-300:oklch(87.1% .15 154.449);--color-green-700:oklch(52.7% .154 150.069);--color-cyan-600:oklch(60.9% .126 221.723);--color-sky-300:oklch(82.8% .111 230.318);--color-sky-700:oklch(50% .134 242.749);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-900:oklch(37.9% .146 265.522);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-300:oklch(86.9% .005 56.366);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-stone-950:oklch(14.7% .004 49.25);--color-white:#fff;--spacing:.25rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-bold:700;--radius-sm:.25rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variatLine truncated
|
||||
@@ -0,0 +1,2 @@
|
||||
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-duration:initial;--tw-leading:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-100:oklch(93.6% .032 17.717);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-green-300:oklch(87.1% .15 154.449);--color-green-500:oklch(72.3% .219 149.579);--color-green-700:oklch(52.7% .154 150.069);--color-cyan-600:oklch(60.9% .126 221.723);--color-sky-300:oklch(82.8% .111 230.318);--color-sky-400:oklch(74.6% .16 232.661);--color-sky-500:oklch(68.5% .169 237.323);--color-sky-700:oklch(50% .134 242.749);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-900:oklch(37.9% .146 265.522);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-stone-50:oklch(98.5% .001 106.423);--color-stone-100:oklch(97% .001 106.424);--color-stone-200:oklch(92.3% .003 48.717);--color-stone-300:oklch(86.9% .005 56.366);--color-stone-400:oklch(70.9% .01 56.259);--color-stone-500:oklch(55.3% .013 58.071);--color-stone-600:oklch(44.4% .011 73.639);--color-stone-700:oklch(37.4% .01 67.558);--color-stone-800:oklch(26.8% .007 34.298);--color-stone-900:oklch(21.6% .006 56.043);--color-stone-950:oklch(14.7% .004 49.25);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-bold:700;--radius-sm:.25rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-heightLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
img.svelte-1hoglcg{opacity:0;max-width:100%;height:auto}img.fade-in.svelte-1hoglcg{opacity:1;transition:opacity .3s ease-in-out}img.visible.svelte-1hoglcg{opacity:1;transition:none}img.error.svelte-1hoglcg{opacity:.5;filter:grayscale()}
|
||||
@@ -1,3 +0,0 @@
|
||||
var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r},n=Array.isArray,r=Array.prototype.indexOf,i=Array.prototype.includes,a=Array.from,o=Object.defineProperty,s=Object.getOwnPropertyDescriptor,c=Object.getOwnPropertyDescriptors,l=Object.prototype,u=Array.prototype,d=Object.getPrototypeOf,f=Object.isExtensible,p=()=>{};function m(e){for(var t=0;t<e.length;t++)e[t]()}function h(){var e,t;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}var g=1<<24,_=1024,v=2048,y=4096,b=8192,x=16384,S=32768,ee=1<<25,C=65536,te=1<<18,ne=1<<19,re=1<<20,ie=1<<25,ae=65536,oe=1<<21,se=1<<22,ce=1<<23,w=Symbol(`$state`),le=Symbol(`legacy props`),ue=Symbol(``),de=Symbol(`proxy path`),fe=Symbol(`attributes`),pe=Symbol(`class`),me=Symbol(`style`),he=Symbol(`text`),ge=Symbol(`form reset`),_e=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},ve=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function ye(e){throw Error(`https://svelte.dev/e/experimental_async_required`)}function be(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function xe(){throw Error(`https://svelte.dev/e/missing_context`)}function Se(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Ce(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function we(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function Te(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Ee(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function De(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Oe(){throw Error(`https://svelte.dev/e/fork_discarded`)}function ke(){throw Error(`https://svelte.dev/e/fork_timing`)}function Ae(){throw Error(`https://svelte.dev/e/get_abort_signal_outside_reaction`)}function je(){throw Error(`https://svelte.dev/e/hydration_failed`)}function Me(e){throw Error(`https://svelte.dev/e/lifecycle_legacy_only`)}function Ne(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Pe(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function Fe(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Ie(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Le(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var Re={},T=Symbol(`uninitialized`),ze=`http://www.w3.org/1999/xhtml`;function Be(){console.warn(`https://svelte.dev/e/derived_inert`)}function Ve(e){console.warn(`https://svelte.dev/e/hydratable_missing_but_expected`)}function He(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Ue(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function We(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var E=!1;function D(e){E=e}var O;function k(e){if(e===null)throw He(),Re;return O=e}function Ge(){return k(R(O))}function Ke(e){if(E){if(R(O)!==null)throw He(),Re;O=e}}function qe(e=1){if(E){for(var t=e,n=O;t--;)n=R(n);O=n}}function Je(e=!0){for(var t=0,n=O;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=R(n);e&&n.remove(),n=i}}function Ye(e){if(!e||e.nodeType!==8)throw He(),Re;return e.data}function Xe(e){return e===this.v}function Ze(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function Qe(e){return!Ze(e,this.v)}var $e=!1;function et(){$e=!0}function tt(e,t){return e.label=t,nt(e.v,t),e}function nt(e,t){return e?.[de]?.(t),e}function rt(e){return typeof e==`symbol`?`Symbol(${e.description})`:typeof e==`function`?`<function>`:typeof e==`object`&&e?`<object>`:String(e)}var A=null;function it(e){A=e}function at(){let e={};return[()=>(ct(e)||xe(),ot(e)),t=>st(e,t)]}function ot(e){return pt(`getContext`).get(e)}function st(e,t){return pt(`setContext`).set(e,t),t}function ct(e){return pt(`hasContext`).has(e)}function lt(){return pt(`getAllContexts`)}function ut(e,t=!1,n){A={p:A,i:!1,c:null,e:null,s:e,x:null,r:G,l:$e&&!t?{s:null,u:null,$:[]}:null}}function dt(e){var t=A,n=t.e;if(n!==null){t.e=null;for(var r of n)Xn(r)}return e!==void 0&&(t.x=e),t.i=!0,A=t.p,e??{}}function ft(){return!$e||A!==null&&A.l===null}function pt(e){return A===null&&be(e),A.c??=new Map(mt(A)||void 0)}function mt(e){let t=e.p;for(;t!==null;){let e=t.c;if(e!==null)return e;t=t.p}return null}var ht=[];function gt(){var e=ht;ht=[],m(e)}function j(e){if(ht.length===0&&!tn){var t=ht;queueMicrotask(()=>{t===ht&>()})}ht.push(e)}function _t(){for(;ht.length>0;)gt()}function vt(e){var t=G;if(t===null)return H.f|=ce,e;if(!(t.f&32768)&&!(t.f&4))throw e;yt(e,t)}function yt(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}var bt=~(v|y|_);function M(e,t){e.f=e.fLine truncated
|
||||
`).some(e=>e.startsWith(`https://svelte.dev/e/`)))throw r;return r!==Re&&console.warn(`Failed to hydrate: `,r),t.recover===!1&&je(),Ln(),Vn(n),D(!1),di(e,t)}finally{D(r),k(i)}}var pi=new Map;function mi(e,{target:t,anchor:n,props:r={},events:i,context:o,intro:s=!0,transformError:c}){Ln();var l=void 0,u=Qn(()=>{var s=n??t.appendChild(I());It(s,{pending:()=>{}},t=>{ut({});var n=A;if(o&&(n.c=o),i&&(r.$$events=i),E&&$(t,null),l=e(t,r)||{},E&&(G.nodes.end=O,O===null||O.nodeType!==8||O.data!==`]`))throw He(),Re;dt()},c);var u=new Set,d=e=>{for(var n=0;n<e.length;n++){var r=e[n];if(!u.has(r)){u.add(r);var i=Gr(r);for(let e of[t,document]){var a=pi.get(e);a===void 0&&(a=new Map,pi.set(e,a));var o=a.get(r);o===void 0?(e.addEventListener(r,ei,{passive:i}),a.set(r,1)):a.set(r,o+1)}}}};return d(a(qr)),Jr.add(d),()=>{for(var e of u)for(let n of[t,document]){var r=pi.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,ei),r.delete(e),r.size===0&&pi.delete(n)):r.set(e,i)}Jr.delete(d),s!==n&&s.parentNode?.removeChild(s)}});return hi.set(l,u),l}var hi=new WeakMap;function gi(e,t){let n=hi.get(e);return n?(hi.delete(e),n(t)):Promise.resolve()}var _i=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)fr(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(fr(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(V(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();mr(r,t),t.append(I()),this.#n.set(e,{effect:r,fragment:t})}else V(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),ur(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(V(n.effect),this.#n.delete(e))};ensure(e,t){var n=N,r=Hn();if(t&&!this.#t.has(e)&&!this.#n.has(e))if(r){var i=document.createDocumentFragment(),a=I();i.append(a),this.#n.set(e,{effect:B(()=>t(a)),fragment:i})}else this.#t.set(e,B(()=>t(this.anchor)));if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else E&&(this.anchor=O),this.#a(n)}};function vi(e,t,n=!1){var r;E&&(r=O,Ge());var i=new _i(e),a=n?C:0;function o(e,t){if(E){var n=Ye(r);if(e!==parseInt(n.substring(1))){var a=Je();k(a),i.anchor=a,D(!1),i.ensure(e,t),D(!0);return}}i.ensure(e,t)}rr(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var yi=Symbol(`NaN`);function bi(e,t,n){E&&Ge();var r=new _i(e),i=!ft();rr(()=>{var e=t();e!==e&&(e=yi),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function xi(e,t){return t}function Si(e,t,n){for(var r=[],i=t.length,o,s=t.length,c=0;c<i;c++){let n=t[c];ur(n,()=>{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;Ci(e,a(o.done)),t.delete(o),t.size===0&&(e.outrogroups=null)}}else--s},!1)}if(s===0){var l=r.length===0&&n!==null;if(l){var u=n,d=u.parentNode;Vn(d),d.append(u),e.items.clear()}Ci(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function Ci(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i<t.length;i++){var a=t[i];r?.has(a)?(a.f|=ie,mr(a,document.createDocumentFragment())):V(t[i],n)}}var wi;function Ti(e,t,r,i,o,s=null){var c=e,l=new Map;if(t&4){var u=e;c=E?k(L(u)):u.appendChild(I())}E&&Ge();var d=null,f=Kt(()=>{var e=r();return n(e)?e:e==null?[]:a(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Di(v,p,c,t,i),d!==null&&(p.length===0?d.f&33554432?(d.f^=ie,ki(d,null,c)):fr(d):ur(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:rr(()=>{p=Z(f);var e=p.length;let n=!1;E&&Ye(c)===`[!`!=(e===0)&&(c=Je(),k(c),D(!1),n=!0);for(var a=new Set,u=N,v=Hn(),y=0;y<e;y+=1){E&&O.nodeType===8&&O.data===`]`&&(c=O,n=!0,D(!1));var b=p[y],x=i(b,y),S=h?null:l.get(x);S?(S.v&&Tn(S.v,b),S.i&&Tn(S.i,y),v&&u.unskip_effect(S.e)):(S=Oi(l,h?c:wi??=I(),b,x,y,o,t,r),h||(S.e.f|=ie),l.set(x,S)),a.add(x)}if(e===0&&s&&!d&&(h?d=B(()=>s(c)):(d=B(()=>s(wi??=I())),d.f|=ie)),e>a.size&&Ce(``,``,``),E&&e>0&&k(Je()),!h)if(m.set(u,a),v){for(let[e,t]of l)a.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u);n&&D(!0),Z(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,E&&(c=O)}function Ei(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Di(e,t,n,r,i){var o=!!(r&8),s=t.length,c=e.items,l=Ei(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(o)for(v=0;v<s;v+=1)h=t[v],g=i(h,v),_=c.get(g).e,_.f&33554432||(_.nodes?.a?.measure(),(f??=new Set).add(_));for(v=0;v<s;v+=1){if(h=t[v],g
Line truncated
|
||||
\r\f\xA0\v`];function Vi(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||Bi.includes(r[o-1]))&&(s===r.length||Bi.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function Hi(e,t=!1){var n=t?` !important;`:`;`,r=``;for(var i of Object.keys(e)){var a=e[i];a!=null&&a!==``&&(r+=` `+i+`: `+a+n)}return r}function Ui(e){return e[0]!==`-`||e[1]!==`-`?e.toLowerCase():e}function Wi(e,t){if(t){var n=``,r,i;if(Array.isArray(t)?(r=t[0],i=t[1]):r=t,e){e=String(e).replaceAll(/\s*\/\*.*?\*\/\s*/g,``).trim();var a=!1,o=0,s=!1,c=[];r&&c.push(...Object.keys(r).map(Ui)),i&&c.push(...Object.keys(i).map(Ui));var l=0,u=-1;let t=e.length;for(var d=0;d<t;d++){var f=e[d];if(s?f===`/`&&e[d-1]===`*`&&(s=!1):a?a===f&&(a=!1):f===`/`&&e[d+1]===`*`?s=!0:f===`"`||f===`'`?a=f:f===`(`?o++:f===`)`&&o--,!s&&a===!1&&o===0){if(f===`:`&&u===-1)u=d;else if(f===`;`||d===t-1){if(u!==-1){var p=Ui(e.substring(l,u).trim());if(!c.includes(p)){f!==`;`&&d++;var m=e.substring(l,d).trim();n+=` `+m+`;`}}l=d+1,u=-1}}}}return r&&(n+=Hi(r)),i&&(n+=Hi(i,!0)),n=n.trim(),n===``?null:n}return e==null?null:String(e)}function Gi(e,t,n,r,i,a){var o=e[pe];if(E||o!==n||o===void 0){var s=Vi(n,r,a);(!E||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[pe]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function Ki(e,t={},n,r){for(var i in n){var a=n[i];t[i]!==a&&(n[i]==null?e.style.removeProperty(i):e.style.setProperty(i,a,r))}}function qi(e,t,n,r){var i=e[me];if(E||i!==t){var a=Wi(t,r);(!E||a!==e.getAttribute(`style`))&&(a==null?e.removeAttribute(`style`):e.style.cssText=a),e[me]=t}else r&&(Array.isArray(r)?(Ki(e,n?.[0],r[0]),Ki(e,n?.[1],r[1],`important`)):Ki(e,n,r));return r}function Ji(e,t,r=!1){if(e.multiple){if(t==null)return;if(!n(t))return Ue();for(var i of e.options)i.selected=t.includes(Zi(i));return}for(i of e.options)if(jn(Zi(i),t)){i.selected=!0;return}(!r||t!==void 0)&&(e.selectedIndex=-1)}function Yi(e){var t=new MutationObserver(()=>{`__value`in e&&Ji(e,e.__value)});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),Jn(()=>{t.disconnect()})}function Xi(e,t,n=t){var r=new WeakSet,i=!0;Nt(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),Zi);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&Zi(o)}n(a),e.__value=a,N!==null&&r.add(N)}),$n(()=>{var a=t();if(e===document.activeElement){var o=N;if(r.has(o))return}if(Ji(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=Zi(s),n(a))}e.__value=a,i=!1}),Yi(e)}function Zi(e){return`__value`in e?e.__value:e.value}var Qi=Symbol(`class`),$i=Symbol(`style`),ea=Symbol(`is custom element`),ta=Symbol(`is html`),na=ve?`link`:`LINK`,ra=ve?`input`:`INPUT`,ia=ve?`option`:`OPTION`,aa=ve?`select`:`SELECT`,oa=ve?`progress`:`PROGRESS`;function sa(e){if(E){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;ua(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;ua(e,`checked`,null),e.checked=r}}};e[ge]=n,j(n),jt()}}function ca(e,t){var n=pa(e);n.value!==(n.value=t??void 0)&&(e.value!==t||t===0&&e.nodeName===oa)&&(e.value=t??``)}function la(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ua(e,t,n,r){var i=pa(e);E&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===na)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[ue]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&ha(e).includes(t)?e[t]=n:e.setAttribute(t,n))}function da(e,t,n,r,i=!1,a=!1){if(E&&i&&e.nodeName===ra){var o=e;(o.type===`checkbox`?`defaultChecked`:`defaultValue`)in n||sa(o)}var s=pa(e),c=s[ea],l=!s[ta];let u=E&&c;u&&D(!1);var d=t||{},f=e.nodeName===ia;for(var p in t)p in n||(n[p]=null);n.class?n.class=zi(n.class):(r||n[Qi])&&(n.class=null),n[$i]&&(n.style??=null);var m=ha(e);if(e.nodeName===ra&&`type`in n&&(`value`in n||`__value`in n)){var h=n.type;(h!==d.type||h===void 0&&e.hasAttribute(`type`))&&(d.type=h,ua(e,`type`,h,a))}for(let i in n){let o=n[i];if(f&&i===`value`&&o==null){e.value=e.__value=``,d[i]=o;continue}if(i===`class`){Gi(e,e.namespaceURI===`http://www.w3.org/1999/xhtml`,o,r,t?.[Qi],n[Qi]),d[i]=o,d[Qi]=n[Qi];continue}if(i===`style`){qi(e,o,t?.[$i],n[$i]),d[i]=o,d[$i]=n[$i];continue}var g=d[i];if(!(o===g&&!(o===void 0&&e.hasAttribute(i)))){d[i]=o;var _=i[0]+i[1];if(_!==`$$`)if(_===`on`){let t={},n=`$$`+i,r=i.slice(2);var v=Br(r);if(Rr(r)&&(r=r.slice(0,-7),t.capture=!0),!v&&g){if(o!=null)continue;e.removeEventListener(r,d[n],t),d[n]=null}if(v)Zr(r,e,o),Qr([r]);else if(o!=null){function a(e){d[i].call(this,e)}d[n]=Yr(r,e,a,t)}}else if(i===`style`)ua(e,i,o);else if(i===`autofocus`)Ot(e,!!o);else if(!c&&(i===`__value`||i===`value`&&o!=null))
Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,B as t,K as n,M as r,N as i,Q as a,W as o,Z as s,at as c,dt as l,ft as u,it as d,j as f,k as p,nt as m,rt as h,tt as g,vt as _}from"./DGrvnoUE.js";import"./xihTtKlq.js";var v=class extends Map{#e=new Map;#t=d(0);#n=d(0);#r=o||-1;constructor(e){if(super(),e){for(var[t,n]of e)super.set(t,n);this.#n.v=super.size}}#i(e){return o===this.#r?d(e):h(e)}has(e){var n=this.#e,r=n.get(e);if(r===void 0)if(super.has(e))r=this.#i(0),n.set(e,r);else return t(this.#t),!1;return t(r),!0}forEach(e,t){this.#a(),super.forEach(e,t)}get(e){var n=this.#e,r=n.get(e);if(r===void 0)if(super.has(e))r=this.#i(0),n.set(e,r);else{t(this.#t);return}return t(r),super.get(e)}set(e,t){var n=this.#e,r=n.get(e),i=super.get(e),a=super.set(e,t),o=this.#t;if(r===void 0)r=this.#i(0),n.set(e,r),m(this.#n,super.size),g(o);else if(i!==t){g(r);var s=o.reactions===null?null:new Set(o.reactions);(s===null||!r.reactions?.every(e=>s.has(e)))&&g(o)}return a}delete(e){var t=this.#e,n=t.get(e),r=super.delete(e);return n!==void 0&&(t.delete(e),m(n,-1)),r&&(m(this.#n,super.size),g(this.#t)),r}clear(){if(super.size!==0){super.clear();var e=this.#e;m(this.#n,0);for(var t of e.values())m(t,-1);g(this.#t),e.clear()}}#a(){t(this.#t);var e=this.#e;if(this.#n.v!==e.size){for(var n of super.keys())if(!e.has(n)){var r=this.#i(0);e.set(n,r)}}for([,r]of this.#e)t(r)}keys(){return t(this.#t),super.keys()}values(){return this.#a(),super.values()}entries(){return this.#a(),super.entries()}[Symbol.iterator](){return this.entries()}get size(){return t(this.#n),super.size}},y=i(`<span class="text-red-700 dark:text-red-400 text-xs"> </span>`);function b(i,o){u(o,!0);var d=r(),m=a(d),h=t=>{var r=y(),i=s(r,!0);_(r),n(t=>e(i,t),[()=>o.errors.get(o.key)]),f(t,r)},g=c(()=>o.errors.has(o.key));p(m,e=>{t(g)&&e(h)}),f(i,d),l()}export{v as n,b as t};
|
||||
@@ -1 +0,0 @@
|
||||
import"./D3up1JC1.js";
|
||||
@@ -0,0 +1 @@
|
||||
import{gt as e}from"./DGrvnoUE.js";e();
|
||||
@@ -0,0 +1 @@
|
||||
var e=e=>{e.focus()};export{e as t};
|
||||
@@ -0,0 +1 @@
|
||||
import{t as e}from"./DtfiK5fE.js";var t={createChat:(t,n)=>e.post(`/chat/${t}`,n),createChatMessage:(t,n,r)=>e.postStream(`/chat/${t}/${n}`,r),allChats:(t,n,r,i)=>e.get(`/chat/${t}/all/${n}${r||i?`?${[r&&`q=${encodeURIComponent(r)}`,i&&`task=${i}`].filter(Boolean).join(`&`)}`:``}`),getChat:(t,n)=>e.get(`/chat/${t}/${n}`),subscribe:(t,n)=>e.get(`/chat/${t}/${n}/subscribe`),deleteChat:(t,n)=>e.delete(`/chat/${t}/${n}`)};export{t};
|
||||
@@ -0,0 +1 @@
|
||||
import{B as e,H as t,V as n,ct as r,it as i,nt as a,r as o,t as s}from"./DGrvnoUE.js";var c=class{constructor(e,t){this.status=e,this.body=typeof t==`string`?{message:t}:t||{message:`Error: ${e}`}}toString(){return JSON.stringify(this.body)}},l=class{constructor(e,t){try{new Headers({location:t})}catch{throw Error(`Invalid redirect location ${JSON.stringify(t)}: this string contains characters that cannot be used in HTTP headers`)}this.status=e,this.location=t}},u=class extends Error{constructor(e,t,n){super(n),this.status=e,this.text=t}};new URL(`sveltekit-internal://`);function d(e,t){return e===`/`||t===`ignore`?e:t===`never`?e.endsWith(`/`)?e.slice(0,-1):e:t===`always`&&!e.endsWith(`/`)?e+`/`:e}function f(e){return e.split(`%25`).map(decodeURI).join(`%25`)}function p(e){for(let t in e)e[t]=decodeURIComponent(e[t]);return e}function m({href:e}){return e.split(`#`)[0]}function h(e,t,n,r=!1){let i=new URL(e);Object.defineProperty(i,"searchParams",{value:new Proxy(i.searchParams,{get(e,r){if(r===`get`||r===`getAll`||r===`has`)return(t,...i)=>(n(t),e[r](t,...i));t();let i=Reflect.get(e,r);return typeof i==`function`?i.bind(e):i}}),enumerable:!0,configurable:!0});let a=[`href`,`pathname`,`search`,`toString`,`toJSON`];r&&a.push(`hash`);for(let n of a)Object.defineProperty(i,n,{get(){return t(),e[n]},enumerable:!0,configurable:!0});return i}function g(){}function _(...e){let t=5381;for(let n of e)if(typeof n==`string`){let e=n.length;for(;e;)t=t*33^n.charCodeAt(--e)}else if(ArrayBuffer.isView(n)){let e=new Uint8Array(n.buffer,n.byteOffset,n.byteLength),r=e.length;for(;r;)t=t*33^e[--r]}else throw TypeError(`value must be a string or TypedArray`);return(t>>>0).toString(36)}new TextEncoder;function v(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}var ee=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:t?.method||`GET`)!==`GET`&&y.delete(x(e)),ee(e,t));var y=new Map;function b(e,t){let n=x(e,t),r=document.querySelector(n);if(r?.textContent){r.remove();let{body:e,...t}=JSON.parse(r.textContent);r.getAttribute(`data-b64`)!==null&&(e=v(e));let i=r.getAttribute(`data-ttl`);return i&&y.set(n,{body:e,init:t,ttl:1e3*Number(i)}),Promise.resolve(new Response(e,t))}return window.fetch(e,t)}function te(e,t,n){if(y.size>0){let t=x(e,n),r=y.get(t);if(r){if(performance.now()<r.ttl&&[`default`,`force-cache`,`only-if-cached`,void 0].includes(n?.cache))return new Response(r.body,r.init);y.delete(t)}}return window.fetch(t,n)}function x(e,t){let n=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t?.headers||t?.body){let e=[];t.headers&&e.push([...new Headers(t.headers)].join(`,`)),t.body&&(typeof t.body==`string`||ArrayBuffer.isView(t.body))&&e.push(t.body),n+=`[data-hash="${_(...e)}"]`}return n}var ne=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/,re=/^\/\((?:[^)]+)\)$/;function ie(e){let t=[];return{pattern:e===`/`||re.test(e)?/^\/$/:RegExp(`^${oe(e).map(e=>{let n=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(e);if(n)return t.push({name:n[1],matcher:n[2],optional:!1,rest:!0,chained:!0}),`(?:/([^]*))?`;let r=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(e);if(r)return t.push({name:r[1],matcher:r[2],optional:!0,rest:!1,chained:!0}),`(?:/([^/]+))?`;if(!e)return;let i=e.split(/\[(.+?)\](?!\])/);return`/`+i.map((e,n)=>{if(n%2){if(e.startsWith(`x+`))return ce(String.fromCharCode(parseInt(e.slice(2),16)));if(e.startsWith(`u+`))return ce(String.fromCharCode(...e.slice(2).split(`-`).map(e=>parseInt(e,16))));let[,r,a,o,s]=ne.exec(e);return t.push({name:o,matcher:s,optional:!!r,rest:!!a,chained:a?n===1&&i[0]===``:!1}),a?`([^]*?)`:r?`([^/]*)?`:`([^/]+?)`}return ce(e)}).join(``)}).join(``)}/?$`),params:t}}function ae(e){return e!==``&&!/^\([^)]+\)$/.test(e)}function oe(e){return e.slice(1).split(`/`).filter(ae)}function se(e,t,n){let r={},i=e.slice(1),a=i.filter(e=>e!==void 0),o=0;for(let e=0;e<t.length;e+=1){let s=t[e],c=i[e-o];if(s.chained&&s.rest&&o&&(c=i.slice(e-o,e+1).filter(e=>e).join(`/`),o=0),c===void 0)if(s.rest)c=``;else continue;if(!s.matcher||n[s.matcher](c)){r[s.name]=c;let n=t[e+1],l=i[e+1];n&&!n.rest&&n.optional&&l&&s.chained&&(o=0),!n&&!l&&Object.keys(r).length===a.length&&(o=0);continue}if(s.optional&&s.chained){o++;continue}return}if(!o)return r}function ce(e){return e.normalize().replace(/[[\]]/g,`\\$&`).replace(/%/g,`%25`).replace(/\//g,`%2[Ff]`).replace(/\?/g,`%3[Ff]`).replace(/#/g,`%23`).replace(/[.*+?^${}()|\\]/g,`\\$&`)}function le({nodes:e,server_loads:t,dictionary:n,matchers:r}){let i=new Set(t);return Object.entries(n).map(([t,[n,i,s]])=>{let{pattern:c,params:l}=ie(t),u={id:t,exec:e=>{let t=c.exec(e);if(t)return se(t,l,r)},errors:[1,...s||[]].map(t=>e[t]),layouts:[0,...i||[]].map(o),leaf:a(n)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function a(t){let n=t<0;return n&&(t=~t),[n,e[t]]}function o(t){return t===void 0?t:[i.has(t),e[t]]}}function ue(e,t=JSON.parse){try{return t(sessionStorage
Line truncated
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{t as e}from"./BqkZ0BZ5.js";var t={providerTypes:()=>e.get(`/provider/providerTypes`),getProviders:t=>e.get(`/provider/${t}`),getProvider:(t,n)=>e.get(`/provider/${t}/${n}`),createProvider:(t,n)=>e.post(`/provider/${t}`,n),updateProvider:(t,n)=>e.put(`/provider/${t}`,n),deleteProvider:(t,n)=>e.delete(`/provider/${t}/${n}`),getAvailableModels:(t,n)=>e.post(`/provider/${t}/avalable-models`,n),getProvidersModels:t=>e.get(`/provider/${t}/models`)};export{t};
|
||||
import{t as e}from"./DtfiK5fE.js";var t={providerTypes:()=>e.get(`/provider/providerTypes`),getProviders:t=>e.get(`/provider/${t}`),getProvider:(t,n)=>e.get(`/provider/${t}/${n}`),createProvider:(t,n)=>e.post(`/provider/${t}`,n),updateProvider:(t,n)=>e.put(`/provider/${t}`,n),deleteProvider:(t,n)=>e.delete(`/provider/${t}/${n}`),getAvailableModels:(t,n)=>e.post(`/provider/${t}/avalable-models`,n),getProvidersModels:t=>e.get(`/provider/${t}/models`)};export{t};
|
||||
@@ -0,0 +1 @@
|
||||
import{t as e}from"./DtfiK5fE.js";var t={register:t=>e.post(`/auth/register`,t),status:()=>e.get(`/auth/status`),login:t=>e.post(`/auth/login`,t),oidcProviders:()=>e.get(`/auth/oidc/providers`,{auth:!1}),oidcLoginUrl:e=>`/api/auth/oidc/login?provider=${encodeURIComponent(e)}`};export{t};
|
||||
@@ -0,0 +1,4 @@
|
||||
import{$ as e,A as t,C as n,I as r,K as i,L as a,M as o,N as s,Q as c,R as l,X as u,Z as d,a as f,dt as p,et as m,ft as h,j as g,k as _,vt as v}from"./DGrvnoUE.js";import"./xihTtKlq.js";import{t as y}from"./DtfiK5fE.js";var b={listTasks:e=>y.get(`/task/${e}`),updateTask:(e,t,n)=>y.put(`/task/${e}/${t}`,n),deleteTask:(e,t)=>y.delete(`/task/${e}/${t}`)},x=0,S=m([]),C=(e,t)=>{S.push({id:x++,type:e,text:t,status:`appearing`})},w=e=>e;function T(e){let t=e-1;return t*t*t+1}function E(e,{delay:t=0,duration:n=400,easing:r=w}={}){let i=+getComputedStyle(e).opacity;return{delay:t,duration:n,easing:r,css:e=>`opacity: ${e*i}`}}function D(e,{delay:t=0,duration:n=400,easing:r=T,start:i=0,opacity:a=0}={}){let o=getComputedStyle(e),s=+o.opacity,c=o.transform===`none`?``:o.transform,l=1-i,u=s*(1-a);return{delay:t,duration:n,easing:r,css:(e,t)=>`
|
||||
transform: ${c} scale(${1-l*t});
|
||||
opacity: ${s-u*t}
|
||||
`}}var O=s(`<div class="fixed inset-0 z-50 flex items-center justify-center p-4"><button type="button" class="absolute inset-0 cursor-default bg-black/50" aria-label="Fermer"></button> <div class="relative w-full max-w-sm rounded-lg bg-stone-50 p-4 shadow-xl dark:bg-stone-900" role="dialog" aria-modal="true" aria-labelledby="confirm-dialog-title"><div class="flex items-center gap-3"><div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-red-100 text-red-600 dark:bg-red-950 dark:text-red-400"><i class="icon-trash-empty"></i></div> <h2 id="confirm-dialog-title" class="text-base font-bold"> </h2></div> <p class="mt-3 text-sm text-stone-600 dark:text-stone-400"> </p> <div class="mt-4 flex justify-end gap-2"><button type="button" class="bt small"> </button> <button type="button" class="bt small danger"> </button></div></div></div>`);function k(r,s){h(s,!0);let m=f(s,`open`,3,!1),y=f(s,`title`,3,``),b=f(s,`message`,3,``),x=f(s,`confirmLabel`,3,`Confirmer`),S=f(s,`cancelLabel`,3,`Annuler`),C=()=>s.onCancel?.(),w=e=>{m()&&e.key===`Escape`&&C()};var T=o();l(`keydown`,u,w);var k=c(T),A=r=>{var o=O(),c=d(o),l=e(c,2),u=d(l),f=e(d(u),2),p=d(f,!0);v(f),v(u);var m=e(u,2),h=d(m,!0);v(m);var _=e(m,2),w=d(_),T=d(w,!0);v(w);var k=e(w,2),A=d(k,!0);v(k),v(_),v(l),v(o),i(()=>{t(p,y()),t(h,b()),t(T,S()),t(A,x())}),a(`click`,c,C),n(3,c,()=>E,()=>({duration:150})),a(`click`,w,C),a(`click`,k,function(...e){s.onConfirm?.apply(this,e)}),n(3,l,()=>D,()=>({duration:150,start:.95})),g(r,o)};_(k,e=>{m()&&e(A)}),g(r,T),p()}r([`click`]);export{C as n,b as r,k as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{J as e}from"./39JK5HWF.js";import{t}from"./D3up1JC1.js";import"./B5isPFOt.js";var n=e({user:null,status:`loading`}),r=()=>{localStorage.removeItem(`accessToken`),n.status=`unauthenticated`},i=()=>localStorage.getItem(`accessToken`),a=e=>{localStorage.setItem(`accessToken`,e)},o=`/api`,s=class extends Error{status;body;constructor(e,t){super(`API Error ${e}`),this.status=e,this.body=t}};async function c(e,n={}){let{auth:a=!0,headers:c,...u}=n,d={"Content-Type":`application/json`,...c},f=new Headers(d);if(a){let e=i();e&&f.append(`Authorization`,`Bearer ${e}`)}let p=await fetch(`${o}${e}`,{...u,headers:f});if(p.status===401&&(r(),t(`/login`)),!p.ok)throw new s(p.status,await l(p));if(p.status!==204)return p.json()}async function l(e){try{return await e.json()}catch{return null}}var u={get:(e,t)=>c(e,{...t,method:`GET`}),post:(e,t,n)=>c(e,{...n,method:`POST`,body:JSON.stringify(t)}),put:(e,t,n)=>c(e,{...n,method:`PUT`,body:JSON.stringify(t)}),delete:(e,t)=>c(e,{...t,method:`DELETE`})};export{a as i,n,r,u as t};
|
||||
@@ -0,0 +1 @@
|
||||
import"./BZuNOv8h.js";
|
||||
@@ -0,0 +1 @@
|
||||
import{et as e}from"./DGrvnoUE.js";var t=e({spaces:[],current:``});export{t};
|
||||
@@ -1 +0,0 @@
|
||||
import"./39JK5HWF.js";import{a as e,r as t}from"./D3up1JC1.js";var n={get data(){return e.data},get error(){return e.error},get form(){return e.form},get params(){return e.params},get route(){return e.route},get state(){return e.state},get status(){return e.status},get url(){return e.url}};t.updated.check;var r=n;export{r as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{F as e,I as t,P as n,Q as r,X as i,nt as a,r as o,t as s}from"./39JK5HWF.js";var c=class{constructor(e,t){this.status=e,this.body=typeof t==`string`?{message:t}:t||{message:`Error: ${e}`}}toString(){return JSON.stringify(this.body)}},l=class{constructor(e,t){try{new Headers({location:t})}catch{throw Error(`Invalid redirect location ${JSON.stringify(t)}: this string contains characters that cannot be used in HTTP headers`)}this.status=e,this.location=t}},u=class extends Error{constructor(e,t,n){super(n),this.status=e,this.text=t}};new URL(`sveltekit-internal://`);function d(e,t){return e===`/`||t===`ignore`?e:t===`never`?e.endsWith(`/`)?e.slice(0,-1):e:t===`always`&&!e.endsWith(`/`)?e+`/`:e}function f(e){return e.split(`%25`).map(decodeURI).join(`%25`)}function p(e){for(let t in e)e[t]=decodeURIComponent(e[t]);return e}function m({href:e}){return e.split(`#`)[0]}function h(e,t,n,r=!1){let i=new URL(e);Object.defineProperty(i,"searchParams",{value:new Proxy(i.searchParams,{get(e,r){if(r===`get`||r===`getAll`||r===`has`)return(t,...i)=>(n(t),e[r](t,...i));t();let i=Reflect.get(e,r);return typeof i==`function`?i.bind(e):i}}),enumerable:!0,configurable:!0});let a=[`href`,`pathname`,`search`,`toString`,`toJSON`];r&&a.push(`hash`);for(let n of a)Object.defineProperty(i,n,{get(){return t(),e[n]},enumerable:!0,configurable:!0});return i}function g(){}function _(...e){let t=5381;for(let n of e)if(typeof n==`string`){let e=n.length;for(;e;)t=t*33^n.charCodeAt(--e)}else if(ArrayBuffer.isView(n)){let e=new Uint8Array(n.buffer,n.byteOffset,n.byteLength),r=e.length;for(;r;)t=t*33^e[--r]}else throw TypeError(`value must be a string or TypedArray`);return(t>>>0).toString(36)}new TextEncoder;function v(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}var ee=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:t?.method||`GET`)!==`GET`&&y.delete(x(e)),ee(e,t));var y=new Map;function b(e,t){let n=x(e,t),r=document.querySelector(n);if(r?.textContent){r.remove();let{body:e,...t}=JSON.parse(r.textContent);r.getAttribute(`data-b64`)!==null&&(e=v(e));let i=r.getAttribute(`data-ttl`);return i&&y.set(n,{body:e,init:t,ttl:1e3*Number(i)}),Promise.resolve(new Response(e,t))}return window.fetch(e,t)}function te(e,t,n){if(y.size>0){let t=x(e,n),r=y.get(t);if(r){if(performance.now()<r.ttl&&[`default`,`force-cache`,`only-if-cached`,void 0].includes(n?.cache))return new Response(r.body,r.init);y.delete(t)}}return window.fetch(t,n)}function x(e,t){let n=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t?.headers||t?.body){let e=[];t.headers&&e.push([...new Headers(t.headers)].join(`,`)),t.body&&(typeof t.body==`string`||ArrayBuffer.isView(t.body))&&e.push(t.body),n+=`[data-hash="${_(...e)}"]`}return n}var ne=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/,re=/^\/\((?:[^)]+)\)$/;function ie(e){let t=[];return{pattern:e===`/`||re.test(e)?/^\/$/:RegExp(`^${oe(e).map(e=>{let n=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(e);if(n)return t.push({name:n[1],matcher:n[2],optional:!1,rest:!0,chained:!0}),`(?:/([^]*))?`;let r=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(e);if(r)return t.push({name:r[1],matcher:r[2],optional:!0,rest:!1,chained:!0}),`(?:/([^/]+))?`;if(!e)return;let i=e.split(/\[(.+?)\](?!\])/);return`/`+i.map((e,n)=>{if(n%2){if(e.startsWith(`x+`))return ce(String.fromCharCode(parseInt(e.slice(2),16)));if(e.startsWith(`u+`))return ce(String.fromCharCode(...e.slice(2).split(`-`).map(e=>parseInt(e,16))));let[,r,a,o,s]=ne.exec(e);return t.push({name:o,matcher:s,optional:!!r,rest:!!a,chained:a?n===1&&i[0]===``:!1}),a?`([^]*?)`:r?`([^/]*)?`:`([^/]+?)`}return ce(e)}).join(``)}).join(``)}/?$`),params:t}}function ae(e){return e!==``&&!/^\([^)]+\)$/.test(e)}function oe(e){return e.slice(1).split(`/`).filter(ae)}function se(e,t,n){let r={},i=e.slice(1),a=i.filter(e=>e!==void 0),o=0;for(let e=0;e<t.length;e+=1){let s=t[e],c=i[e-o];if(s.chained&&s.rest&&o&&(c=i.slice(e-o,e+1).filter(e=>e).join(`/`),o=0),c===void 0)if(s.rest)c=``;else continue;if(!s.matcher||n[s.matcher](c)){r[s.name]=c;let n=t[e+1],l=i[e+1];n&&!n.rest&&n.optional&&l&&s.chained&&(o=0),!n&&!l&&Object.keys(r).length===a.length&&(o=0);continue}if(s.optional&&s.chained){o++;continue}return}if(!o)return r}function ce(e){return e.normalize().replace(/[[\]]/g,`\\$&`).replace(/%/g,`%25`).replace(/\//g,`%2[Ff]`).replace(/\?/g,`%3[Ff]`).replace(/#/g,`%23`).replace(/[.*+?^${}()|\\]/g,`\\$&`)}function le({nodes:e,server_loads:t,dictionary:n,matchers:r}){let i=new Set(t);return Object.entries(n).map(([t,[n,i,s]])=>{let{pattern:c,params:l}=ie(t),u={id:t,exec:e=>{let t=c.exec(e);if(t)return se(t,l,r)},errors:[1,...s||[]].map(t=>e[t]),layouts:[0,...i||[]].map(o),leaf:a(n)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function a(t){let n=t<0;return n&&(t=~t),[n,e[t]]}function o(t){return t===void 0?t:[i.has(t),e[t]]}}function ue(e,t=JSON.parse){try{return t(sessionStorage[e
Line truncated
|
||||
@@ -1,6 +0,0 @@
|
||||
import{$ as e,A as t,B as n,D as r,E as i,G as a,I as o,J as s,K as c,L as l,M as u,N as d,O as f,P as p,Q as m,S as h,T as g,V as _,W as v,X as y,_ as b,a as x,at as ee,b as S,c as te,d as C,dt as w,f as ne,h as re,it as ie,j as ae,k as oe,l as se,lt as ce,m as le,n as ue,o as de,q as T,r as fe,u as pe,w as E,x as me}from"./39JK5HWF.js";import"./D3up1JC1.js";import"./xihTtKlq.js";var he=Math.min,D=Math.max,ge=Math.round,O=Math.floor,k=e=>({x:e,y:e}),A={left:`right`,right:`left`,bottom:`top`,top:`bottom`};function _e(e,t,n){return D(e,he(t,n))}function j(e,t){return typeof e==`function`?e(t):e}function M(e){return e.split(`-`)[0]}function N(e){return e.split(`-`)[1]}function P(e){return e===`x`?`y`:`x`}function ve(e){return e===`y`?`height`:`width`}function F(e){let t=e[0];return t===`t`||t===`b`?`y`:`x`}function I(e){return P(F(e))}function ye(e,t,n){n===void 0&&(n=!1);let r=N(e),i=I(e),a=ve(i),o=i===`x`?r===(n?`end`:`start`)?`right`:`left`:r===`start`?`bottom`:`top`;return t.reference[a]>t.floating[a]&&(o=Ee(o)),[o,Ee(o)]}function be(e){let t=Ee(e);return[L(e),t,L(t)]}function L(e){return e.includes(`start`)?e.replace(`start`,`end`):e.replace(`end`,`start`)}var xe=[`left`,`right`],Se=[`right`,`left`],R=[`top`,`bottom`],Ce=[`bottom`,`top`];function we(e,t,n){switch(e){case`top`:case`bottom`:return n?t?Se:xe:t?xe:Se;case`left`:case`right`:return t?R:Ce;default:return[]}}function Te(e,t,n,r){let i=N(e),a=we(M(e),n===`start`,r);return i&&(a=a.map(e=>e+`-`+i),t&&(a=a.concat(a.map(L)))),a}function Ee(e){let t=M(e);return A[t]+e.slice(t.length)}function De(e){return{top:e.top??0,right:e.right??0,bottom:e.bottom??0,left:e.left??0}}function Oe(e){return typeof e==`number`?{top:e,right:e,bottom:e,left:e}:De(e)}function ke(e){let{x:t,y:n,width:r,height:i}=e;return{width:r,height:i,top:n,left:t,right:t+r,bottom:n+i,x:t,y:n}}function Ae(e,t,n){let{reference:r,floating:i}=e,a=F(t),o=I(t),s=ve(o),c=M(t),l=a===`y`,u=r.x+r.width/2-i.width/2,d=r.y+r.height/2-i.height/2,f=r[s]/2-i[s]/2,p;switch(c){case`top`:p={x:u,y:r.y-i.height};break;case`bottom`:p={x:u,y:r.y+r.height};break;case`right`:p={x:r.x+r.width,y:d};break;case`left`:p={x:r.x-i.width,y:d};break;default:p={x:r.x,y:r.y}}let m=N(t);return m&&(p[o]+=f*(m===`end`?1:-1)*(n&&l?-1:1)),p}async function je(e,t){t===void 0&&(t={});let{x:n,y:r,platform:i,rects:a,elements:o,strategy:s}=e,{boundary:c=`clippingAncestors`,rootBoundary:l=`viewport`,elementContext:u=`floating`,altBoundary:d=!1,padding:f=0}=j(t,e),p=Oe(f),m=o[d?u===`floating`?`reference`:`floating`:u],h=ke(await i.getClippingRect({element:await(i.isElement==null?void 0:i.isElement(m))??!0?m:m.contextElement||await(i.getDocumentElement==null?void 0:i.getDocumentElement(o.floating)),boundary:c,rootBoundary:l,strategy:s})),g=u===`floating`?{x:n,y:r,width:a.floating.width,height:a.floating.height}:a.reference,_=await(i.getOffsetParent==null?void 0:i.getOffsetParent(o.floating)),v=await(i.isElement==null?void 0:i.isElement(_))&&await(i.getScale==null?void 0:i.getScale(_))||{x:1,y:1},y=ke(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:o,rect:g,offsetParent:_,strategy:s}):g);return{top:(h.top-y.top+p.top)/v.y,bottom:(y.bottom-h.bottom+p.bottom)/v.y,left:(h.left-y.left+p.left)/v.x,right:(y.right-h.right+p.right)/v.x}}var Me=50,Ne=async(e,t,n)=>{let{placement:r=`bottom`,strategy:i=`absolute`,middleware:a=[],platform:o}=n,s=o.detectOverflow?o:{...o,detectOverflow:je},c=await(o.isRTL==null?void 0:o.isRTL(t)),l=await o.getElementRects({reference:e,floating:t,strategy:i}),{x:u,y:d}=Ae(l,r,c),f=r,p=0,m={};for(let n=0;n<a.length;n++){let h=a[n];if(!h)continue;let{name:g,fn:_}=h,{x:v,y,data:b,reset:x}=await _({x:u,y:d,initialPlacement:r,placement:f,strategy:i,middlewareData:m,rects:l,platform:s,elements:{reference:e,floating:t}});u=v??u,d=y??d,m[g]={...m[g],...b},x&&p<Me&&(p++,typeof x==`object`&&(x.placement&&(f=x.placement),x.rects&&(l=x.rects===!0?await o.getElementRects({reference:e,floating:t,strategy:i}):x.rects),{x:u,y:d}=Ae(l,f,c)),n=-1)}return{x:u,y:d,placement:f,strategy:i,middlewareData:m}},Pe=function(e){return e===void 0&&(e={}),{name:`flip`,options:e,async fn(t){var n;let{placement:r,middlewareData:i,rects:a,initialPlacement:o,platform:s,elements:c}=t,{mainAxis:l=!0,crossAxis:u=!0,fallbackPlacements:d,fallbackStrategy:f=`bestFit`,fallbackAxisSideDirection:p=`none`,flipAlignment:m=!0,...h}=j(e,t);if((n=i.arrow)!=null&&n.alignmentOffset)return{};let g=M(r),_=F(o),v=M(o)===o,y=await(s.isRTL==null?void 0:s.isRTL(c.floating)),b=d||(v||!m?[Ee(o)]:be(o)),x=p!==`none`;!d&&x&&b.push(...Te(o,m,p,y));let ee=[o,...b],S=await s.detectOverflow(t,h),te=[],C=i.flip?.overflows||[];if(l&&te.push(S[g]),u){let e=ye(r,a,y);te.push(S[e[0]],S[e[1]])}if(C=[...C,{placement:r,overflows:te}],!te.every(e=>e<=0)){let e=(i.flip?.index||0)+1,t=ee[e];if(t&&(u!==`alignment`||_===F(t)||C.every(e=>F(e.placement)!==_||e.overflows[0]>0)))return{data:{index:e,overflows:C},reLine truncated
|
||||
3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0
|
||||
1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502
|
||||
0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0
|
||||
0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"></path></svg>`);function Mt(e){var t=$();i(e,t)}var Nt=oe(`<svg width="100%" height="100%" viewBox="-2 -2 50 50" focusable="false" aria-hidden="true" role="presentation"><path fill="currentColor" d="M34.923,37.251L24,26.328L13.077,37.251L9.436,33.61l10.923-10.923L9.436,11.765l3.641-3.641L24,19.047L34.923,8.124
|
||||
l3.641,3.641L27.641,22.688L38.564,33.61L34.923,37.251z"></path></svg>`);function Pt(e){var t=Nt();i(e,t)}var Ft=oe(`<svg class="loading" viewBox="25 25 50 50"><circle class="circle_path" cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10"></circle></svg>`);function It(e){var t=Ft();i(e,t)}var Lt=f(`<div class="list-item" tabindex="-1" role="none"><div><!></div></div>`),Rt=f(`<div class="empty">No options</div>`),zt=f(`<div role="none"><!> <!> <!></div>`),Bt=f(`<span id="aria-selection"> </span> <span id="aria-context"> </span>`,1),Vt=f(`<div class="multi-item-clear"><!></div>`),Ht=f(`<div role="none"><span class="multi-item-text"><!></span> <!></div>`),Ut=f(`<div><!></div>`),Wt=f(`<div class="icon loading" aria-hidden="true"><!></div>`),Gt=f(`<button type="button" class="icon clear-select"><!></button>`),Kt=f(`<div class="icon chevron" aria-hidden="true"><!></div>`),qt=f(`<input type="hidden"/>`),Jt=f(`<select class="required" required="" tabindex="-1" aria-hidden="true"></select>`),Yt=f(`<div role="none"><!> <span aria-live="polite" aria-atomic="false" aria-relevant="additions text" class="a11y-text"><!></span> <div class="prepend"><!></div> <div class="value-container"><!> <input/></div> <div class="indicators"><!> <!> <!></div> <!> <!></div>`);function Xt(o,f){ee(f,!0);let ae,oe=x(f,`valueMode`,3,`item`),ce=x(f,`filter`,3,At),he=x(f,`getItems`,3,jt),D=x(f,`id`,3,null),ge=x(f,`name`,3,null),O=x(f,`container`,15,null),k=x(f,`input`,15,null),A=x(f,`multiple`,3,!1),_e=x(f,`multiFullItemClearable`,3,!1),j=x(f,`disabled`,3,!1),M=x(f,`focused`,15,!1),N=x(f,`value`,15),P=x(f,`filterText`,15,``),ve=x(f,`placeholder`,3,`Please select`),F=x(f,`placeholderAlwaysShow`,3,!1),I=x(f,`items`,31,()=>s([])),ye=x(f,`label`,3,`label`),be=x(f,`itemFilter`,3,(e,t,n)=>`${e}`.toLowerCase().includes(t.toLowerCase())),L=x(f,`groupBy`,3,void 0),xe=x(f,`groupFilter`,3,e=>e),Se=x(f,`groupHeaderSelectable`,3,!1),R=x(f,`itemId`,3,`value`),Ce=x(f,`loadOptions`,3,void 0),we=x(f,`containerStyles`,3,``),Te=x(f,`hasError`,3,!1),Ee=x(f,`filterSelectedItems`,3,!0),De=x(f,`required`,3,!1),Oe=x(f,`closeListOnChange`,3,!0),ke=x(f,`clearFilterTextOnBlur`,3,!0),Ae=x(f,`createGroupHeaderItem`,3,(e,t)=>({value:e,[ye()]:e})),je=x(f,`searchable`,3,!0),Me=x(f,`inputStyles`,3,``),Ne=x(f,`clearable`,3,!0),Pe=x(f,`loading`,15,!1),z=x(f,`listOpen`,15,!1),Fe=x(f,`debounce`,3,(e,t=1)=>{clearTimeout(ae),ae=setTimeout(e,t)}),Ie=x(f,`debounceWait`,3,300),Le=x(f,`hideEmptyState`,3,!1),Re=x(f,`inputAttributes`,19,()=>({})),ze=x(f,`listAutoWidth`,3,!0),B=x(f,`showChevron`,3,!1),V=x(f,`listOffset`,3,5),H=x(f,`hoverItemIndex`,15,0),U=x(f,`floatingConfig`,19,()=>({})),W=x(f,`class`,3,``),Be=x(f,`ariaValues`,3,e=>`Option ${e}, selected.`),Ve=x(f,`ariaListOpen`,3,(e,t)=>`You are currently focused on option ${e}. There are ${t} results available.`),He=x(f,`ariaFocused`,3,()=>`Select is focused, type to refine list, press down to open the menu.`);function Ue(){return p(Q)}let G=m(void 0),We,K,Ge,Ke,q=m(null),qe={},Je=e(()=>{if(Re()||!je()){let e=Object.assign({autocapitalize:`none`,autocomplete:`off`,autocorrect:`off`,spellcheck:!1,tabindex:0,type:`text`,"aria-autocomplete":`list`},Re());D()&&(e.id=D()),je()||(e.readonly=!0),JSON.stringify(e)!==JSON.stringify(qe)&&(qe=e)}return qe});function J(e){return e==null?e:typeof e==`object`?e[R()]:e}function Ye(e){return Array.isArray(e)&&e.length>0&&typeof e[0]!=`object`}let Y=e(()=>oe()===`id`||Ye(I()));function Xe(e){return p(Y)?J(e):e}function X(e){if(e==null)return e;if(typeof e==`object`)return e[ye()];let t=I()?.find(t=>t[R()]===e);return t?t[ye()]:e}function Ze(){return N()==null?null:p(Y)?A()&&Array.isArray(N())?JSON.stringify(N()):`${N()}`:JSON.stringify(N())}function Qe(e){return e.map((e,t)=>({index:t,value:e,label:`${e}`}))}function $e(e){if(!L())return e;let t=[],n={};e.forEach(e=>{let r=L()(e)??``;t.includes(r)||(t.push(r),n[r]=[],r&&n[r].push(Object.assign(Ae()(r,e),{id:r,groupHeader:!0,selectable:Se()}))),n[r].push(Object.assign({groupItem:!!r},e))});let r=[];return xe()(t).forEach(e=>{n[e]&&r.push(...n[e])}),r}function et(){if(A()){JSON.stringify(N())!==JSON.stringify(We)&&(!N()||gt())&&f.oninput?.(N());return}N()?(!We||JSON.stringify(J(N()))!==JSON.stringify(J(We)))&&f.oninput?.(N()):We&&f.oninput?.(N())}function tt(){Ge=!0,N()&&(Array.isArray(N())||N([N()]))}function nt(){Ge=!1,N()&&N(void 0)}function rt(){Z(p(Q).findIndex(e=>e[R()]===J(N())),!0)}function Z(e=0,t){let n=e<0?0:e;H()!==n&&H(n),!t&&L()&&p(Q)[H()]&&!p(Q)[H()].selectable&&en(1)}function it(){!Ce()&&P().length===0||(Ce()?Fe()(async function(){Pe(!0);let e=await he()({onerror:f.onerror,onloaded:f.onloaded,loadOptions:Ce(),convertStringItemsToObjects:Qe,filterText:P()});e?(Pe(e.loading),z(z()?e.listOpen:P().length>0),M(z()&&e.focused),I(L()?$e(e.filteredItems):e.filteredItems)):(Pe(!1),M(!0),z(!0))},Ie()):(z()||z(!0),A()&&y(G,void 0)))}let at=e(()=>A()?Array.isArray(N())&&N().length>0:!!N()),ot=e(()=>pLine truncated
|
||||
@@ -1 +0,0 @@
|
||||
import{J as e}from"./39JK5HWF.js";var t=e({spaces:[],current:``});export{t};
|
||||
@@ -0,0 +1,3 @@
|
||||
var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r},n=Array.isArray,r=Array.prototype.indexOf,i=Array.prototype.includes,a=Array.from,o=Object.defineProperty,s=Object.getOwnPropertyDescriptor,c=Object.getOwnPropertyDescriptors,l=Object.prototype,u=Array.prototype,d=Object.getPrototypeOf,f=Object.isExtensible;function p(e){return typeof e==`function`}var m=()=>{};function h(e){for(var t=0;t<e.length;t++)e[t]()}function g(){var e,t;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}var _=1<<24,v=1024,y=2048,b=4096,x=8192,S=16384,ee=32768,te=1<<25,ne=65536,re=1<<18,ie=1<<19,ae=1<<20,oe=1<<25,se=65536,ce=1<<21,le=1<<22,ue=1<<23,C=Symbol(`$state`),de=Symbol(`legacy props`),fe=Symbol(``),pe=Symbol(`proxy path`),me=Symbol(`attributes`),he=Symbol(`class`),ge=Symbol(`style`),_e=Symbol(`text`),ve=Symbol(`form reset`),ye=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},be=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function xe(e){throw Error(`https://svelte.dev/e/experimental_async_required`)}function Se(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function Ce(){throw Error(`https://svelte.dev/e/missing_context`)}function we(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function Te(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function Ee(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function De(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function Oe(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function ke(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Ae(){throw Error(`https://svelte.dev/e/fork_discarded`)}function je(){throw Error(`https://svelte.dev/e/fork_timing`)}function Me(){throw Error(`https://svelte.dev/e/get_abort_signal_outside_reaction`)}function Ne(){throw Error(`https://svelte.dev/e/hydration_failed`)}function Pe(e){throw Error(`https://svelte.dev/e/lifecycle_legacy_only`)}function Fe(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ie(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function Le(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Re(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function ze(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var Be={},w=Symbol(`uninitialized`),Ve=`http://www.w3.org/1999/xhtml`,He=`http://www.w3.org/2000/svg`;function Ue(){console.warn(`https://svelte.dev/e/derived_inert`)}function We(e){console.warn(`https://svelte.dev/e/hydratable_missing_but_expected`)}function Ge(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function Ke(){console.warn(`https://svelte.dev/e/select_multiple_invalid_value`)}function qe(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var T=!1;function E(e){T=e}var D;function O(e){if(e===null)throw Ge(),Be;return D=e}function k(){return O(R(D))}function Je(e){if(T){if(R(D)!==null)throw Ge(),Be;D=e}}function Ye(e=1){if(T){for(var t=e,n=D;t--;)n=R(n);D=n}}function Xe(e=!0){for(var t=0,n=D;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else(r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=R(n);e&&n.remove(),n=i}}function Ze(e){if(!e||e.nodeType!==8)throw Ge(),Be;return e.data}function Qe(e){return e===this.v}function $e(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function et(e){return!$e(e,this.v)}var tt=!1;function nt(){tt=!0}function rt(e,t){return e.label=t,it(e.v,t),e}function it(e,t){return e?.[pe]?.(t),e}function at(e){return typeof e==`symbol`?`Symbol(${e.description})`:typeof e==`function`?`<function>`:typeof e==`object`&&e?`<object>`:String(e)}var A=null;function ot(e){A=e}function st(){let e={};return[()=>(ut(e)||Ce(),ct(e)),t=>lt(e,t)]}function ct(e){return ht(`getContext`).get(e)}function lt(e,t){return ht(`setContext`).set(e,t),t}function ut(e){return ht(`hasContext`).has(e)}function dt(){return ht(`getAllContexts`)}function ft(e,t=!1,n){A={p:A,i:!1,c:null,e:null,s:e,x:null,r:G,l:tt&&!t?{s:null,u:null,$:[]}:null}}function pt(e){var t=A,n=t.e;if(n!==null){t.e=null;for(var r of n)$n(r)}return e!==void 0&&(t.x=e),t.i=!0,A=t.p,e??{}}function mt(){return!tt||A!==null&&A.l===null}function ht(e){return A===null&&Se(e),A.c??=new Map(gt(A)||void 0)}function gt(e){let t=e.p;for(;t!==null;){let e=t.c;if(e!==null)return e;t=t.p}return null}var _t=[];function vt(){var e=_t;_t=[],h(e)}function j(e){if(_t.length===0&&!rn){var t=_t;queueMicrotask(()=>{t===_t&&vt()})}_t.push(e)}function yt(){for(;_t.length>0;)vt()}function bt(e){var t=G;if(t===null)return H.f|=ue,e;if(!(t.f&32768)&&!(t.f&4))throw e;xt(e,t)}function xt(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128){if(!(t.f&32768))throw e;try{t.b.error(e);rLine truncated
|
||||
`).some(e=>e.startsWith(`https://svelte.dev/e/`)))throw r;return r!==Be&&console.warn(`Failed to hydrate: `,r),t.recover===!1&&Ne(),zn(),Un(n),E(!1),yi(e,t)}finally{E(r),O(i)}}var xi=new Map;function Si(e,{target:t,anchor:n,props:r={},events:i,context:o,intro:s=!0,transformError:c}){zn();var l=void 0,u=tr(()=>{var u=n??t.appendChild(I());Rt(u,{pending:()=>{}},t=>{ft({});var n=A;if(o&&(n.c=o),i&&(r.$$events=i),T&&$(t,null),gi=s,l=e(t,r)||{},gi=!0,T&&(G.nodes.end=D,D===null||D.nodeType!==8||D.data!==`]`))throw Ge(),Be;pt()},c);var d=new Set,f=e=>{for(var n=0;n<e.length;n++){var r=e[n];if(!d.has(r)){d.add(r);var i=Jr(r);for(let e of[t,document]){var a=xi.get(e);a===void 0&&(a=new Map,xi.set(e,a));var o=a.get(r);o===void 0?(e.addEventListener(r,oi,{passive:i}),a.set(r,1)):a.set(r,o+1)}}}};return f(a(Qr)),$r.add(f),()=>{for(var e of d)for(let n of[t,document]){var r=xi.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,oi),r.delete(e),r.size===0&&xi.delete(n)):r.set(e,i)}$r.delete(f),u!==n&&u.parentNode?.removeChild(u)}});return Ci.set(l,u),l}var Ci=new WeakMap;function wi(e,t){let n=Ci.get(e);return n?(Ci.delete(e),n(t)):Promise.resolve()}var Ti=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)hr(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(hr(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(V(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();_r(r,t),t.append(I()),this.#n.set(e,{effect:r,fragment:t})}else V(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),pr(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(V(n.effect),this.#n.delete(e))};ensure(e,t){var n=N,r=Wn();if(t&&!this.#t.has(e)&&!this.#n.has(e))if(r){var i=document.createDocumentFragment(),a=I();i.append(a),this.#n.set(e,{effect:B(()=>t(a)),fragment:i})}else this.#t.set(e,B(()=>t(this.anchor)));if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else T&&(this.anchor=D),this.#a(n)}};function Ei(e,t,n=!1){var r;T&&(r=D,k());var i=new Ti(e),a=n?ne:0;function o(e,t){if(T){var n=Ze(r);if(e!==parseInt(n.substring(1))){var a=Xe();O(a),i.anchor=a,E(!1),i.ensure(e,t),E(!0);return}}i.ensure(e,t)}or(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}var Di=Symbol(`NaN`);function Oi(e,t,n){T&&k();var r=new Ti(e),i=!mt();or(()=>{var e=t();e!==e&&(e=Di),i&&typeof e==`object`&&e&&(e={}),r.ensure(e,n)})}function ki(e,t){return t}function Ai(e,t,n){for(var r=[],i=t.length,o,s=t.length,c=0;c<i;c++){let n=t[c];pr(n,()=>{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;ji(e,a(o.done)),t.delete(o),t.size===0&&(e.outrogroups=null)}}else--s},!1)}if(s===0){var l=r.length===0&&n!==null;if(l){var u=n,d=u.parentNode;Un(d),d.append(u),e.items.clear()}ji(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function ji(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i<t.length;i++){var a=t[i];r?.has(a)?(a.f|=oe,_r(a,document.createDocumentFragment())):V(t[i],n)}}var Mi;function Ni(e,t,r,i,o,s=null){var c=e,l=new Map;if(t&4){var u=e;c=T?O(L(u)):u.appendChild(I())}T&&k();var d=null,f=Jt(()=>{var e=r();return n(e)?e:e==null?[]:a(e)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,Fi(v,p,c,t,i),d!==null&&(p.length===0?d.f&33554432?(d.f^=oe,Li(d,null,c)):hr(d):pr(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:or(()=>{p=Z(f);var e=p.length;let n=!1;T&&Ze(c)===`[!`!=(e===0)&&(c=Xe(),O(c),E(!1),n=!0);for(var a=new Set,u=N,v=Wn(),y=0;y<e;y+=1){T&&D.nodeType===8&&D.data===`]`&&(c=D,n=!0,E(!1));var b=p[y],x=i(b,y),S=h?null:l.get(x);S?(S.v&&Dn(S.v,b),S.i&&Dn(S.i,y),v&&u.unskip_effect(S.e)):(S=Ii(l,h?c:Mi??=I(),b,x,y,o,t,r),h||(S.e.f|=oe),l.set(x,S)),a.add(x)}if(e===0&&s&&!d&&(h?d=B(()=>s(c)):(d=B(()=>s(Mi??=I())),d.f|=oe)),e>a.size&&Te(``,``,``),T&&e>0&&O(Xe()),!h)if(m.set(u,a),v){for(let[e,t]of l)a.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u);n&&E(!0),Z(f)}),flags:t,items:l,pending:m,outrogroups:null,fallback:d};h=!1,T&&(c=D)}function Pi(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function Fi(e,t,n,r,i){var o=!!(r&8),s=t.length,c=e.items,l=Pi(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(o)for(v=0;v<s;v+=1)h=t[v],g=i(h,v),_=c.get(g).e,_.f&33554432||(_.nodes?.a?.measure(),(f??=new Set).add(_));for(v=0;v<s;v+=1){if
Line truncated
|
||||
\r\f\xA0\v`];function oa(e,t,n){var r=e==null?``:``+e;if(t&&(r=r?r+` `+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+` `+i:i;else if(r.length)for(var a=i.length,o=0;(o=r.indexOf(i,o))>=0;){var s=o+a;(o===0||aa.includes(r[o-1]))&&(s===r.length||aa.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function sa(e,t=!1){var n=t?` !important;`:`;`,r=``;for(var i of Object.keys(e)){var a=e[i];a!=null&&a!==``&&(r+=` `+i+`: `+a+n)}return r}function ca(e){return e[0]!==`-`||e[1]!==`-`?e.toLowerCase():e}function la(e,t){if(t){var n=``,r,i;if(Array.isArray(t)?(r=t[0],i=t[1]):r=t,e){e=String(e).replaceAll(/\s*\/\*.*?\*\/\s*/g,``).trim();var a=!1,o=0,s=!1,c=[];r&&c.push(...Object.keys(r).map(ca)),i&&c.push(...Object.keys(i).map(ca));var l=0,u=-1;let t=e.length;for(var d=0;d<t;d++){var f=e[d];if(s?f===`/`&&e[d-1]===`*`&&(s=!1):a?a===f&&(a=!1):f===`/`&&e[d+1]===`*`?s=!0:f===`"`||f===`'`?a=f:f===`(`?o++:f===`)`&&o--,!s&&a===!1&&o===0){if(f===`:`&&u===-1)u=d;else if(f===`;`||d===t-1){if(u!==-1){var p=ca(e.substring(l,u).trim());if(!c.includes(p)){f!==`;`&&d++;var m=e.substring(l,d).trim();n+=` `+m+`;`}}l=d+1,u=-1}}}}return r&&(n+=sa(r)),i&&(n+=sa(i,!0)),n=n.trim(),n===``?null:n}return e==null?null:String(e)}function ua(e,t,n,r,i,a){var o=e[he];if(T||o!==n||o===void 0){var s=oa(n,r,a);(!T||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[he]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}function da(e,t={},n,r){for(var i in n){var a=n[i];t[i]!==a&&(n[i]==null?e.style.removeProperty(i):e.style.setProperty(i,a,r))}}function fa(e,t,n,r){var i=e[ge];if(T||i!==t){var a=la(t,r);(!T||a!==e.getAttribute(`style`))&&(a==null?e.removeAttribute(`style`):e.style.cssText=a),e[ge]=t}else r&&(Array.isArray(r)?(da(e,n?.[0],r[0]),da(e,n?.[1],r[1],`important`)):da(e,n,r));return r}function pa(e,t,r=!1){if(e.multiple){if(t==null)return;if(!n(t))return Ke();for(var i of e.options)i.selected=t.includes(ga(i));return}for(i of e.options)if(Nn(ga(i),t)){i.selected=!0;return}(!r||t!==void 0)&&(e.selectedIndex=-1)}function ma(e){var t=new MutationObserver(()=>{`__value`in e&&pa(e,e.__value)});t.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[`value`]}),Zn(()=>{t.disconnect()})}function ha(e,t,n=t){var r=new WeakSet,i=!0;Ft(e,`change`,t=>{var i=t?`[selected]`:`:checked`,a;if(e.multiple)a=[].map.call(e.querySelectorAll(i),ga);else{var o=e.querySelector(i)??e.querySelector(`option:not([disabled])`);a=o&&ga(o)}n(a),e.__value=a,N!==null&&r.add(N)}),nr(()=>{var a=t();if(e===document.activeElement){var o=N;if(r.has(o))return}if(pa(e,a,i),i&&a===void 0){var s=e.querySelector(`:checked`);s!==null&&(a=ga(s),n(a))}e.__value=a,i=!1}),ma(e)}function ga(e){return`__value`in e?e.__value:e.value}var _a=Symbol(`class`),va=Symbol(`style`),ya=Symbol(`is custom element`),ba=Symbol(`is html`),xa=be?`link`:`LINK`,Sa=be?`input`:`INPUT`,Ca=be?`option`:`OPTION`,wa=be?`select`:`SELECT`,Ta=be?`progress`:`PROGRESS`;function Ea(e){if(T){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;ka(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;ka(e,`checked`,null),e.checked=r}}};e[ve]=n,j(n),Nt()}}function Da(e,t){var n=Ma(e);n.value!==(n.value=t??void 0)&&(e.value!==t||t===0&&e.nodeName===Ta)&&(e.value=t??``)}function Oa(e,t){t?e.hasAttribute(`selected`)||e.setAttribute(`selected`,``):e.removeAttribute(`selected`)}function ka(e,t,n,r){var i=Ma(e);T&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===xa)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[fe]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&Pa(e).includes(t)?e[t]=n:e.setAttribute(t,n))}function Aa(e,t,n,r,i=!1,a=!1){if(T&&i&&e.nodeName===Sa){var o=e;(o.type===`checkbox`?`defaultChecked`:`defaultValue`)in n||Ea(o)}var s=Ma(e),c=s[ya],l=!s[ba];let u=T&&c;u&&E(!1);var d=t||{},f=e.nodeName===Ca;for(var p in t)p in n||(n[p]=null);n.class?n.class=ia(n.class):(r||n[_a])&&(n.class=null),n[va]&&(n.style??=null);var m=Pa(e);if(e.nodeName===Sa&&`type`in n&&(`value`in n||`__value`in n)){var h=n.type;(h!==d.type||h===void 0&&e.hasAttribute(`type`))&&(d.type=h,ka(e,`type`,h,a))}for(let i in n){let o=n[i];if(f&&i===`value`&&o==null){e.value=e.__value=``,d[i]=o;continue}if(i===`class`){ua(e,e.namespaceURI===`http://www.w3.org/1999/xhtml`,o,r,t?.[_a],n[_a]),d[i]=o,d[_a]=n[_a];continue}if(i===`style`){fa(e,o,t?.[va],n[va]),d[i]=o,d[va]=n[va];continue}var g=d[i];if(!(o===g&&!(o===void 0&&e.hasAttribute(i)))){d[i]=o;var _=i[0]+i[1];if(_!==`$$`)if(_===`on`){let t={},n=`$$`+i,r=i.slice(2);var v=Ur(r);if(Vr(r)&&(r=r.slice(0,-7),t.capture=!0),!v&&g){if(o!=null)continue;e.removeEventListener(r,d[n],t),d[n]=null}if(v)ri(r,e,o),ii([r]);else if(o!=null){function a(e){d[i].call(this,e)}d[n]=ti(r,e,a,t)}}else if(i===`style`)ka(e,i,o);else if(i===`autofocus`)At(e,!!o);else if(!c&&(i===`__value`||i===`value`&&o!=null))
Line truncated
|
||||
@@ -1 +0,0 @@
|
||||
import{t as e}from"./BqkZ0BZ5.js";var t={register:t=>e.post(`/auth/register`,t),status:()=>e.get(`/auth/status`),login:t=>e.post(`/auth/login`,t)};export{t};
|
||||
@@ -1 +0,0 @@
|
||||
import{$ as e,B as t,D as n,E as r,G as i,K as a,O as o,P as s,Q as c,R as l,T as u,X as d,Y as f,Z as p,at as m,dt as h,it as g,w as _}from"./39JK5HWF.js";import"./xihTtKlq.js";var v=class extends Map{#e=new Map;#t=c(0);#n=c(0);#r=l||-1;constructor(e){if(super(),e){for(var[t,n]of e)super.set(t,n);this.#n.v=super.size}}#i(e){return l===this.#r?c(e):p(e)}has(e){var t=this.#e,n=t.get(e);if(n===void 0)if(super.has(e))n=this.#i(0),t.set(e,n);else return s(this.#t),!1;return s(n),!0}forEach(e,t){this.#a(),super.forEach(e,t)}get(e){var t=this.#e,n=t.get(e);if(n===void 0)if(super.has(e))n=this.#i(0),t.set(e,n);else{s(this.#t);return}return s(n),super.get(e)}set(e,t){var n=this.#e,r=n.get(e),i=super.get(e),a=super.set(e,t),o=this.#t;if(r===void 0)r=this.#i(0),n.set(e,r),d(this.#n,super.size),f(o);else if(i!==t){f(r);var s=o.reactions===null?null:new Set(o.reactions);(s===null||!r.reactions?.every(e=>s.has(e)))&&f(o)}return a}delete(e){var t=this.#e,n=t.get(e),r=super.delete(e);return n!==void 0&&(t.delete(e),d(n,-1)),r&&(d(this.#n,super.size),f(this.#t)),r}clear(){if(super.size!==0){super.clear();var e=this.#e;d(this.#n,0);for(var t of e.values())d(t,-1);f(this.#t),e.clear()}}#a(){s(this.#t);var e=this.#e;if(this.#n.v!==e.size){for(var t of super.keys())if(!e.has(t)){var n=this.#i(0);e.set(t,n)}}for([,n]of this.#e)s(n)}keys(){return s(this.#t),super.keys()}values(){return this.#a(),super.values()}entries(){return this.#a(),super.entries()}[Symbol.iterator](){return this.entries()}get size(){return s(this.#n),super.size}},y=o(`<span class="text-red-700 dark:text-red-400 text-xs"> </span>`);function b(o,c){m(c,!0);var l=n(),d=a(l),f=e=>{var n=y(),a=i(n,!0);h(n),t(e=>u(a,e),[()=>c.errors.get(c.key)]),r(e,n)},p=e(()=>c.errors.has(c.key));_(d,e=>{s(p)&&e(f)}),r(o,l),g()}export{v as n,b as t};
|
||||
@@ -0,0 +1,6 @@
|
||||
import{$ as e,A as t,B as n,D as r,E as i,F as a,H as o,I as s,K as c,L as l,M as u,N as d,P as f,Q as p,R as m,T as h,U as g,X as _,Z as v,_ as y,a as b,at as x,b as ee,c as te,dt as ne,et as S,f as re,ft as ie,h as ae,it as oe,j as C,k as w,m as se,n as ce,nt as T,p as le,q as E,r as ue,u as de,v as fe,vt as D}from"./DGrvnoUE.js";import"./BZuNOv8h.js";import"./xihTtKlq.js";import"./B6JECYQD.js";var pe=Math.min,O=Math.max,me=Math.round,he=Math.floor,k=e=>({x:e,y:e}),A={left:`right`,right:`left`,bottom:`top`,top:`bottom`};function j(e,t,n){return O(e,pe(t,n))}function ge(e,t){return typeof e==`function`?e(t):e}function M(e){return e.split(`-`)[0]}function N(e){return e.split(`-`)[1]}function P(e){return e===`x`?`y`:`x`}function F(e){return e===`y`?`height`:`width`}function I(e){let t=e[0];return t===`t`||t===`b`?`y`:`x`}function _e(e){return P(I(e))}function ve(e,t,n){n===void 0&&(n=!1);let r=N(e),i=_e(e),a=F(i),o=i===`x`?r===(n?`end`:`start`)?`right`:`left`:r===`start`?`bottom`:`top`;return t.reference[a]>t.floating[a]&&(o=Ee(o)),[o,Ee(o)]}function ye(e){let t=Ee(e);return[be(e),t,be(t)]}function be(e){return e.includes(`start`)?e.replace(`start`,`end`):e.replace(`end`,`start`)}var xe=[`left`,`right`],Se=[`right`,`left`],Ce=[`top`,`bottom`],L=[`bottom`,`top`];function we(e,t,n){switch(e){case`top`:case`bottom`:return n?t?Se:xe:t?xe:Se;case`left`:case`right`:return t?Ce:L;default:return[]}}function Te(e,t,n,r){let i=N(e),a=we(M(e),n===`start`,r);return i&&(a=a.map(e=>e+`-`+i),t&&(a=a.concat(a.map(be)))),a}function Ee(e){let t=M(e);return A[t]+e.slice(t.length)}function De(e){return{top:e.top??0,right:e.right??0,bottom:e.bottom??0,left:e.left??0}}function Oe(e){return typeof e==`number`?{top:e,right:e,bottom:e,left:e}:De(e)}function ke(e){let{x:t,y:n,width:r,height:i}=e;return{width:r,height:i,top:n,left:t,right:t+r,bottom:n+i,x:t,y:n}}function Ae(e,t,n){let{reference:r,floating:i}=e,a=I(t),o=_e(t),s=F(o),c=M(t),l=a===`y`,u=r.x+r.width/2-i.width/2,d=r.y+r.height/2-i.height/2,f=r[s]/2-i[s]/2,p;switch(c){case`top`:p={x:u,y:r.y-i.height};break;case`bottom`:p={x:u,y:r.y+r.height};break;case`right`:p={x:r.x+r.width,y:d};break;case`left`:p={x:r.x-i.width,y:d};break;default:p={x:r.x,y:r.y}}let m=N(t);return m&&(p[o]+=f*(m===`end`?1:-1)*(n&&l?-1:1)),p}async function je(e,t){t===void 0&&(t={});let{x:n,y:r,platform:i,rects:a,elements:o,strategy:s}=e,{boundary:c=`clippingAncestors`,rootBoundary:l=`viewport`,elementContext:u=`floating`,altBoundary:d=!1,padding:f=0}=ge(t,e),p=Oe(f),m=o[d?u===`floating`?`reference`:`floating`:u],h=ke(await i.getClippingRect({element:await(i.isElement==null?void 0:i.isElement(m))??!0?m:m.contextElement||await(i.getDocumentElement==null?void 0:i.getDocumentElement(o.floating)),boundary:c,rootBoundary:l,strategy:s})),g=u===`floating`?{x:n,y:r,width:a.floating.width,height:a.floating.height}:a.reference,_=await(i.getOffsetParent==null?void 0:i.getOffsetParent(o.floating)),v=await(i.isElement==null?void 0:i.isElement(_))&&await(i.getScale==null?void 0:i.getScale(_))||{x:1,y:1},y=ke(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:o,rect:g,offsetParent:_,strategy:s}):g);return{top:(h.top-y.top+p.top)/v.y,bottom:(y.bottom-h.bottom+p.bottom)/v.y,left:(h.left-y.left+p.left)/v.x,right:(y.right-h.right+p.right)/v.x}}var Me=50,Ne=async(e,t,n)=>{let{placement:r=`bottom`,strategy:i=`absolute`,middleware:a=[],platform:o}=n,s=o.detectOverflow?o:{...o,detectOverflow:je},c=await(o.isRTL==null?void 0:o.isRTL(t)),l=await o.getElementRects({reference:e,floating:t,strategy:i}),{x:u,y:d}=Ae(l,r,c),f=r,p=0,m={};for(let n=0;n<a.length;n++){let h=a[n];if(!h)continue;let{name:g,fn:_}=h,{x:v,y,data:b,reset:x}=await _({x:u,y:d,initialPlacement:r,placement:f,strategy:i,middlewareData:m,rects:l,platform:s,elements:{reference:e,floating:t}});u=v??u,d=y??d,m[g]={...m[g],...b},x&&p<Me&&(p++,typeof x==`object`&&(x.placement&&(f=x.placement),x.rects&&(l=x.rects===!0?await o.getElementRects({reference:e,floating:t,strategy:i}):x.rects),{x:u,y:d}=Ae(l,f,c)),n=-1)}return{x:u,y:d,placement:f,strategy:i,middlewareData:m}},Pe=function(e){return e===void 0&&(e={}),{name:`flip`,options:e,async fn(t){var n;let{placement:r,middlewareData:i,rects:a,initialPlacement:o,platform:s,elements:c}=t,{mainAxis:l=!0,crossAxis:u=!0,fallbackPlacements:d,fallbackStrategy:f=`bestFit`,fallbackAxisSideDirection:p=`none`,flipAlignment:m=!0,...h}=ge(e,t);if((n=i.arrow)!=null&&n.alignmentOffset)return{};let g=M(r),_=I(o),v=M(o)===o,y=await(s.isRTL==null?void 0:s.isRTL(c.floating)),b=d||(v||!m?[Ee(o)]:ye(o)),x=p!==`none`;!d&&x&&b.push(...Te(o,m,p,y));let ee=[o,...b],te=await s.detectOverflow(t,h),ne=[],S=i.flip?.overflows||[];if(l&&ne.push(te[g]),u){let e=ve(r,a,y);ne.push(te[e[0]],te[e[1]])}if(S=[...S,{placement:r,overflows:ne}],!ne.every(e=>e<=0)){let e=(i.flip?.index||0)+1,t=ee[e];if(t&&(u!==`alignment`||_===I(t)||S.every(e=>I(e.placement)!==_||e.overflows[0]>0)))return{daLine truncated
|
||||
3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0
|
||||
1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502
|
||||
0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0
|
||||
0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"></path></svg>`);function Nt(e){var t=Mt();C(e,t)}var Q=f(`<svg width="100%" height="100%" viewBox="-2 -2 50 50" focusable="false" aria-hidden="true" role="presentation"><path fill="currentColor" d="M34.923,37.251L24,26.328L13.077,37.251L9.436,33.61l10.923-10.923L9.436,11.765l3.641-3.641L24,19.047L34.923,8.124
|
||||
l3.641,3.641L27.641,22.688L38.564,33.61L34.923,37.251z"></path></svg>`);function Pt(e){var t=Q();C(e,t)}var Ft=f(`<svg class="loading" viewBox="25 25 50 50"><circle class="circle_path" cx="50" cy="50" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-miterlimit="10"></circle></svg>`);function It(e){var t=Ft();C(e,t)}var Lt=d(`<div class="list-item" tabindex="-1" role="none"><div><!></div></div>`),Rt=d(`<div class="empty">No options</div>`),zt=d(`<div role="none"><!> <!> <!></div>`),Bt=d(`<span id="aria-selection"> </span> <span id="aria-context"> </span>`,1),Vt=d(`<div class="multi-item-clear"><!></div>`),Ht=d(`<div role="none"><span class="multi-item-text"><!></span> <!></div>`),Ut=d(`<div><!></div>`),Wt=d(`<div class="icon loading" aria-hidden="true"><!></div>`),Gt=d(`<button type="button" class="icon clear-select"><!></button>`),Kt=d(`<div class="icon chevron" aria-hidden="true"><!></div>`),qt=d(`<input type="hidden"/>`),Jt=d(`<select class="required" required="" tabindex="-1" aria-hidden="true"></select>`),Yt=d(`<div role="none"><!> <span aria-live="polite" aria-atomic="false" aria-relevant="additions text" class="a11y-text"><!></span> <div class="prepend"><!></div> <div class="value-container"><!> <input/></div> <div class="indicators"><!> <!> <!></div> <!> <!></div>`);function Xt(o,s){ie(s,!0);let d,f=b(s,`valueMode`,3,`item`),pe=b(s,`filter`,3,At),O=b(s,`getItems`,3,jt),me=b(s,`id`,3,null),he=b(s,`name`,3,null),k=b(s,`container`,15,null),A=b(s,`input`,15,null),j=b(s,`multiple`,3,!1),ge=b(s,`multiFullItemClearable`,3,!1),M=b(s,`disabled`,3,!1),N=b(s,`focused`,15,!1),P=b(s,`value`,15),F=b(s,`filterText`,15,``),I=b(s,`placeholder`,3,`Please select`),_e=b(s,`placeholderAlwaysShow`,3,!1),ve=b(s,`items`,31,()=>S([])),ye=b(s,`label`,3,`label`),be=b(s,`itemFilter`,3,(e,t,n)=>`${e}`.toLowerCase().includes(t.toLowerCase())),xe=b(s,`groupBy`,3,void 0),Se=b(s,`groupFilter`,3,e=>e),Ce=b(s,`groupHeaderSelectable`,3,!1),L=b(s,`itemId`,3,`value`),we=b(s,`loadOptions`,3,void 0),Te=b(s,`containerStyles`,3,``),Ee=b(s,`hasError`,3,!1),De=b(s,`filterSelectedItems`,3,!0),Oe=b(s,`required`,3,!1),ke=b(s,`closeListOnChange`,3,!0),Ae=b(s,`clearFilterTextOnBlur`,3,!0),je=b(s,`createGroupHeaderItem`,3,(e,t)=>({value:e,[ye()]:e})),Me=b(s,`searchable`,3,!0),Ne=b(s,`inputStyles`,3,``),Pe=b(s,`clearable`,3,!0),Fe=b(s,`loading`,15,!1),R=b(s,`listOpen`,15,!1),Ie=b(s,`debounce`,3,(e,t=1)=>{clearTimeout(d),d=setTimeout(e,t)}),Le=b(s,`debounceWait`,3,300),Re=b(s,`hideEmptyState`,3,!1),z=b(s,`inputAttributes`,19,()=>({})),B=b(s,`listAutoWidth`,3,!0),V=b(s,`showChevron`,3,!1),ze=b(s,`listOffset`,3,5),H=b(s,`hoverItemIndex`,15,0),U=b(s,`floatingConfig`,19,()=>({})),Be=b(s,`class`,3,``),Ve=b(s,`ariaValues`,3,e=>`Option ${e}, selected.`),He=b(s,`ariaListOpen`,3,(e,t)=>`You are currently focused on option ${e}. There are ${t} results available.`),Ue=b(s,`ariaFocused`,3,()=>`Select is focused, type to refine list, press down to open the menu.`);function We(){return n(Z)}let W=oe(void 0),G,Ge,Ke,qe,K=oe(null),q={},J=x(()=>{if(z()||!Me()){let e=Object.assign({autocapitalize:`none`,autocomplete:`off`,autocorrect:`off`,spellcheck:!1,tabindex:0,type:`text`,"aria-autocomplete":`list`},z());me()&&(e.id=me()),Me()||(e.readonly=!0),JSON.stringify(e)!==JSON.stringify(q)&&(q=e)}return q});function Y(e){return e==null?e:typeof e==`object`?e[L()]:e}function Je(e){return Array.isArray(e)&&e.length>0&&typeof e[0]!=`object`}let Ye=x(()=>f()===`id`||Je(ve()));function Xe(e){return n(Ye)?Y(e):e}function Ze(e){if(e==null)return e;if(typeof e==`object`)return e[ye()];let t=ve()?.find(t=>t[L()]===e);return t?t[ye()]:e}function Qe(){return P()==null?null:n(Ye)?j()&&Array.isArray(P())?JSON.stringify(P()):`${P()}`:JSON.stringify(P())}function $e(e){return e.map((e,t)=>({index:t,value:e,label:`${e}`}))}function et(e){if(!xe())return e;let t=[],n={};e.forEach(e=>{let r=xe()(e)??``;t.includes(r)||(t.push(r),n[r]=[],r&&n[r].push(Object.assign(je()(r,e),{id:r,groupHeader:!0,selectable:Ce()}))),n[r].push(Object.assign({groupItem:!!r},e))});let r=[];return Se()(t).forEach(e=>{n[e]&&r.push(...n[e])}),r}function tt(){if(j()){JSON.stringify(P())!==JSON.stringify(G)&&(!P()||_t())&&s.oninput?.(P());return}P()?(!G||JSON.stringify(Y(P()))!==JSON.stringify(Y(G)))&&s.oninput?.(P()):G&&s.oninput?.(P())}function nt(){Ke=!0,P()&&(Array.isArray(P())||P([P()]))}function rt(){Ke=!1,P()&&P(void 0)}function X(){it(n(Z).findIndex(e=>e[L()]===Y(P())),!0)}function it(e=0,t){let r=e<0?0:e;H()!==r&&H(r),!t&&xe()&&n(Z)[H()]&&!n(Z)[H()].selectable&&tn(1)}function at(){!we()&&F().length===0||(we()?Ie()(async function(){Fe(!0);let e=await O()({onerror:s.onerror,onloaded:s.onloaded,loadOptions:we(),convertStringItemsToObjects:$e,filterText:F()});e?(Fe(e.loading),R(R()?e.listOpen:F().length>0),N(R()&&e.focused),ve(xe()?et(e.filteredItems):e.filteredItems)):(Fe(!1),N(!0),R(!0))},Le()):(R()||R(!0),j()&&T(W,void 0)))}let ot=x(()=>j()?Array.isArray(P())&&P().length>0:!!P()),st=x(()=>n(Line truncated
|
||||
@@ -0,0 +1,2 @@
|
||||
import{et as e}from"./DGrvnoUE.js";import{t}from"./BZuNOv8h.js";import"./CY9OlB4q.js";var n=e({user:null,status:`loading`}),r=()=>{localStorage.removeItem(`accessToken`),n.status=`unauthenticated`},i=()=>localStorage.getItem(`accessToken`),a=e=>{localStorage.setItem(`accessToken`,e)},o=`/api`,s=class extends Error{status;body;constructor(e,t){super(`API Error ${e}`),this.status=e,this.body=t}};async function c(e,n={}){let{auth:a=!0,headers:c,...l}=n,d={"Content-Type":`application/json`,...c},f=new Headers(d);if(a){let e=i();e&&f.append(`Authorization`,`Bearer ${e}`)}let p=await fetch(`${o}${e}`,{...l,headers:f});if(p.status===401&&(r(),t(`/login`)),!p.ok)throw new s(p.status,await u(p));if(p.status!==204)return p.json()}async function*l(e,n={}){let{auth:a=!0,headers:s,...c}=n,l={"Content-Type":`application/json`,...s},u=new Headers(l);if(a){let e=i();e&&u.append(`Authorization`,`Bearer ${e}`)}let d=await fetch(`${o}${e}`,{...c,headers:u});if(d.status===401&&(r(),t(`/login`)),!d.ok||!d.body)throw Error(`HTTP ${d.status}`);let f=d.body.pipeThrough(new TextDecoderStream).getReader(),p=``;try{for(;;){let{value:e,done:t}=await f.read();if(t)break;p+=e;let n=p.split(`
|
||||
`);p=n.pop()??``;for(let e of n)e.trim()&&(yield JSON.parse(e))}p.trim()&&(yield JSON.parse(p))}finally{f.releaseLock()}}async function u(e){try{return await e.json()}catch{return null}}var d={get:(e,t)=>c(e,{...t,method:`GET`}),post:(e,t,n)=>c(e,{...n,method:`POST`,body:JSON.stringify(t)}),postStream:(e,t,n)=>l(e,{...n,method:`POST`,body:JSON.stringify(t)}),put:(e,t,n)=>c(e,{...n,method:`PUT`,body:JSON.stringify(t)}),delete:(e,t)=>c(e,{...t,method:`DELETE`})};export{a as i,n,r,d as t};
|
||||
@@ -0,0 +1 @@
|
||||
import"./DGrvnoUE.js";import{i as e,o as t}from"./BZuNOv8h.js";var n={get data(){return t.data},get error(){return t.error},get form(){return t.form},get params(){return t.params},get route(){return t.route},get state(){return t.state},get status(){return t.status},get url(){return t.url}};e.updated.check;var r=n;export{r as t};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{t as e}from"./BqkZ0BZ5.js";var t={me:()=>e.get(`/user/me`),update:(t,n)=>e.put(`/user/${t}`,n),listAddresses:t=>e.get(`/user/${t}/address`),createAddress:(t,n)=>e.post(`/user/${t}/address`,n),updateAddress:(t,n)=>e.put(`/user/${t}/address`,n),deleteAddress:(t,n)=>e.delete(`/user/${t}/address/${n}`)};export{t};
|
||||
import{t as e}from"./DtfiK5fE.js";var t={me:()=>e.get(`/user/me`),update:(t,n)=>e.put(`/user/${t}`,n),listAddresses:t=>e.get(`/user/${t}/address`),createAddress:(t,n)=>e.post(`/user/${t}/address`,n),updateAddress:(t,n)=>e.put(`/user/${t}/address`,n),deleteAddress:(t,n)=>e.delete(`/user/${t}/address/${n}`)};export{t};
|
||||
@@ -0,0 +1,2 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.BLV5Ey1y.js","../chunks/DGrvnoUE.js","../chunks/xihTtKlq.js","../chunks/DTCALkcP.js","../assets/0.C0TpjoIE.css","../nodes/1.COgG0Fy8.js","../chunks/ffLBjIeN.js","../chunks/BZuNOv8h.js","../nodes/2.wASuVt48.js","../chunks/CY9OlB4q.js","../chunks/DtfiK5fE.js","../chunks/zsRwiIdn.js","../chunks/Cyrg28w2.js","../chunks/BRXBpvt8.js","../nodes/3.CHETzYMH.js","../nodes/4.DkQEi_Qd.js","../chunks/BYWIgY43.js","../chunks/BYYkuYgk.js","../nodes/5.BgmoPtaF.js","../chunks/B6JECYQD.js","../assets/5.nNRn7XR0.css","../nodes/6.B8E4TBNF.js","../chunks/Bht2nTIZ.js","../nodes/7.CJI6is1J.js","../nodes/8.Ds1ONmZq.js","../nodes/9.CYl-UmMc.js","../chunks/BeIYuf23.js","../chunks/6mSXxE57.js","../chunks/DdGy_ePN.js","../nodes/10.D6tOeXzG.js","../nodes/11.DtFjRc5K.js","../nodes/12.D1XIMXHX.js","../nodes/13.C-WGkE4P.js","../chunks/D3MDK1zR.js","../nodes/14.Zp7NcB4T.js","../nodes/15.DBuFCR43.js","../nodes/16.Bwbj6qfw.js","../nodes/17.BV0v_EXc.js","../nodes/18.e37B7XcZ.js","../nodes/19.CdvrW-UN.js","../chunks/BedKI9iq.js","../nodes/20.CPbrVrB7.js"])))=>i.map(i=>d[i]);
|
||||
import{$ as e,A as t,B as n,F as r,H as i,J as a,K as o,M as s,N as c,Q as l,Z as u,a as d,at as f,c as p,dt as m,ft as h,i as g,it as _,j as v,k as y,nt as b,q as x,r as S,vt as C,w}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";var T=`modulepreload`,E=function(e,t){return new URL(e,t).href},D={},O=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=E(t,n),t=s(t),t in D)return;D[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:T,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},k={},A=c(`<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"><!></div>`),j=c(`<!> <!>`,1);function M(c,g){h(g,!0);let T=d(g,`components`,23,()=>[]),E=d(g,`data_0`,3,null),D=d(g,`data_1`,3,null),O=d(g,`data_2`,3,null);a(()=>g.stores.page.set(g.page)),x(()=>{g.stores,g.page,g.constructors,T(),g.form,E(),D(),O(),g.stores.page.notify()});let k=_(!1),M=_(!1),N=_(null);S(()=>{let e=g.stores.page.subscribe(()=>{n(k)&&(b(M,!0),i().then(()=>{b(N,document.title||`untitled page`,!0)}))});return b(k,!0),e});let P=f(()=>g.constructors[2]);var F=j(),I=l(F),L=e=>{let t=f(()=>g.constructors[0]);var r=s(),i=l(r);w(i,()=>n(t),(e,t)=>{p(t(e,{get data(){return E()},get form(){return g.form},get params(){return g.page.params},children:(e,t)=>{var r=s(),i=l(r),a=e=>{let t=f(()=>g.constructors[1]);var r=s(),i=l(r);w(i,()=>n(t),(e,t)=>{p(t(e,{get data(){return D()},get form(){return g.form},get params(){return g.page.params},children:(e,t)=>{var r=s(),i=l(r);w(i,()=>n(P),(e,t)=>{p(t(e,{get data(){return O()},get form(){return g.form},get params(){return g.page.params}}),e=>T()[2]=e,()=>T()?.[2])}),v(e,r)},$$slots:{default:!0}}),e=>T()[1]=e,()=>T()?.[1])}),v(e,r)},o=e=>{let t=f(()=>g.constructors[1]);var r=s(),i=l(r);w(i,()=>n(t),(e,t)=>{p(t(e,{get data(){return D()},get form(){return g.form},get params(){return g.page.params}}),e=>T()[1]=e,()=>T()?.[1])}),v(e,r)};y(i,e=>{g.constructors[2]?e(a):e(o,-1)}),v(e,r)},$$slots:{default:!0}}),e=>T()[0]=e,()=>T()?.[0])}),v(e,r)},R=e=>{let t=f(()=>g.constructors[0]);var r=s(),i=l(r);w(i,()=>n(t),(e,t)=>{p(t(e,{get data(){return E()},get form(){return g.form},get params(){return g.page.params}}),e=>T()[0]=e,()=>T()?.[0])}),v(e,r)};y(I,e=>{g.constructors[1]?e(L):e(R,-1)});var z=e(I,2),B=e=>{var i=A(),a=u(i),s=e=>{var i=r();o(()=>t(i,n(N))),v(e,i)};y(a,e=>{n(M)&&e(s)}),C(i),v(e,i)};y(z,e=>{n(k)&&e(B)}),v(c,F),m()}var N=g(M),P=[()=>O(()=>import(`../nodes/0.BLV5Ey1y.js`),__vite__mapDeps([0,1,2,3,4]),import.meta.url),()=>O(()=>import(`../nodes/1.COgG0Fy8.js`),__vite__mapDeps([5,1,2,6,7]),import.meta.url),()=>O(()=>import(`../nodes/2.wASuVt48.js`),__vite__mapDeps([8,1,7,2,3,6,9,10,11,12,13]),import.meta.url),()=>O(()=>import(`../nodes/3.CHETzYMH.js`),__vite__mapDeps([14,1,7,2,9,12]),import.meta.url),()=>O(()=>import(`../nodes/4.DkQEi_Qd.js`),__vite__mapDeps([15,1,7,2,9,16,17,10]),import.meta.url),()=>O(()=>import(`../nodes/5.BgmoPtaF.js`),__vite__mapDeps([18,1,2,6,7,16,17,10,9,19,20]),import.meta.url),()=>O(()=>import(`../nodes/6.B8E4TBNF.js`),__vite__mapDeps([21,1,7,2,9,17,10,22]),import.meta.url),()=>O(()=>import(`../nodes/7.CJI6is1J.js`),__vite__mapDeps([23,1,2]),import.meta.url),()=>O(()=>import(`../nodes/8.Ds1ONmZq.js`),__vite__mapDeps([24,1,2]),import.meta.url),()=>O(()=>import(`../nodes/9.CYl-UmMc.js`),__vite__mapDeps([25,1,2,10,7,9,12,26,27,28,19]),import.meta.url),()=>O(()=>import(`../nodes/10.D6tOeXzG.js`),__vite__mapDeps([29,1,2]),import.meta.url),()=>O(()=>import(`../nodes/11.DtFjRc5K.js`),__vite__mapDeps([30,1,2]),import.meta.url),()=>O(()=>import(`../nodes/12.D1XIMXHX.js`),__vite__mapDeps([31,1,2]),import.meta.url),()=>O(()=>import(`../nodes/13.C-WGkE4P.js`),__vite__mapDeps([32,1,2,26,10,7,9,27,28,19,33]),import.meta.url),()=>O(()=>import(`../nodes/14.Zp7NcB4T.js`),__vite__mapDeps([34,1,2]),import.meta.url),()=>O(()=>import(`../nodes/15.DBuFCR43.js`),__vite__mapDeps([35,1,2,10,7,9,27]),import.meta.url),()Line truncated
|
||||
@@ -1,2 +0,0 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.DNLpy_rd.js","../chunks/39JK5HWF.js","../chunks/xihTtKlq.js","../chunks/DTCALkcP.js","../assets/0.Bdga2HVs.css","../nodes/1.Cvrs0-W0.js","../chunks/D1d5-1oo.js","../chunks/D3up1JC1.js","../nodes/2.Bs3bPWro.js","../chunks/B5isPFOt.js","../chunks/BqkZ0BZ5.js","../chunks/BEgKbVuy.js","../chunks/D8dI9nx6.js","../chunks/BRXBpvt8.js","../nodes/3.BD_oZvMH.js","../nodes/4.Dw1ji0S9.js","../nodes/5.BV8wWprr.js","../nodes/6.iWRSAZEv.js","../nodes/7.CJ-_CrAo.js","../nodes/8.D-K-fxzc.js","../chunks/D35bQ8xD.js","../chunks/DRdGzrsF.js","../chunks/D4uBfDnr.js","../nodes/9.C_kOH_FX.js","../nodes/10.2kRbJUBr.js","../nodes/11.3PxmG5oJ.js","../nodes/12.B5iUSTUw.js","../chunks/D3MDK1zR.js","../nodes/13.CjlegDH_.js","../nodes/14.CFcHzpcm.js","../nodes/15.SpasLkf4.js","../nodes/16.TiX4VGhr.js","../nodes/17.wrIvaz1M.js","../nodes/18.BfPDxxm9.js","../chunks/DNQ6C3zq.js","../nodes/19.Bhx_NIDl.js"])))=>i.map(i=>d[i]);
|
||||
import{$ as e,A as t,B as n,D as r,E as i,G as a,H as o,I as s,K as c,O as l,P as u,Q as d,T as f,V as p,X as m,a as h,at as g,dt as _,i as v,it as y,o as b,q as x,r as S,w as C,y as w}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var T=`modulepreload`,E=function(e,t){return new URL(e,t).href},D={},O=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=E(t,n),t=s(t),t in D)return;D[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:T,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},k={},A=l(`<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"><!></div>`),j=l(`<!> <!>`,1);function M(l,v){g(v,!0);let T=h(v,`components`,23,()=>[]),E=h(v,`data_0`,3,null),D=h(v,`data_1`,3,null),O=h(v,`data_2`,3,null);o(()=>v.stores.page.set(v.page)),p(()=>{v.stores,v.page,v.constructors,T(),v.form,E(),D(),O(),v.stores.page.notify()});let k=d(!1),M=d(!1),N=d(null);S(()=>{let e=v.stores.page.subscribe(()=>{u(k)&&(m(M,!0),s().then(()=>{m(N,document.title||`untitled page`,!0)}))});return m(k,!0),e});let P=e(()=>v.constructors[2]);var F=j(),I=c(F),L=t=>{let n=e(()=>v.constructors[0]);var a=r(),o=c(a);w(o,()=>u(n),(t,n)=>{b(n(t,{get data(){return E()},get form(){return v.form},get params(){return v.page.params},children:(t,n)=>{var a=r(),o=c(a),s=t=>{let n=e(()=>v.constructors[1]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return D()},get form(){return v.form},get params(){return v.page.params},children:(e,t)=>{var n=r(),a=c(n);w(a,()=>u(P),(e,t)=>{b(t(e,{get data(){return O()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[2]=e,()=>T()?.[2])}),i(e,n)},$$slots:{default:!0}}),e=>T()[1]=e,()=>T()?.[1])}),i(t,a)},l=t=>{let n=e(()=>v.constructors[1]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return D()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[1]=e,()=>T()?.[1])}),i(t,a)};C(o,e=>{v.constructors[2]?e(s):e(l,-1)}),i(t,a)},$$slots:{default:!0}}),e=>T()[0]=e,()=>T()?.[0])}),i(t,a)},R=t=>{let n=e(()=>v.constructors[0]);var a=r(),o=c(a);w(o,()=>u(n),(e,t)=>{b(t(e,{get data(){return E()},get form(){return v.form},get params(){return v.page.params}}),e=>T()[0]=e,()=>T()?.[0])}),i(t,a)};C(I,e=>{v.constructors[1]?e(L):e(R,-1)});var z=x(I,2),B=e=>{var r=A(),o=a(r),s=e=>{var r=t();n(()=>f(r,u(N))),i(e,r)};C(o,e=>{u(M)&&e(s)}),_(r),i(e,r)};C(z,e=>{u(k)&&e(B)}),i(l,F),y()}var N=v(M),P=[()=>O(()=>import(`../nodes/0.DNLpy_rd.js`),__vite__mapDeps([0,1,2,3,4]),import.meta.url),()=>O(()=>import(`../nodes/1.Cvrs0-W0.js`),__vite__mapDeps([5,1,2,6,7]),import.meta.url),()=>O(()=>import(`../nodes/2.Bs3bPWro.js`),__vite__mapDeps([8,1,7,2,3,6,9,10,11,12,13]),import.meta.url),()=>O(()=>import(`../nodes/3.BD_oZvMH.js`),__vite__mapDeps([14,1,7,2,9,12]),import.meta.url),()=>O(()=>import(`../nodes/4.Dw1ji0S9.js`),__vite__mapDeps([15,1,2]),import.meta.url),()=>O(()=>import(`../nodes/5.BV8wWprr.js`),__vite__mapDeps([16,1,2]),import.meta.url),()=>O(()=>import(`../nodes/6.iWRSAZEv.js`),__vite__mapDeps([17,1,2]),import.meta.url),()=>O(()=>import(`../nodes/7.CJ-_CrAo.js`),__vite__mapDeps([18,1,2]),import.meta.url),()=>O(()=>import(`../nodes/8.D-K-fxzc.js`),__vite__mapDeps([19,1,2,10,7,9,20,21,22]),import.meta.url),()=>O(()=>import(`../nodes/9.C_kOH_FX.js`),__vite__mapDeps([23,1,2]),import.meta.url),()=>O(()=>import(`../nodes/10.2kRbJUBr.js`),__vite__mapDeps([24,1,2]),import.meta.url),()=>O(()=>import(`../nodes/11.3PxmG5oJ.js`),__vite__mapDeps([25,1,2]),import.meta.url),()=>O(()=>import(`../nodes/12.B5iUSTUw.js`),__vite__mapDeps([26,1,2,20,10,7,9,21,22,27]),import.meta.url),()=>O(()=>import(`../nodes/13.CjlegDH_.js`),__vite__mapDeps([28,1,2]),import.meta.url),()=>O(()=>import(`../nodes/14.CFcHzpcm.js`),__vite__mapDeps([29,1,2]),import.meta.url),()=>O(()=>import(`../nodes/15.SpasLkf4.js`),__vite__mapDeps([30,1,2]),import.meta.url),()=>O(()=>import(`../nodes/16.TiX4VGhr.js`),__vite__mapDeps([31,1,Line truncated
|
||||
@@ -1 +0,0 @@
|
||||
import{n as e,s as t}from"../chunks/D3up1JC1.js";export{t as load_css,e as start};
|
||||
@@ -0,0 +1 @@
|
||||
import{c as e,r as t}from"../chunks/BZuNOv8h.js";export{e as load_css,t as start};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{B as e,D as t,E as n,K as r,O as i,U as a,at as o,b as s,d as c,ft as l,it as u,r as d,ut as f,v as p,z as m}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as h}from"../chunks/DTCALkcP.js";var g=l({prerender:()=>!1,ssr:()=>!1}),_=`data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB3aWR0aD0iMTNtbSIKICAgaGVpZ2h0PSIxM21tIgogICB2aWV3Qm94PSIwIDAgMTMgMTMiCiAgIHZlcnNpb249IjEuMSIKICAgaWQ9InN2ZzEiCiAgIGlua3NjYXBlOnZlcnNpb249IjEuNC40IChkY2FmM2U3ZDllLCAyMDI2LTA1LTA1KSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iZmF2aWNvbi5zdmciCiAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iLi4vYmFja2VuZC93ZWIvc3JjL2xpYi9hc3NldHMvbG9nby10eHQucG5nIgogICBpbmtzY2FwZTpleHBvcnQteGRwaT0iOTYiCiAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI5NiIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9Im5hbWVkdmlldzEiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjMDAwMDAwIgogICAgIGJvcmRlcm9wYWNpdHk9IjAuMjUiCiAgICAgaW5rc2NhcGU6c2hvd3BhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlY2hlY2tlcmJvYXJkPSIwIgogICAgIGlua3NjYXBlOmRlc2tjb2xvcj0iI2QxZDFkMSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0ibW0iCiAgICAgaW5rc2NhcGU6em9vbT0iMS40NDMxODE4IgogICAgIGlua3NjYXBlOmN4PSIyODcuOTA1NTIiCiAgICAgaW5rc2NhcGU6Y3k9IjIxNC44MDMxNSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjI1NjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTAxMSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIgLz4KICA8ZGVmcwogICAgIGlkPSJkZWZzMSIgLz4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJDYWxxdWUgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjE0LjExMTFweDtmb250LWZhbWlseTpwaXhlbGFydDstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOnBpeGVsYXJ0O3dyaXRpbmctbW9kZTpsci10YjtkaXJlY3Rpb246bHRyO2ZpbGw6IzM3YWJjODtzdHJva2Utd2lkdGg6MC4yNjQ1ODMiCiAgICAgICB4PSIyLjc0OTk5OTUiCiAgICAgICB5PSIxMS41NDk5OTciCiAgICAgICBpZD0idGV4dDEiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW4xIgogICAgICAgICBzdHlsZT0iZm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtmb250LXNpemU6MTQuMTExMXB4O2ZvbnQtZmFtaWx5OidHb2h1Rm9udCAxMSBOZXJkIEZvbnQnOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246J0dvaHVGb250IDExIE5lcmQgRm9udCc7ZmlsbDojMGVhNWU5O2ZpbGwtb3BhY2l0eToxO3N0cm9rZS13aWR0aDowLjI2NDU4MyIKICAgICAgICAgeD0iMi43NDk5OTk1IgogICAgICAgICB5PSIxMS41NDk5OTciPmw8L3RzcGFuPjwvdGV4dD4KICA8L2c+Cjwvc3ZnPgo=`,v=i(`<link rel="icon"/> <link rel="stylesheet" href="/assets/fontello/css/fontello.css"/>`,1);function y(i,l){o(l,!0),d(()=>{h()});var g=t();p(`12qhfyh`,t=>{var i=v(),o=r(i);f(2),e(()=>c(o,`href`,_)),m(()=>{a.title=`Lasssistanoque`}),n(t,i)});var y=r(g);s(y,()=>l.children),n(i,g),u()}export{y as component,g as universal};
|
||||
import{G as e,K as t,M as n,N as r,Q as i,T as a,Y as o,_t as s,bt as c,dt as l,ft as u,j as d,m as f,r as p,x as m}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";import{t as h}from"../chunks/DTCALkcP.js";var g=c({prerender:()=>!1,ssr:()=>!1}),_=`data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB3aWR0aD0iMTNtbSIKICAgaGVpZ2h0PSIxM21tIgogICB2aWV3Qm94PSIwIDAgMTMgMTMiCiAgIHZlcnNpb249IjEuMSIKICAgaWQ9InN2ZzEiCiAgIGlua3NjYXBlOnZlcnNpb249IjEuNC40IChkY2FmM2U3ZDllLCAyMDI2LTA1LTA1KSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iZmF2aWNvbi5zdmciCiAgIGlua3NjYXBlOmV4cG9ydC1maWxlbmFtZT0iLi4vYmFja2VuZC93ZWIvc3JjL2xpYi9hc3NldHMvbG9nby10eHQucG5nIgogICBpbmtzY2FwZTpleHBvcnQteGRwaT0iOTYiCiAgIGlua3NjYXBlOmV4cG9ydC15ZHBpPSI5NiIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9Im5hbWVkdmlldzEiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjMDAwMDAwIgogICAgIGJvcmRlcm9wYWNpdHk9IjAuMjUiCiAgICAgaW5rc2NhcGU6c2hvd3BhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlY2hlY2tlcmJvYXJkPSIwIgogICAgIGlua3NjYXBlOmRlc2tjb2xvcj0iI2QxZDFkMSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0ibW0iCiAgICAgaW5rc2NhcGU6em9vbT0iMS40NDMxODE4IgogICAgIGlua3NjYXBlOmN4PSIyODcuOTA1NTIiCiAgICAgaW5rc2NhcGU6Y3k9IjIxNC44MDMxNSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjI1NjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTAxMSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIgLz4KICA8ZGVmcwogICAgIGlkPSJkZWZzMSIgLz4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJDYWxxdWUgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjE0LjExMTFweDtmb250LWZhbWlseTpwaXhlbGFydDstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOnBpeGVsYXJ0O3dyaXRpbmctbW9kZTpsci10YjtkaXJlY3Rpb246bHRyO2ZpbGw6IzM3YWJjODtzdHJva2Utd2lkdGg6MC4yNjQ1ODMiCiAgICAgICB4PSIyLjc0OTk5OTUiCiAgICAgICB5PSIxMS41NDk5OTciCiAgICAgICBpZD0idGV4dDEiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW4xIgogICAgICAgICBzdHlsZT0iZm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtmb250LXNpemU6MTQuMTExMXB4O2ZvbnQtZmFtaWx5OidHb2h1Rm9udCAxMSBOZXJkIEZvbnQnOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246J0dvaHVGb250IDExIE5lcmQgRm9udCc7ZmlsbDojMGVhNWU5O2ZpbGwtb3BhY2l0eToxO3N0cm9rZS13aWR0aDowLjI2NDU4MyIKICAgICAgICAgeD0iMi43NDk5OTk1IgogICAgICAgICB5PSIxMS41NDk5OTciPmw8L3RzcGFuPjwvdGV4dD4KICA8L2c+Cjwvc3ZnPgo=`,v=r(`<link rel="icon"/> <link rel="stylesheet" href="/assets/fontello/css/fontello.css"/>`,1);function y(r,c){u(c,!0),p(()=>{h()});var g=n();m(`12qhfyh`,n=>{var r=v(),a=i(r);s(2),t(()=>f(a,`href`,_)),e(()=>{o.title=`Lasssistanoque`}),d(n,r)});var y=i(g);a(y,()=>c.children),d(r,g),l()}export{y as component,g as universal};
|
||||
@@ -0,0 +1 @@
|
||||
import{$ as e,A as t,K as n,N as r,Q as i,Z as a,dt as o,ft as s,j as c,vt as l}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";import{t as u}from"../chunks/ffLBjIeN.js";var d=r(`<h1> </h1> <p> </p>`,1);function f(r,f){s(f,!0);var p=d(),m=i(p),h=a(m,!0);l(m);var g=e(m,2),_=a(g,!0);l(g),n(()=>{t(h,u.status),t(_,u.error?.message)}),c(r,p),o()}export{f as component};
|
||||
@@ -1 +0,0 @@
|
||||
import{B as e,E as t,G as n,K as r,O as i,T as a,at as o,dt as s,it as c,q as l}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as u}from"../chunks/D1d5-1oo.js";var d=i(`<h1> </h1> <p> </p>`,1);function f(i,f){o(f,!0);var p=d(),m=r(p),h=n(m,!0);s(m);var g=l(m,2),_=n(g,!0);s(g),e(()=>{a(h,u.status),a(_,u.error?.message)}),t(i,p),c()}export{f as component};
|
||||
@@ -1 +0,0 @@
|
||||
import{E as e,O as t,ft as n}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var r=n({load:()=>i});function i(){return{title:`Paramétrage : Base de connaissance`,back:`/settings`}}var a=t(`<div>Modèles</div>`);function o(t){var n=a();e(t,n)}export{o as component,r as universal};
|
||||
@@ -0,0 +1 @@
|
||||
import{N as e,bt as t,j as n}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";var r=t({load:()=>i});function i(){return{title:`Paramétrage : Canaux de communication`,back:`/settings`}}var a=e(`<div>Modèles</div>`);function o(e){var t=a();n(e,t)}export{o as component,r as universal};
|
||||
@@ -1 +0,0 @@
|
||||
import{E as e,O as t,ft as n}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var r=n({load:()=>i});function i(){return{title:`Paramétrage : Mémoire`,back:`/settings`}}var a=t(`<div>Modèles</div>`);function o(t){var n=a();e(t,n)}export{o as component,r as universal};
|
||||
@@ -0,0 +1 @@
|
||||
import{N as e,bt as t,j as n}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";var r=t({load:()=>i});function i(){return{title:`Paramétrage : Base de connaissance`,back:`/settings`}}var a=e(`<div>Modèles</div>`);function o(e){var t=a();n(e,t)}export{o as component,r as universal};
|
||||
@@ -1 +0,0 @@
|
||||
import{B as e,C as t,D as n,E as r,G as i,J as a,K as o,M as s,N as c,O as l,P as u,Q as d,T as f,X as p,_ as m,a as h,at as g,c as _,dt as v,ft as y,it as b,j as x,q as S,r as C,s as w,u as T,ut as E,w as D,x as O,z as k}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{t as A}from"../chunks/D35bQ8xD.js";import{n as j,t as M}from"../chunks/DRdGzrsF.js";import{t as ee}from"../chunks/D4uBfDnr.js";import{n as N}from"../chunks/D3MDK1zR.js";var P=y({load:()=>F}),F=async({params:e})=>({title:`Paramétrage : Modèles`,back:`../settings`,space:e.space,providerTypes:await A.providerTypes()}),I=l(`<li><label><input type="checkbox"/> </label></li>`),L=l(`<div><div class="formgroup my-2"><form><div class="formelement"><label for="name">Nom</label> <input id="name" type="text" class="textinput" placeholder="nom affiché"/> <!></div> <div class="formelement"><label for="type" class="block my-1 flex-1">Type</label> <!> <!></div> <div class="formelement"><label for="baseUrl">URL</label> <input id="baseUrl" type="text" class="textinput" placeholder="http://127.0.0.1:8080/v1" autocomplete="off"/> <!></div> <div class="formelement"><label for="apiKey">Clé API</label> <input id="apiKey" type="password" class="textinput" placeholder="clé API (optionnel)" autocomplete="new-password"/> <!></div> <div class="formelement"><label for="models">Models</label> <button class="bt small">Afficher les modèles disponibles</button> <ol class="ml-2"></ol> <div><!></div></div> <div class="text-right"><button type="button" class="bt">Annuler</button> <button type="submit" class="bt mx-4">Enregistrer</button></div></form></div></div>`);function R(t,l){g(l,!0);let y=h(l,`item`,7),x=d(a(y().models.split(`|`))),C=d(a(y().models.split(`|`).map(e=>({value:e,selected:!0})))),N=new j,P=e=>{e.focus()},F=e=>{e.preventDefault(),y().models=u(x).join(`|`),l.onSave(y())},R=e=>{e.preventDefault(),l.onCancel()},z=e=>{e.preventDefault(),N.clear(),A.getAvailableModels(y().teamId,y()).then(e=>{p(C,e.map(e=>({value:e,selected:u(x).includes(e)})),!0)}).catch(e=>{N.set(`models`,e)})},B=()=>{p(x,u(C).filter(e=>e.selected).map(e=>e.value),!0)};var V=L(),H=i(V),U=i(H),W=i(U),G=S(i(W),2);T(G),k(()=>_(G,()=>y().name,e=>y().name=e)),m(G,e=>P?.(e));var te=S(G,2);M(te,{key:`name`,get errors(){return l.errors}}),v(W);var K=S(W,2),q=S(i(K),2);ee(q,{get items(){return l.providerTypes},valueMode:`id`,get value(){return y().type},set value(e){y().type=e}});var ne=S(q,2);M(ne,{key:`type`,get errors(){return l.errors}}),v(K);var J=S(K,2),Y=S(i(J),2);T(Y);var re=S(Y,2);M(re,{key:`baseUrl`,get errors(){return l.errors}}),v(J);var X=S(J,2),Z=S(i(X),2);T(Z);var ie=S(Z,2);M(ie,{key:`apiKey`,get errors(){return l.errors}}),v(X);var Q=S(X,2),ae=S(i(Q),2),$=S(ae,2);O($,21,()=>u(C),e=>e.value,(t,a,c)=>{var l=n(),d=o(l),p=t=>{var n=I(),o=i(n),c=i(o);T(c);var l=S(c);v(o),v(n),e(()=>f(l,` ${u(a).value??``}`)),s(`change`,c,B),w(c,()=>u(a).selected,e=>u(a).selected=e),r(t,n)};D(d,e=>{u(a).value&&e(p)}),r(t,l)}),v($);var oe=S($,2),se=i(oe);M(se,{key:`models`,get errors(){return N}}),v(oe),v(Q);var ce=S(Q,2),le=i(ce);E(2),v(ce),v(U),v(H),v(V),c(`submit`,U,F),s(`change`,U,()=>{l.errors.clear()}),_(Y,()=>y().baseUrl,e=>y().baseUrl=e),_(Z,()=>y().apiKey,e=>y().apiKey=e),s(`click`,ae,z),s(`click`,le,R),r(t,V),b()}x([`change`,`click`]);var z=l(`<div class="rounded border border-dashed p-2 border-stone-500 cursor-pointer" tabindex="0"><i class="icon-plus"></i> Ajouter un fournisseur</div>`),B=l(`<div class="rounded border p-2 my-2 border-stone-500 flex"><div class="flex-1"><span> </span> <span class="text-stone-500"> </span></div> <button class="bt small mx-1"><i class="icon-edit"></i></button> <button class="bt small mx-1"><i class="icon-trash-empty"></i></button></div>`),V=l(`<div class="container"><!> <!></div>`);function H(c,l){g(l,!0);let m=d(a([])),h=d(!1),_=d(null),y=d(a({})),x=new j;C(async()=>{p(m,await A.getProviders(l.data.space),!0)});let w=()=>{u(_)||(x.clear(),p(y,{},!0),u(y).models=``,p(h,!0))},T=()=>{p(h,!1)},E=e=>{e.name||x.set(`name`,`Le nom est obligatoire !`),e.type||x.set(`type`,`Le type est obligatoire !`),e.baseUrl&&!N(e.baseUrl)&&x.set(`baseUrl`,`Mauvaise URL !`),x.size===0&&A.createProvider(l.data.space,e).then(e=>{u(m).push(e),p(h,!1),p(y,{},!0)}).catch(e=>{console.log(e)})},k=()=>{p(_,null),p(y,{},!0)},M=e=>{e.name||x.set(`name`,`Le nom est obligatoire !`),e.type||x.set(`type`,`Le type est obligatoire !`),e.baseUrl&&!N(e.baseUrl)&&x.set(`baseUrl`,`Mauvaise URL !`),x.size===0&&A.updateProvider(l.data.space,e).then(e=>{p(m,u(m).map(t=>e.id===t.id?e:t),!0),p(_,null),p(y,{},!0)}).catch(e=>{console.log(e)})},ee=async e=>{x.clear(),!u(h)&&A.getProvider(l.data.space,e).then(t=>{p(m,u(m).map(n=>n.id===e?t:n),!0),p(_,e,!0)})},P=e=>{A.deleteProvider(l.data.space,e).then(()=>{p(m,u(m).filter(t=>t.id!==e),!0)}).catch(e=>{console.log(e)})};var F=V(),I=i(F),L=e=>{var i=n(),a=o(i);t(a,()=>`new`,e=>{R(e,{get item(){return u(y)},get errors(){return x},get providerTypes(){rLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
import{N as e,bt as t,j as n}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";var r=t({load:()=>i});function i(){return{title:`Paramétrage : Mémoire`,back:`/settings`}}var a=e(`<div>Modèles</div>`);function o(e){var t=a();n(e,t)}export{o as component,r as universal};
|
||||
@@ -0,0 +1 @@
|
||||
import{$ as e,A as t,B as n,E as r,G as i,I as a,K as o,L as s,M as c,N as l,O as u,Q as d,R as f,U as p,X as m,Z as h,_t as g,a as _,b as v,bt as y,dt as b,et as x,ft as S,it as C,j as w,k as T,l as E,nt as D,p as O,u as k,vt as A}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";import{t as j}from"../chunks/BeIYuf23.js";import{n as M,t as N}from"../chunks/6mSXxE57.js";import{t as P}from"../chunks/DdGy_ePN.js";import{n as F}from"../chunks/D3MDK1zR.js";var I=y({load:()=>L}),L=async({params:e})=>({title:`Paramétrage : Modèles`,back:`../settings`,space:e.space,providerTypes:await j.providerTypes(),providers:await j.getProviders(e.space)}),R=l(`<li><label><input type="checkbox"/> </label></li>`),z=l(`<div><div class="formgroup my-2"><form><div class="formelement"><label for="name">Nom</label> <input id="name" type="text" class="textinput" placeholder="nom affiché"/> <!></div> <div class="formelement"><label for="type" class="block my-1 flex-1">Type</label> <!> <!></div> <div class="formelement"><label for="baseUrl">URL</label> <input id="baseUrl" type="text" class="textinput" placeholder="http://127.0.0.1:8080/v1" autocomplete="off"/> <!></div> <div class="formelement"><label for="apiKey">Clé API</label> <input id="apiKey" type="password" class="textinput" placeholder="clé API (optionnel)" autocomplete="new-password"/> <!></div> <div class="formelement"><label for="models">Models</label> <button class="bt small">Afficher les modèles disponibles</button> <ol class="ml-2"></ol> <div><!></div></div> <div class="text-right"><button type="button" class="bt">Annuler</button> <button type="submit" class="bt mx-4">Enregistrer</button></div></form></div></div>`);function B(a,l){S(l,!0);let u=_(l,`item`,7),p=C(x(u().models.split(`|`))),m=C(x(u().models.split(`|`).map(e=>({value:e,selected:!0})))),y=new M,F=e=>{e.focus()},I=e=>{e.preventDefault(),u().models=n(p).join(`|`),l.onSave(u())},L=e=>{e.preventDefault(),l.onCancel()},B=e=>{e.preventDefault(),y.clear(),j.getAvailableModels(u().teamId,u()).then(e=>{D(m,e.map(e=>({value:e,selected:n(p).includes(e)})),!0)}).catch(e=>{y.set(`models`,e)})},V=()=>{D(p,n(m).filter(e=>e.selected).map(e=>e.value),!0)};var H=z(),U=h(H),W=h(U),G=h(W),K=e(h(G),2);O(K),i(()=>k(K,()=>u().name,e=>u().name=e)),v(K,e=>F?.(e));var ee=e(K,2);N(ee,{key:`name`,get errors(){return l.errors}}),A(G);var q=e(G,2),te=e(h(q),2);P(te,{get items(){return l.providerTypes},valueMode:`id`,get value(){return u().type},set value(e){u().type=e}});var ne=e(te,2);N(ne,{key:`type`,get errors(){return l.errors}}),A(q);var J=e(q,2),Y=e(h(J),2);O(Y);var re=e(Y,2);N(re,{key:`baseUrl`,get errors(){return l.errors}}),A(J);var X=e(J,2),Z=e(h(X),2);O(Z);var ie=e(Z,2);N(ie,{key:`apiKey`,get errors(){return l.errors}}),A(X);var Q=e(X,2),ae=e(h(Q),2),$=e(ae,2);r($,21,()=>n(m),e=>e.value,(r,i,a)=>{var l=c(),u=d(l),f=r=>{var a=R(),c=h(a),l=h(c);O(l);var u=e(l);A(c),A(a),o(()=>t(u,` ${n(i).value??``}`)),s(`change`,l,V),E(l,()=>n(i).selected,e=>n(i).selected=e),w(r,a)};T(u,e=>{n(i).value&&e(f)}),w(r,l)}),A($);var oe=e($,2),se=h(oe);N(se,{key:`models`,get errors(){return y}}),A(oe),A(Q);var ce=e(Q,2),le=h(ce);g(2),A(ce),A(W),A(U),A(H),f(`submit`,W,I),s(`change`,W,()=>{l.errors.clear()}),k(Y,()=>u().baseUrl,e=>u().baseUrl=e),k(Z,()=>u().apiKey,e=>u().apiKey=e),s(`click`,ae,B),s(`click`,le,L),w(a,H),b()}a([`change`,`click`]);var V=l(`<button class="rounded w-full border border-dashed p-2 border-stone-500 cursor-pointer" tabindex="0"><i class="icon-plus"></i> Ajouter un fournisseur</button>`),H=l(`<div class="rounded border p-2 my-2 border-stone-500 flex formgroup"><div class="flex-1 p-2"><span> </span> <span class="text-stone-500"> </span></div> <button class="bt small mx-1" aria-label="Modifier"><i class="icon-edit"></i></button> <button class="bt small mx-1" aria-label="Supprimer"><i class="icon-trash-empty"></i></button></div>`),U=l(`<div class="container"><!> <!></div>`);function W(i,a){S(a,!0);let l=C(x(p(()=>a.data.providers))),g=C(!1),_=C(null),v=C(x({})),y=new M,E=()=>{n(_)||(y.clear(),D(v,{},!0),n(v).models=``,D(g,!0))},O=()=>{D(g,!1)},k=e=>{e.name||y.set(`name`,`Le nom est obligatoire !`),e.type||y.set(`type`,`Le type est obligatoire !`),e.baseUrl&&!F(e.baseUrl)&&y.set(`baseUrl`,`Mauvaise URL !`),y.size===0&&j.createProvider(a.data.space,e).then(e=>{n(l).push(e),D(g,!1),D(v,{},!0)}).catch(e=>{console.log(e)})},N=()=>{D(_,null),D(v,{},!0)},P=e=>{e.name||y.set(`name`,`Le nom est obligatoire !`),e.type||y.set(`type`,`Le type est obligatoire !`),e.baseUrl&&!F(e.baseUrl)&&y.set(`baseUrl`,`Mauvaise URL !`),y.size===0&&j.updateProvider(a.data.space,e).then(e=>{D(l,n(l).map(t=>e.id===t.id?e:t),!0),D(_,null),D(v,{},!0)}).catch(e=>{console.log(e)})},I=async e=>{y.clear(),!n(g)&&j.getProvider(a.data.space,e).then(t=>{D(l,n(l).map(n=>n.id===e?t:n),!0),D(_,e,!0)})},L=e=>{j.deleteProvider(a.data.space,e).then(()=>{D(l,n(l).filter(t=>t.id!==e),!0)}).catch(e=>{console.log(e)})},R=e=>{e.key===`Escape`&&N()};var z=U();f(`keydown
Line truncated
|
||||
@@ -1 +0,0 @@
|
||||
import{E as e,O as t,ft as n}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var r=n({load:()=>i});function i(){return{title:`Paramétrage : Skills`,back:`/settings`}}var a=t(`<div>Modèles</div>`);function o(t){var n=a();e(t,n)}export{o as component,r as universal};
|
||||
@@ -1 +0,0 @@
|
||||
import{E as e,O as t,ft as n}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var r=n({load:()=>i});function i(){return{title:`Paramétrage : Outils`,back:`/settings`}}var a=t(`<div>Modèles</div>`);function o(t){var n=a();e(t,n)}export{o as component,r as universal};
|
||||
@@ -0,0 +1 @@
|
||||
import{N as e,bt as t,j as n}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";var r=t({load:()=>i});function i(){return{title:`Paramétrage : Skills`,back:`/settings`}}var a=e(`<div>Modèles</div>`);function o(e){var t=a();n(e,t)}export{o as component,r as universal};
|
||||
@@ -0,0 +1 @@
|
||||
import{$ as e,A as t,B as n,E as r,G as i,I as a,K as o,L as s,M as c,N as l,O as u,Q as d,R as f,U as p,X as m,Z as h,_t as g,a as _,b as v,bt as y,dt as b,et as x,ft as S,it as C,j as w,k as T,l as E,m as D,nt as O,p as k,r as A,u as j,vt as M}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";import{t as N}from"../chunks/DtfiK5fE.js";import{n as P,t as F}from"../chunks/6mSXxE57.js";var I={getTools:e=>N.get(`/tool/${e}`),getTool:(e,t)=>N.get(`/tool/${e}/${t}`),getToolParams:(e,t)=>N.get(`/tool/${e}/${t}/params`),createTool:(e,t)=>N.post(`/tool/${e}`,t),updateTool:(e,t)=>N.put(`/tool/${e}`,t)},L=y({load:()=>R}),R=async({params:e})=>({title:`Paramétrage : Outils`,back:`../settings`,space:e.space,tools:await I.getTools(e.space)}),z=l(`<input id="enabled" type="checkbox" class="checkboxinput"/>`),B=l(`<input type="number" class="textinput"/>`),V=l(`<input class="textinput"/>`),H=l(`<div class="formelement"><label> </label> <!> <!></div>`),U=l(`<div><div class="formgroup my-2"><form><div class="formelement"><label for="name">Nom</label> <input id="name" type="text" class="textinput" placeholder="nom affiché" disabled=""/> <!></div> <div class="formelement"><label for="enabled">Actif</label> <input id="enabled" type="checkbox" class="checkboxinput"/> <!></div> <!> <div class="text-right"><button type="button" class="bt">Annuler</button> <button type="submit" class="bt mx-4">Enregistrer</button></div></form></div></div>`);function W(a,c){S(c,!0);let l=_(c,`item`,7),u=x(p(()=>l())),d=C(x([])),m=new P;A(async()=>{O(d,await I.getToolParams(l().teamId,l().id),!0),m=new P(Object.entries(JSON.parse(l().configuration)))});let y=e=>{e.focus()},N=e=>{e.preventDefault(),l().configuration=JSON.stringify(Object.fromEntries(m)),c.onSave(l())},L=e=>{e.preventDefault(),c.onCancel()};var R=U(),W=h(R),G=h(W),K=h(G),q=e(h(K),2);k(q),i(()=>j(q,()=>u.name,e=>u.name=e)),v(q,e=>y?.(e));var J=e(q,2);F(J,{key:`name`,get errors(){return c.errors}}),M(K);var Y=e(K,2),X=e(h(Y),2);k(X);var Z=e(X,2);F(Z,{key:`baseUrl`,get errors(){return c.errors}}),M(Y);var Q=e(Y,2);r(Q,17,()=>n(d),e=>e.name,(r,i)=>{var a=H(),s=h(a),l=h(s,!0);M(s);var u=e(s,2),d=e=>{var t=z();k(t),E(t,()=>m.get(n(i).name)??``,e=>m.set(n(i).name,e)),w(e,t)},f=e=>{var t=B();k(t),o(()=>{D(t,`id`,n(i).name),D(t,`placeholder`,n(i).description)}),j(t,()=>m.get(n(i).name)??``,e=>m.set(n(i).name,e)),w(e,t)},p=e=>{var t=V();k(t),o(()=>{D(t,`id`,n(i).name),D(t,`placeholder`,n(i).description)}),j(t,()=>m.get(n(i).name)??``,e=>m.set(n(i).name,e)),w(e,t)};T(u,e=>{n(i).type==`bool`?e(d):n(i).type==`int`?e(f,1):e(p,-1)});var g=e(u,2);F(g,{key:`baseUrl`,get errors(){return c.errors}}),M(a),o(()=>{D(s,`for`,n(i).name),t(l,n(i).name)}),w(r,a)});var $=e(Q,2),ee=h($);g(2),M($),M(G),M(W),M(R),f(`submit`,G,N),s(`change`,G,()=>{c.errors.clear()}),E(X,()=>u.enabled,e=>u.enabled=e),s(`click`,ee,L),w(a,R),b()}a([`change`,`click`]);var G=l(`<div class="rounded border p-2 my-2 border-stone-500 flex formgroup"><div class="flex-1 p-2"><span> </span> <span class="text-stone-500"> </span></div> <button class="bt small mx-1" aria-label="Modifier"><i class="icon-edit"></i></button></div>`),K=l(`<div class="container"></div>`);function q(i,a){S(a,!0);let l=C(x(p(()=>a.data.tools))),g=C(null),_=new P,v=()=>{O(g,null)},y=e=>{_.size===0&&I.updateTool(a.data.space,e).then(e=>{O(l,n(l).map(t=>e.id===t.id?e:t),!0),O(g,null)}).catch(e=>{console.log(e)})},E=async e=>{_.clear(),I.getTool(a.data.space,e).then(t=>{O(l,n(l).map(n=>n.id===e?t:n),!0),O(g,e,!0)})},D=e=>{e.key===`Escape`&&v()};var k=K();f(`keydown`,m,D),r(k,21,()=>n(l),e=>e.id,(r,i)=>{var a=c(),l=d(a),f=e=>{var t=c(),r=d(t);u(r,()=>n(i).id,e=>{W(e,{get item(){return n(i)},get errors(){return _},onSave:y,onCancel:v})}),w(e,t)},p=r=>{var a=G(),c=h(a),l=h(c),u=h(l,!0);M(l);var d=e(l,2),f=h(d);M(d),M(c);var p=e(c,2);M(a),o(()=>{t(u,n(i).name),t(f,`(${n(i).enabled??``})`)}),s(`click`,p,()=>E(n(i).id)),w(r,a)};T(l,e=>{n(g)===n(i).id?e(f):e(p,-1)}),w(r,a)}),M(k),w(i,k),b()}a([`click`]);export{q as component,L as universal};
|
||||
@@ -1 +0,0 @@
|
||||
import{E as e,O as t,ft as n}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var r=n({load:()=>i});function i(){return{title:`Paramétrage : Utilisateurs`,back:`/settings`}}var a=t(`<div>Modèles</div>`);function o(t){var n=a();e(t,n)}export{o as component,r as universal};
|
||||
@@ -0,0 +1 @@
|
||||
import{N as e,bt as t,j as n}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";var r=t({load:()=>i});function i(){return{title:`Paramétrage : Utilisateurs`,back:`/settings`}}var a=e(`<div>Modèles</div>`);function o(e){var t=a();n(e,t)}export{o as component,r as universal};
|
||||
@@ -1 +0,0 @@
|
||||
import{E as e,O as t,ft as n}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";var r=n({load:()=>i});function i(){return{title:`Tâches`}}var a=t(`<div>Tâches</div>`);function o(t){var n=a();e(t,n)}export{o as component,r as universal};
|
||||
@@ -0,0 +1,4 @@
|
||||
import{$ as e,A as t,B as n,E as r,I as i,K as a,L as o,M as s,N as c,Q as l,R as u,Z as d,at as f,bt as p,dt as m,et as h,ft as g,it as _,j as v,k as y,l as b,m as x,nt as S,p as C,q as w,st as T,u as E,v as D,vt as O}from"../chunks/DGrvnoUE.js";import{n as k}from"../chunks/BZuNOv8h.js";import"../chunks/xihTtKlq.js";import{t as A}from"../chunks/ffLBjIeN.js";import"../chunks/CY9OlB4q.js";import{t as j}from"../chunks/BYYkuYgk.js";import{n as M,r as N,t as P}from"../chunks/Bht2nTIZ.js";var F=p({load:()=>I}),I=async({params:e})=>({title:`Tâches`,tasks:await N.listTasks(e.space),space:e.space}),ee=c(`<p class="my-4 text-center text-sm text-stone-500">Aucune tâche planifiée. Demandez-en une dans le chat
|
||||
(« résume-moi l'actualité tous les jours à 8h »).</p>`),L=c(`<div class="dark:bg-stone-800 rounded p-3 my-2"><form><div class="formgroup my-2"><div class="formelement"><label>Titre</label> <input class="textinput" type="text"/></div> <div class="formelement"><label>Planification (cron)</label> <input class="textinput" type="text"/></div> <div class="formelement"><label>Répétition</label> <input type="checkbox"/></div></div> <div class="formelement"><label>Prompt</label> <textarea class="textinput w-full" rows="4"></textarea></div> <div class="mt-2 flex gap-2"><button class="bt small" type="submit"> </button> <button class="bt small" type="button">Annuler</button></div></form></div>`),R=c(`<p class="text-sm text-stone-500">Chargement...</p>`),z=c(`<p class="text-sm text-stone-500">Aucune conversation pour cette tâche.</p>`),B=c(`<li class="my-1 text-sm"><a class="text-sky-700 dark:text-sky-300"> </a> <span class="ml-2 text-xs text-stone-500"> </span></li>`),V=c(`<ul class="mt-1"></ul>`),H=c(`<div class="mt-2 border-t border-stone-600 pt-2"><div class="text-xs font-medium uppercase text-stone-500">Conversations liées</div> <!></div>`),U=c(`<div><div class="flex flex-row items-center"><div class="flex-1"><div class="font-bold text-base"> </div> <div class="text-xs text-stone-500"> </div></div> <button class="bt small" title="Conversations liées"><i class="icon-chat"></i></button> <button class="bt small" title="Modifier la tâche"><i class="icon-edit"></i></button> <button class="bt small" title="Supprimer la tâche"><i class="icon-trash-empty"></i></button></div> <p class="mt-1 text-sm text-stone-600 dark:text-stone-400"> </p> <span class="text-xs"> </span> <!></div>`),te=c(`<div class="container"><!> <!></div> <!>`,1);function W(i,c){g(c,!0);let p=f(()=>c.data.tasks??[]),F=_(null),I=_(null),W=_(!1),G=_(h(A.url.searchParams.get(`task`))),K=_(h({})),q=_(!1),J=async e=>{S(q,!0);try{let t=await j.allChats(c.data.space,0,void 0,e);S(K,{...n(K),[e]:t??[]},!0)}catch{M(`failure`,`Erreur lors du chargement des conversations`)}finally{S(q,!1)}},ne=async e=>{if(n(G)===e.id){S(G,null);return}S(G,e.id,!0),n(K)[e.id]||await J(e.id)},Y=!1;w(()=>{!Y&&n(G)&&(Y=!0,J(n(G))),Y=!0});let X=e=>{if(!e)return`—`;let t=new Date(e);return Number.isNaN(t.getTime())?`—`:t.toLocaleString(`fr-FR`,{day:`numeric`,month:`long`,year:`numeric`,hour:`2-digit`,minute:`2-digit`})},re=e=>{S(F,{...e},!0)},ie=async e=>{if(e.preventDefault(),n(F)){if(!n(F).label.trim()||!n(F).prompt.trim()||!n(F).cron.trim()){M(`failure`,`Titre, prompt et planification sont obligatoires`);return}S(W,!0);try{await N.updateTask(c.data.space,n(F).id,{label:n(F).label,prompt:n(F).prompt,cron:n(F).cron,repeat:n(F).repeat}),M(`success`,`Tâche mise à jour`),S(F,null),await k()}catch{M(`failure`,`Erreur lors de la mise à jour de la tâche`)}finally{S(W,!1)}}},ae=async()=>{if(n(I))try{await N.deleteTask(c.data.space,n(I).id),M(`success`,`Tâche supprimée`),await k()}catch{M(`failure`,`Erreur lors de la suppression de la tâche`)}finally{S(I,null)}};var Z=te(),Q=l(Z),$=d(Q),oe=e=>{var t=ee();v(e,t)};y($,e=>{n(p).length===0&&e(oe)});var se=e($,2);r(se,17,()=>n(p),e=>e.id,(i,f)=>{var p=s(),m=l(p),h=r=>{var i=L(),s=d(i),c=d(s),l=d(c),p=d(l),m=e(p,2);C(m),O(l);var h=e(l,2),g=d(h),_=e(g,2);C(_),O(h);var y=e(h,2),w=d(y),D=e(w,2);C(D),O(y),O(c);var k=e(c,2),A=d(k),j=e(A,2);T(j),O(k);var M=e(k,2),N=d(M),P=d(N,!0);O(N);var I=e(N,2);O(M),O(s),O(i),a(()=>{x(p,`for`,`label-${n(f).id}`),x(m,`id`,`label-${n(f).id}`),x(g,`for`,`cron-${n(f).id}`),x(_,`id`,`cron-${n(f).id}`),x(w,`for`,`repeat-${n(f).id}`),x(D,`id`,`repeat-${n(f).id}`),x(A,`for`,`prompt-${n(f).id}`),x(j,`id`,`prompt-${n(f).id}`),N.disabled=n(W),t(P,n(W)?`Enregistrement...`:`Enregistrer`)}),u(`submit`,s,ie),E(m,()=>n(F).label,e=>n(F).label=e),E(_,()=>n(F).cron,e=>n(F).cron=e),b(D,()=>n(F).repeat,e=>n(F).repeat=e),E(j,()=>n(F).prompt,e=>n(F).prompt=e),o(`click`,I,()=>S(F,null)),v(r,i)},g=i=>{var s=U();let l;var u=d(s),p=d(u),m=d(p),h=d(m,!0);O(m);var g=e(m,2),_=d(g);O(g),O(p);var b=e(p,2),C=e(b,2),w=e(C,2);O(u);var T=e(u,2),E=d(T,!0);O(T);var k=e(T,2),A=d(k);O(k);var j=e(k,2),M=i=>{var o=H(),s=e(d(o),2),l=e=>{var t=R();v(e,t)},u=e=>{var t=z();v(e,t)},p=i=>{var o=V();r(o,21,()=>n(K)[n(f).id],e=>e.id,(r,i)=>{var o=B(),s=d(o),l=d(s,!0);O(s);var u=e(s,2),f=d(u,!0);O(u),O(o),a(e=>{x(s,`href`,`/${c.data.space}/${n(i).id}`),t(l,n(i).title),t(f,e)},[()=>X(n(i)._date_created)]),v(r,o)}),O(o),v(i,o)};y(s,e=>{n(q)&&!n(K)[n(f).id]?e(l):(n(K)[n(f).id]??[]).length===0?e(u,1):e(p,-1)}),O(o),v(i,o)};y(j,e=>{n(G)===n(f).id&&e(M)}),O(s),a(e=>{l=D(s,1,`dark:bg-stone-800 rounded p-2 my-2`,null,l,{"ring-2":n(G)===n(f).id,"ring-sky-500":n(G)===n(f).id}),t(h,n(f).label),t(_,`${n(f).cron??``}
|
||||
${n(f).repeat?`· répétée`:`· ponctuelle`}
|
||||
${n(f).status===`done`?`· terminée`:``}`),t(E,n(f).prompt),t(A,`Prochaine exécution : ${e??``}`)},[()=>X(n(f).nextDatetime)]),o(`click`,b,()=>ne(n(f))),o(`click`,C,()=>re(n(f))),o(`click`,w,()=>S(I,n(f),!0)),v(i,s)};y(m,e=>{n(F)&&n(F).id===n(f).id?e(h):e(g,-1)}),v(i,p)}),O(Q);var ce=e(Q,2);{let e=f(()=>n(I)!==null),t=f(()=>`Voulez-vous vraiment supprimer « ${n(I)?.label} » ? Cette action est irréversible.`);P(ce,{get open(){return n(e)},title:`Supprimer la tâche`,get message(){return n(t)},confirmLabel:`Supprimer`,onConfirm:ae,onCancel:()=>S(I,null)})}v(i,Z),m()}i([`click`]);export{W as component,F as universal};
|
||||
@@ -1 +0,0 @@
|
||||
import{$ as e,A as t,B as n,E as r,G as i,J as a,K as o,L as s,M as c,N as l,O as u,P as d,Q as f,T as p,V as m,X as h,_ as ee,a as g,at as te,c as _,dt as v,ft as y,h as b,it as ne,j as x,p as S,q as C,r as re,u as w,ut as T,w as E,x as ie,z as ae}from"../chunks/39JK5HWF.js";import"../chunks/xihTtKlq.js";import{n as oe,t as se}from"../chunks/DTCALkcP.js";import{t as D}from"../chunks/BqkZ0BZ5.js";import{t as O}from"../chunks/BEgKbVuy.js";import{n as ce,t as k}from"../chunks/DRdGzrsF.js";import{t as le}from"../chunks/D4uBfDnr.js";import{t as ue}from"../chunks/D3MDK1zR.js";var de=y({load:()=>fe}),fe=async()=>({title:`Profil`,me:await O.me()}),A=function(e){return e[e.None=0]=`None`,e[e.Changed=1]=`Changed`,e[e.Saving=2]=`Saving`,e[e.Saved=3]=`Saved`,e[e.Error=4]=`Error`,e}({}),pe={listTimezones:()=>D.get(`/misc/timezones`)},me=u(`<i class="incon-spin1 anomate-spin"></i> Enregistrement en cours`,1),he=u(`<i class="incon-ok anomate-spin"></i> Enregistrement réussi`,1),ge=u(`<button><!></button>`);function _e(e,a){te(a,!0);let o=g(a,`state`,7),s=g(a,`disabled`,3,!1);m(()=>{(o()===A.Saved||o()===A.Error)&&setTimeout(()=>{o(A.None)},2e3)});let l=()=>{a.handleClick&&(o()===A.Changed||o()===A.None)&&a.handleClick()};var u=ge();let d;var f=i(u),h=e=>{var i=t();n(()=>p(i,a.label)),r(e,i)},ee=e=>{var t=me();T(),r(e,t)},_=e=>{var t=he();T(),r(e,t)},y=e=>{var n=t(`Echec`);r(e,n)};E(f,e=>{o()==A.None||o()==A.Changed?e(h):o()==A.Saving?e(ee,1):o()==A.Saved?e(_,2):o()==A.Error&&e(y,3)}),v(u),n(()=>{u.disabled=s(),d=b(u,1,`bt`,null,d,{disabled:s(),changed:A.Changed,saving:A.Saving,saved:A.Saved,error:A.Error})}),c(`click`,u,l),r(e,u),ne()}x([`click`]);var ve=u(`<div class="m-2"><div class="dark:bg-stone-700 bg-stone-300 rounded inline px-2 py-1 text-sm"><span class="inline-block w-20"> </span> <span> </span> <button class="ml-4 pointer" title="Annuler"><i class="icon-cancel"></i></button></div></div>`),ye=u(`<div class="mb-4 formgroup container"><h2>Mon compte</h2> <form><div class="formelement"><label for="email" class="my-1 flex-1">Email</label> <input id="email" type="text" class="textinput"/> <!></div> <div class="grid grid-cols-1 gap-x-4 md:grid-cols-2"><div class="formelement"><label for="firstname" class=" my-1 flex-1">Prénom</label> <input id="firstname" type="text" class="textinput"/> <!></div> <div class="formelement"><label for="lastname" class="my-1 flex-1">Nom</label> <input id="lastname" type="text" class="textinput"/> <!></div></div> <div class="grid grid-cols-1 gap-x-4 md:grid-cols-2"><div class="formelement"><label for="lang" class="block my-1 flex-1">Langue</label> <select class="textinput"><option>English</option><option>Français</option></select> <!></div> <div class="formelement"><label for="timezone" class="block my-1 flex-1">Timezone</label> <!> <!></div></div> <div class="formelement"><label for="theme" class="block my-1 flex-1">Thème</label> <select class="textinput"><option>Automatique</option><option>Sombre</option><option>Clair</option></select> <!></div> <!></form></div> <div class="mb-4 formgroup container"><h2>Canaux de communication</h2> <!> <div class="flex gap-2 mt-6"><div><select class="textinput"><option></option><option>Email</option><option>Signal</option><option>Telegram</option><option>Whatsapp</option></select></div> <div class="flex-1"><input type="text" class="textinput"/></div> <div><!></div></div></div>`,1);function j(t,u){te(u,!0);let g=a(s(()=>u.data.me.user)),y=f(a([])),b=f(a({type:``,address:``})),x=f(a([])),T=new ce,E=f(a(A.None)),D=f(a(A.None));re(async()=>{h(y,await O.listAddresses(g.id),!0),h(x,(await pe.listTimezones()).map(e=>({value:e,label:e})),!0)}),m(()=>{oe(g.theme),se()});let de=e=>{e.preventDefault(),h(E,A.Saving,!0),ue(g.email)||T.set(`email`,`Adresse email invalide !`),g.firstname||T.set(`firstname`,`Le prénom est obligatoire !`),g.lastname||T.set(`lastname`,`Le nom est obligatoire !`),T.size==0&&O.update(g.id,g).then(()=>{h(E,A.Saved,!0)}).catch(e=>{h(E,A.Saving,!0),console.error(e)})},fe=()=>{h(D,A.Saving,!0),O.createAddress(g.id,d(b)).then(e=>{d(y).push(e),h(b,{type:``,address:``},!0),h(D,A.Saved,!0)}).catch(e=>{console.log(e)})},me=e=>{O.deleteAddress(g.id,e).then(()=>{h(y,d(y).filter(t=>t.id!=e),!0)}).catch(e=>{console.log(e)})},he=()=>{T.clear(),h(E,A.Changed,!0)},ge=e=>{e.focus()};var j=ye(),M=o(j),N=C(i(M),2),P=i(N),F=C(i(P),2);w(F),ae(()=>_(F,()=>g.email,e=>g.email=e)),ee(F,e=>ge?.(e));var be=C(F,2);k(be,{key:`email`,get errors(){return T}}),v(P);var I=C(P,2),L=i(I),R=C(i(L),2);w(R);var xe=C(R,2);k(xe,{key:`firstname`,get errors(){return T}}),v(L);var Se=C(L,2),z=C(i(Se),2);w(z);var Ce=C(z,2);k(Ce,{key:`lastname`,get errors(){return T}}),v(Se),v(I);var B=C(I,2),V=i(B),H=C(i(V),2),U=i(H);U.value=U.__value=`en`;var we=C(U);we.value=we.__value=`fr`,v(H);var Te=C(H,2);k(Te,{key:`lang`,get errors(){return T}}),v(V);var Ee=C(V,2),De=C(i(Ee),2);le(De,{get items(){return d(x)},valueMode:`id`,get value(){return g.timezone},set value(e){g.timezone=e}});var
Line truncated
|
||||
@@ -1 +0,0 @@
|
||||
import{B as e,E as t,G as n,J as r,K as i,M as a,N as o,O as s,at as c,c as l,d as u,dt as d,et as f,it as p,j as m,q as h,u as g,ut as _,w as v}from"../chunks/39JK5HWF.js";import{t as y}from"../chunks/D3up1JC1.js";import"../chunks/xihTtKlq.js";import"../chunks/B5isPFOt.js";import{i as b,n as x}from"../chunks/BqkZ0BZ5.js";import{t as S}from"../chunks/BRXBpvt8.js";import{t as C}from"../chunks/DNQ6C3zq.js";var w=s(`<form><div class="flex"><label for="email" class="block mb-2 flex-1">Email</label> <a href="/recover-password" class="text-xs">Mot de passe oublié ?</a></div> <div class="flex flex-row"><input type="text" class="textinput mr-1"/> <button class="bt" title="page suivante"><i class="icon-right-big"></i></button></div></form> <div class="my-4 text-xs"><a href="/register">Pas encore de compte ? Incrivez-vous.</a></div>`,1),T=s(`<form><div class="flex"><label for="email" class="block mb-2 flex-1">Mot de passe</label></div> <div class="flex flex-row"><input type="password" class="textinput mr-1"/> <button class="bt">Entrer</button></div> <a href="/login" class="text-xs">← Retour</a></form>`),E=s(`<div class="h-full w-full flex items-center public"><div class="m-auto w-96 max-w-full p-4"><img class="h-10 mb-4" alt="logo"/> <h1>Identifiez-vous ...</h1> <!> <hr class="my-4"/> <div class="grid grid-cols-[repeat(auto-fit,minmax(150px,1fr))] gap-4"><button class="bt p-5 text-center" title="Connexion avec Google"><i class="icon-google"></i></button> <button class="bt p-5 text-center" title="Connexion avec Facebook"><i class="icon-facebook"></i></button> <button class="bt p-5 text-center" title="Connexion avec Github"><i class="icon-github"></i></button> <button class="bt p-5 text-center" title="Connexion avec Apple"><i class="icon-apple"></i></button></div></div></div>`);function D(s,m){c(m,!0);let D=r({email:``,password:``,validemail:!1}),O=()=>{D.validemail=!0},k=e=>{e.preventDefault(),C.login(D).then(e=>{x.user=e.user,b(e.accessToken),y(`/`)}).catch(e=>{console.log(e)})};var A=E(),j=n(A),M=n(j),N=h(M,4),P=e=>{var r=w(),a=i(r),s=h(n(a),2),c=n(s);g(c),f(c,!0),_(2),d(s),d(a),_(2),o(`submit`,a,O),l(c,()=>D.email,e=>D.email=e),t(e,r)},F=e=>{var r=T(),i=h(n(r),2),s=n(i);g(s),f(s,!0),_(2),d(i);var c=h(i,2);d(r),o(`submit`,r,k),l(s,()=>D.password,e=>D.password=e),a(`click`,c,()=>{D.validemail=!1}),t(e,r)};v(N,e=>{D.validemail?e(F,-1):e(P)}),_(4),d(j),d(A),e(()=>u(M,`src`,S)),t(s,A),p()}m([`click`]);export{D as component};
|
||||
@@ -0,0 +1 @@
|
||||
import{$ as e,A as t,B as n,E as r,F as i,G as a,I as o,K as s,L as c,N as l,Q as u,R as d,U as f,Z as p,_t as m,a as h,at as ee,b as te,bt as g,dt as ne,et as _,ft as re,g as v,it as y,j as b,k as x,nt as S,p as C,q as ie,r as ae,u as w,v as T,vt as E}from"../chunks/DGrvnoUE.js";import"../chunks/xihTtKlq.js";import{n as oe,t as se}from"../chunks/DTCALkcP.js";import{t as D}from"../chunks/DtfiK5fE.js";import{t as O}from"../chunks/zsRwiIdn.js";import{n as ce,t as k}from"../chunks/6mSXxE57.js";import{t as le}from"../chunks/DdGy_ePN.js";import{t as ue}from"../chunks/D3MDK1zR.js";var de=g({load:()=>fe}),fe=async()=>({title:`Profil`,me:await O.me()}),A=function(e){return e[e.None=0]=`None`,e[e.Changed=1]=`Changed`,e[e.Saving=2]=`Saving`,e[e.Saved=3]=`Saved`,e[e.Error=4]=`Error`,e}({}),pe={listTimezones:()=>D.get(`/misc/timezones`)},me=l(`<i class="incon-spin1 anomate-spin"></i> Enregistrement en cours`,1),he=l(`<i class="incon-ok anomate-spin"></i> Enregistrement réussi`,1),ge=l(`<button><!></button>`);function _e(e,n){re(n,!0);let r=h(n,`state`,7),a=h(n,`disabled`,3,!1);ie(()=>{(r()===A.Saved||r()===A.Error)&&setTimeout(()=>{r(A.None)},2e3)});let o=()=>{n.handleClick&&(r()===A.Changed||r()===A.None)&&n.handleClick()};var l=ge();let u;var d=p(l),f=e=>{var r=i();s(()=>t(r,n.label)),b(e,r)},ee=e=>{var t=me();m(),b(e,t)},te=e=>{var t=he();m(),b(e,t)},g=e=>{var t=i(`Echec`);b(e,t)};x(d,e=>{r()==A.None||r()==A.Changed?e(f):r()==A.Saving?e(ee,1):r()==A.Saved?e(te,2):r()==A.Error&&e(g,3)}),E(l),s(()=>{l.disabled=a(),u=T(l,1,`bt`,null,u,{disabled:a(),changed:r()===A.Changed,saving:r()===A.Saving,saved:r()===A.Saved,error:r()===A.Error})}),c(`click`,l,o),b(e,l),ne()}o([`click`]);var ve=l(`<div class="m-2"><div class="dark:bg-stone-700 bg-stone-300 rounded inline px-2 py-1 text-sm"><span class="inline-block w-20"> </span> <span> </span> <button class="ml-4 pointer" title="Annuler"><i class="icon-cancel"></i></button></div></div>`),ye=l(`<div class="mb-4 formgroup container"><h2>Mon compte</h2> <form><div class="formelement"><label for="email" class="my-1 flex-1">Email</label> <input id="email" type="text" class="textinput"/> <!></div> <div class="grid grid-cols-1 gap-x-4 md:grid-cols-2"><div class="formelement"><label for="firstname" class=" my-1 flex-1">Prénom</label> <input id="firstname" type="text" class="textinput"/> <!></div> <div class="formelement"><label for="lastname" class="my-1 flex-1">Nom</label> <input id="lastname" type="text" class="textinput"/> <!></div></div> <div class="grid grid-cols-1 gap-x-4 md:grid-cols-2"><div class="formelement"><label for="lang" class="block my-1 flex-1">Langue</label> <select class="textinput"><option>English</option><option>Français</option></select> <!></div> <div class="formelement"><label for="timezone" class="block my-1 flex-1">Timezone</label> <!> <!></div></div> <div class="formelement"><label for="theme" class="block my-1 flex-1">Thème</label> <select class="textinput"><option>Automatique</option><option>Sombre</option><option>Clair</option></select> <!></div> <!></form></div> <div class="mb-4 formgroup container"><h2>Canaux de communication</h2> <!> <div class="flex gap-2 mt-6"><div><select class="textinput"><option></option><option>Email</option><option>Signal</option><option>Telegram</option><option>Whatsapp</option></select></div> <div class="flex-1"><input type="text" class="textinput"/></div> <div><!></div></div></div>`,1);function j(i,o){re(o,!0);let l=_(f(()=>o.data.me.user)),m=y(_([])),h=y(_({type:``,address:``})),g=y(_([])),x=new ce,T=y(_(A.None)),D=y(_(A.None));ae(async()=>{S(m,await O.listAddresses(l.id),!0),S(g,(await pe.listTimezones()).map(e=>({value:e,label:e})),!0)}),ie(()=>{oe(l.theme),se()});let de=e=>{e.preventDefault(),S(T,A.Saving,!0),ue(l.email)||x.set(`email`,`Adresse email invalide !`),l.firstname||x.set(`firstname`,`Le prénom est obligatoire !`),l.lastname||x.set(`lastname`,`Le nom est obligatoire !`),x.size==0&&O.update(l.id,l).then(()=>{S(T,A.Saved,!0)}).catch(e=>{S(T,A.Saving,!0),console.error(e)})},fe=()=>{S(D,A.Saving,!0),O.createAddress(l.id,n(h)).then(e=>{n(m).push(e),S(h,{type:``,address:``},!0),S(D,A.Saved,!0)}).catch(e=>{console.log(e)})},me=e=>{O.deleteAddress(l.id,e).then(()=>{S(m,n(m).filter(t=>t.id!=e),!0)}).catch(e=>{console.log(e)})},he=()=>{x.clear(),S(T,A.Changed,!0)},ge=e=>{e.focus()};var j=ye(),M=u(j),N=e(p(M),2),P=p(N),F=e(p(P),2);C(F),a(()=>w(F,()=>l.email,e=>l.email=e)),te(F,e=>ge?.(e));var be=e(F,2);k(be,{key:`email`,get errors(){return x}}),E(P);var I=e(P,2),L=p(I),R=e(p(L),2);C(R);var xe=e(R,2);k(xe,{key:`firstname`,get errors(){return x}}),E(L);var Se=e(L,2),z=e(p(Se),2);C(z);var Ce=e(z,2);k(Ce,{key:`lastname`,get errors(){return x}}),E(Se),E(I);var B=e(I,2),V=p(B),H=e(p(V),2),U=p(H);U.value=U.__value=`en`;var we=e(U);we.value=we.__value=`fr`,E(H);var Te=e(H,2);k(Te,{key:`lang`,get errors(){return x}}),E(V);var Ee=e(V,2),De=e(p(Ee),2);le(De,{get items(){return n(g)},valueMode:`id`,get value(){return l.timezone},seLine truncated
|
||||
@@ -1 +0,0 @@
|
||||
import{B as e,E as t,G as n,J as r,M as i,N as a,O as o,at as s,c,d as l,dt as u,et as d,it as f,j as p,q as m,u as h}from"../chunks/39JK5HWF.js";import{t as g}from"../chunks/D3up1JC1.js";import"../chunks/xihTtKlq.js";import"../chunks/B5isPFOt.js";import{t as _}from"../chunks/BRXBpvt8.js";import{n as v,t as y}from"../chunks/DRdGzrsF.js";import{t as b}from"../chunks/D3MDK1zR.js";import{t as x}from"../chunks/DNQ6C3zq.js";var S=o(`<div class="h-full w-full flex items-center public"><div class="m-auto w-96 max-w-full p-4"><img class="h-10 mb-4" alt="logo"/> <h1>Inscription</h1> <form><label for="email" class="block my-1 flex-1">Email</label> <input id="email" type="text" class="textinput"/> <!> <label for="firstname" class="block my-1 flex-1">Prénom</label> <input id="firstname" type="text" class="textinput"/> <!> <label for="lastname" class="block my-1 flex-1">Nom</label> <input id="lastname" type="text" class="textinput"/> <!> <label for="password" class="block my-1 flex-1">Mot de passe</label> <input id="password" type="password" class="textinput"/> <!> <label for="verifpassword" class="block my-1 flex-1">Vérification du mot de passe</label> <input id="verifpassword" type="password" class="textinput"/> <!> <button class="bt my-2 w-full ">Enregistrer</button> <!></form></div></div>`);function C(o,p){s(p,!0);let C=new v,w=r({email:``,firstname:``,lastname:``,password:``,verifPassword:``}),T=e=>{if(e.preventDefault(),C.clear(),b(w.email)||C.set(`email`,`Adresse email invalide !`),w.firstname||C.set(`firstname`,`Le prénom est obligatoire !`),w.lastname||C.set(`lastname`,`Le nom est obligatoire !`),w.password!==w.verifPassword&&C.set(`verifPassword`,`Les mots de pass ne correspondent pas.`),C.size==0){let e={method:`password`,email:w.email,firstname:w.firstname,lastname:w.lastname,password:w.password};x.register(e).then(()=>{g(`/`)}).catch(e=>{C.set(`submit`,`Erreur: `+e)})}};var E=S(),D=n(E),O=n(D),k=m(O,4),A=m(n(k),2);h(A),d(A,!0);var j=m(A,2);y(j,{key:`email`,get errors(){return C}});var M=m(j,4);h(M);var N=m(M,2);y(N,{key:`firstname`,get errors(){return C}});var P=m(N,4);h(P);var F=m(P,2);y(F,{key:`lastname`,get errors(){return C}});var I=m(F,4);h(I);var L=m(I,2);y(L,{key:`password`,get errors(){return C}});var R=m(L,4);h(R);var z=m(R,2);y(z,{key:`verifPassword`,get errors(){return C}});var B=m(z,4);y(B,{key:`submit`,get errors(){return C}}),u(k),u(D),u(E),e(()=>l(O,`src`,_)),a(`submit`,k,T),i(`change`,k,()=>{C.clear()}),c(A,()=>w.email,e=>w.email=e),c(M,()=>w.firstname,e=>w.firstname=e),c(P,()=>w.lastname,e=>w.lastname=e),c(I,()=>w.password,e=>w.password=e),c(R,()=>w.verifPassword,e=>w.verifPassword=e),t(o,E),f()}p([`change`]);export{C as component};
|
||||
@@ -0,0 +1 @@
|
||||
import{$ as e,A as t,B as n,E as r,I as i,K as a,L as o,N as s,Q as c,R as l,Z as u,_t as d,dt as f,et as p,ft as m,it as h,j as g,k as _,m as v,nt as y,ot as b,p as x,r as S,u as C,vt as w}from"../chunks/DGrvnoUE.js";import{t as T}from"../chunks/BZuNOv8h.js";import"../chunks/xihTtKlq.js";import"../chunks/CY9OlB4q.js";import{i as E,n as D}from"../chunks/DtfiK5fE.js";import{t as O}from"../chunks/BRXBpvt8.js";import{t as k}from"../chunks/D3MDK1zR.js";import{t as A}from"../chunks/BedKI9iq.js";var j=s(`<form><div class="flex"><label for="email" class="block mb-2 flex-1">Email</label> <a href="/recover-password" class="text-xs ">Mot de passe oublié ?</a></div> <div class="flex flex-row"><input type="text" class="textinput mr-1 flex-1"/> <button class="bt small" title="page suivante"><i class="icon-right-big"></i></button></div></form> <div class="my-4 text-xs"><a href="/register">Pas encore de compte ? Incrivez-vous.</a></div>`,1),M=s(`<form><div class="flex"><label for="email" class="block mb-2 flex-1">Mot de passe</label></div> <div class="flex flex-row"><input type="password" class="textinput mr-1 flex-1"/> <button class="bt small">Entrer</button></div> <a href="/login" class="text-xs">← Retour</a></form>`),N=s(`<button class="bt p-5 text-center"> </button>`),P=s(`<hr class="my-4"/> <div class="grid grid-cols-[repeat(auto-fit,minmax(150px,1fr))] gap-4"></div>`,1),F=s(`<div class="h-full w-full flex items-center public"><div class="m-auto w-96 max-w-full p-4"><img class="h-10 mb-4" alt="logo"/> <h1>Identifiez-vous ...</h1> <!> <!></div></div>`);function I(i,s){m(s,!0);let I=p({email:``,password:``,validemail:!1}),L=h(p([]));S(()=>{A.oidcProviders().then(e=>{y(L,e??[],!0)}).catch(()=>{y(L,[],!0)})});let R=e=>{window.location.href=A.oidcLoginUrl(e)},z=()=>{I.validemail=k(I.email)},B=e=>{e.preventDefault(),A.login(I).then(e=>{D.user=e.user,E(e.accessToken),T(`/`)}).catch(e=>{console.log(e)})};var V=F(),H=u(V),U=u(H),W=e(U,4),G=t=>{var n=j(),r=c(n),i=e(u(r),2),a=u(i);x(a),b(a,!0),d(2),w(i),w(r),d(2),l(`submit`,r,z),C(a,()=>I.email,e=>I.email=e),g(t,n)},K=t=>{var n=M(),r=e(u(n),2),i=u(r);x(i),b(i,!0),d(2),w(r);var a=e(r,2);w(n),l(`submit`,n,B),C(i,()=>I.password,e=>I.password=e),o(`click`,a,()=>{I.validemail=!1}),g(t,n)};_(W,e=>{I.validemail?e(K,-1):e(G)});var q=e(W,2),J=i=>{var s=P(),l=e(c(s),2);r(l,21,()=>n(L),e=>e.id,(e,r)=>{var i=N(),s=u(i,!0);w(i),a(()=>{v(i,`title`,`Connexion avec ${n(r).label??``}`),t(s,n(r).label)}),o(`click`,i,()=>R(n(r).id)),g(e,i)}),w(l),g(i,s)};_(q,e=>{n(L).length>0&&e(J)}),w(H),w(V),a(()=>v(U,`src`,O)),g(i,V),f()}i([`click`]);export{I as component};
|
||||
@@ -1 +0,0 @@
|
||||
import{$ as e,B as t,E as n,G as r,J as i,K as a,M as o,N as s,O as c,P as l,T as u,V as d,W as f,a as p,at as m,b as h,d as g,dt as _,ft as v,g as y,h as b,it as x,j as S,q as C,r as w,w as T,x as E}from"../chunks/39JK5HWF.js";import{t as D}from"../chunks/D3up1JC1.js";import"../chunks/xihTtKlq.js";import{n as O,t as k}from"../chunks/DTCALkcP.js";import{t as A}from"../chunks/D1d5-1oo.js";import"../chunks/B5isPFOt.js";import{n as j,r as M}from"../chunks/BqkZ0BZ5.js";import{t as N}from"../chunks/BEgKbVuy.js";import{t as P}from"../chunks/D8dI9nx6.js";import{t as F}from"../chunks/BRXBpvt8.js";var I=v({load:()=>L}),L=async()=>{let e=await N.me();return P.spaces=e.teams,P.current||(P.current=P.spaces[0].id),{spaceid:P.current,me:e}},R=i({sidebarDesktopOpen:!0,sidebarMobileOpen:!1,menuContentVisible:!0}),z=c(`<span class="ml-4"> </span>`),B=c(`<div class="my-2 block"><a class="p-2 w-full text-left menuitem block"><i></i> <!></a></div>`);function V(e,i){m(i,!0);let a=p(i,`label`,3,``),o=p(i,`icon`,3,``),s=p(i,`link`,3,``);var c=B(),l=r(c),d=r(l),f=C(d,2),h=e=>{var i=z(),o=r(i,!0);_(i),t(()=>u(o,a())),n(e,i)};T(f,e=>{R.menuContentVisible&&e(h)}),_(l),_(c),t(()=>{g(l,`href`,s()),b(d,1,y([o()]))}),n(e,c),x()}var H=c(`<div class="flex-1 p-3 font openonly"><img class="h-5" alt="logo"/></div>`),U=c(`<option> </option>`),W=c(`<div class="m-2"><div class="formelement"><select class="textinput"></select></div></div>`),G=c(`<div class="flex flex-row gap-2 p-2 "><div class="py-1 flex-1 flex flex-row text-xs"><div class="w-6 h-6 rounded-full bg-cyan-600 text-white flex items-center justify-center font-medium">FM</div> <div class="flex-1 ml-2 inline-flex items-center">Fabien Masson</div></div> <a href="/profile" class="bt small inline-flex items-center justify-center" title="Edition profil"><i class="icon-edit"></i></a> <button class="bt small" title="Déconnecter"><i class="icon-logout"></i></button></div>`),K=c(`<div class="flex flex-row italic mb-2"><!> <button id="hamburgerDesktop" title="Open menu"><i class="icon-menu pointer block p-2"></i></button> <button id="hamburgerMobile" title="Open menu"><i class="icon-menu pointer block p-2"></i></button></div> <!> <div id="menu" class="flex-1 overflow-y-scroll"><!> <!> <!> <!> <!></div> <div id="profile" class="w-full items-center border-t border-stone-200 dark:border-stone-800"><!></div>`,1);function q(i,s){m(s,!0);let c=()=>{M()};var d=K(),f=a(d),p=r(f),h=e=>{var i=H(),a=r(i);_(i),t(()=>g(a,`src`,F)),n(e,i)};T(p,e=>{R.menuContentVisible&&e(h)});var v=C(p,2),y=C(v,2);_(f);var b=C(f,2),S=e=>{var i=W(),a=r(i),o=r(a);E(o,21,()=>s.spaces,e=>e.id,(e,i)=>{var a=U(),o=r(a,!0);_(a);var s={};t(()=>{u(o,l(i).label),s!==(s=l(i).id)&&(a.value=(a.__value=l(i).id)??``)}),n(e,a)}),_(o),_(a),_(i),n(e,i)};T(b,e=>{R.menuContentVisible&&e(S)});var w=C(b,2),D=r(w);{let t=e(()=>`/${s.spaceid}`);V(D,{label:`Nouveau chat`,icon:`icon-plus`,get link(){return l(t)}})}var O=C(D,2);{let t=e(()=>`/${s.spaceid}/history`);V(O,{label:`Conversations`,icon:`icon-chat`,get link(){return l(t)}})}var k=C(O,2);{let t=e(()=>`/${s.spaceid}/tasks`);V(k,{label:`Planification`,icon:`icon-tasks`,get link(){return l(t)}})}var A=C(k,2);{let t=e(()=>`/${s.spaceid}/kb`);V(A,{label:`Connaissances`,icon:`icon-graduation-cap`,get link(){return l(t)}})}var j=C(A,2);{let t=e(()=>`/${s.spaceid}/settings`);V(j,{label:`Réglages`,icon:`icon-cog`,get link(){return l(t)}})}_(w);var N=C(w,2),P=r(N),I=e=>{var t=G(),i=C(r(t),4);_(t),o(`click`,i,c),n(e,t)},L=e=>{V(e,{label:`User`,icon:`icon-user`,link:`/profile`})};T(P,e=>{R.menuContentVisible?e(I):e(L,-1)}),_(N),o(`click`,v,function(...e){s.handleMenuDesktop?.apply(this,e)}),o(`click`,y,function(...e){s.handleMenuMobile?.apply(this,e)}),n(i,d),x()}S([`click`]);var J=c(`<span><a title="retour"><i class="icon-left-big"></i></a></span>`),Y=c(`<div id="outer"><div id="sidebar" class="flex flex-col z-10"><!></div> <div id="main"><div class="flex flex-col h-screen "><div class="px-2"><div class="header container"><h1 class="flex"><span class="flex-1"> </span> <!></h1></div></div> <div class="overflow-scroll flex-1 p-2"><!></div></div></div></div>`);function X(e,i){m(i,!0);let a=()=>{R.sidebarDesktopOpen=!R.sidebarDesktopOpen,R.sidebarDesktopOpen?setTimeout(()=>{R.menuContentVisible=R.sidebarDesktopOpen},200):R.menuContentVisible=R.sidebarDesktopOpen},o=()=>{R.sidebarMobileOpen=!R.sidebarMobileOpen,R.sidebarMobileOpen?setTimeout(()=>{R.menuContentVisible=R.sidebarMobileOpen},200):R.menuContentVisible=R.sidebarMobileOpen},c=()=>{R.menuContentVisible=window.innerWidth<640?R.sidebarMobileOpen:R.sidebarDesktopOpen};w(async()=>{c(),i.data.me?(j.user=i.data.me.user,O(j.user.theme),k(),j.status=`authenticated`):j.status=`unauthenticated`}),d(()=>{j.status==`unauthenticated`&&D(`/login`)});var l=Y();s(`resize`,f,c);var p=r(l);q(r(p),{get spaces(){return P.spaces},get spaceid(){return i.data.spaceid},handleMenuDesktop:a,handleMenuMobile:o}),_(p);var v=C(p,2),S=r(v),E=r(S),M=r(E),N=r(M),F=r(N),I=r(F,Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
import{$ as e,A as t,B as n,E as r,I as i,K as a,L as o,N as s,Q as c,R as l,T as u,X as d,Z as f,a as p,at as m,bt as h,dt as g,et as _,ft as v,j as y,k as b,m as x,q as S,r as C,v as w,vt as T,y as E}from"../chunks/DGrvnoUE.js";import{t as D}from"../chunks/BZuNOv8h.js";import"../chunks/xihTtKlq.js";import{n as O,t as k}from"../chunks/DTCALkcP.js";import{t as A}from"../chunks/ffLBjIeN.js";import"../chunks/CY9OlB4q.js";import{n as j,r as M}from"../chunks/DtfiK5fE.js";import{t as N}from"../chunks/zsRwiIdn.js";import{t as P}from"../chunks/Cyrg28w2.js";import{t as F}from"../chunks/BRXBpvt8.js";var I=h({load:()=>L}),L=async()=>{let e=await N.me();return P.spaces=e.teams,P.current||(P.current=P.spaces[0].id),{spaceid:P.current,me:e}},R=_({sidebarDesktopOpen:!0,sidebarMobileOpen:!1,menuContentVisible:!0}),z=s(`<span class="ml-4"> </span>`),B=s(`<div class="my-2 block"><a class="p-2 w-full text-left menuitem block"><i></i> <!></a></div>`);function V(n,r){v(r,!0);let i=p(r,`label`,3,``),s=p(r,`icon`,3,``),c=p(r,`link`,3,``),l=e=>{e.preventDefault(),R.sidebarMobileOpen&&(R.sidebarMobileOpen=!1,R.menuContentVisible=!1),D(c())};var u=B(),d=f(u),m=f(d),h=e(m,2),_=e=>{var n=z(),r=f(n,!0);T(n),a(()=>t(r,i())),y(e,n)};b(h,e=>{R.menuContentVisible&&e(_)}),T(d),T(u),a(()=>{x(d,`href`,c()),w(m,1,E([s()]))}),o(`click`,d,l),y(n,u),g()}i([`click`]);var H=s(`<div class="flex-1 p-3 font openonly"><img class="h-5" alt="logo"/></div>`),U=s(`<option> </option>`),W=s(`<div class="m-2"><div class="formelement"><select class="textinput"></select></div></div>`),G=s(`<div class="flex flex-row gap-2 p-2 "><div class="py-1 flex-1 flex flex-row text-xs"><div class="w-6 h-6 rounded-full bg-cyan-600 text-white flex items-center justify-center font-medium">FM</div> <div class="flex-1 ml-2 inline-flex items-center">Fabien Masson</div></div> <a href="/profile" class="bt small inline-flex items-center justify-center" title="Edition profil"><i class="icon-edit"></i></a> <button class="bt small" title="Déconnecter"><i class="icon-logout"></i></button></div>`),K=s(`<div class="flex flex-row italic mb-2"><!> <button id="hamburgerDesktop" title="Open menu"><i class="icon-menu pointer block p-2"></i></button> <button id="hamburgerMobile" title="Open menu"><i class="icon-menu pointer block p-2"></i></button></div> <!> <div id="menu" class="flex-1 overflow-y-scroll"><!> <!> <!> <!> <!></div> <div id="profile" class="w-full items-center border-t border-stone-200 dark:border-stone-800"><!></div>`,1);function q(i,s){v(s,!0);let l=()=>{M()};var u=K(),d=c(u),p=f(d),h=e=>{var t=H(),n=f(t);T(t),a(()=>x(n,`src`,F)),y(e,t)};b(p,e=>{R.menuContentVisible&&e(h)});var _=e(p,2),S=e(_,2);T(d);var C=e(d,2),w=e=>{var i=W(),o=f(i),c=f(o);r(c,21,()=>s.spaces,e=>e.id,(e,r)=>{var i=U(),o=f(i,!0);T(i);var s={};a(()=>{t(o,n(r).label),s!==(s=n(r).id)&&(i.value=(i.__value=n(r).id)??``)}),y(e,i)}),T(c),T(o),T(i),y(e,i)};b(C,e=>{R.menuContentVisible&&e(w)});var E=e(C,2),D=f(E);{let e=m(()=>`/${s.spaceid}`);V(D,{label:`Nouveau chat`,icon:`icon-plus`,get link(){return n(e)}})}var O=e(D,2);{let e=m(()=>`/${s.spaceid}/history`);V(O,{label:`Conversations`,icon:`icon-chat`,get link(){return n(e)}})}var k=e(O,2);{let e=m(()=>`/${s.spaceid}/tasks`);V(k,{label:`Planification`,icon:`icon-tasks`,get link(){return n(e)}})}var A=e(k,2);{let e=m(()=>`/${s.spaceid}/kb`);V(A,{label:`Connaissances`,icon:`icon-graduation-cap`,get link(){return n(e)}})}var j=e(A,2);{let e=m(()=>`/${s.spaceid}/settings`);V(j,{label:`Réglages`,icon:`icon-cog`,get link(){return n(e)}})}T(E);var N=e(E,2),P=f(N),I=t=>{var n=G(),r=e(f(n),4);T(n),o(`click`,r,l),y(t,n)},L=e=>{V(e,{label:`User`,icon:`icon-user`,link:`/profile`})};b(P,e=>{R.menuContentVisible?e(I):e(L,-1)}),T(N),o(`click`,_,function(...e){s.handleMenuDesktop?.apply(this,e)}),o(`click`,S,function(...e){s.handleMenuMobile?.apply(this,e)}),y(i,u),g()}i([`click`]);var J=s(`<span><a title="retour"><i class="icon-left-big"></i></a></span>`),Y=s(`<div id="outer"><div id="sidebar" class="flex flex-col z-10"><!></div> <div id="main"><div class="flex flex-col h-screen "><div class="px-2"><div class="header container"><h1 class="flex mx-2"><span class="flex-1"> </span> <!></h1></div></div> <div class="overflow-scroll flex-1 p-2"><!></div></div></div></div>`);function X(n,r){v(r,!0);let i=()=>{R.sidebarDesktopOpen=!R.sidebarDesktopOpen,R.sidebarDesktopOpen?setTimeout(()=>{R.menuContentVisible=R.sidebarDesktopOpen},200):R.menuContentVisible=R.sidebarDesktopOpen},o=()=>{R.sidebarMobileOpen=!R.sidebarMobileOpen,R.sidebarMobileOpen?setTimeout(()=>{R.menuContentVisible=R.sidebarMobileOpen},200):R.menuContentVisible=R.sidebarMobileOpen},s=()=>{R.menuContentVisible=window.innerWidth<640?R.sidebarMobileOpen:R.sidebarDesktopOpen};C(async()=>{s(),r.data.me?(j.user=r.data.me.user,O(j.user.theme),k(),j.status=`authenticated`):j.status=`unauthenticated`}),S(()=>{j.status==`unauthenticated`&&D(`/login`)});var c=Y();l(`resize`,d,s);var p=f(c);q(f(p),{get spaces(){return P.spaces},geLine truncated
|
||||
Loaded 100 of 160 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user