On se propose de réaliser un très simple "moteur de recherche", capable de découvrir du contenu en ligne, et garder une trace du contenu qu'il a trouvé et de permettre de recherches.
## Architecture du projet
Le projet sera composé de trois commandes indépendantes :
### index
En gros une base de données qui contient :
* les sites à visiter (ou déjà visités)
* les fichiers trouvés (et la référence au site concerné)
### crawler
* un "agent" qui consulte l'index
* iel prend un site non visité (s'il y en a) et le visite
* iel prend le site le plus ancien visité (si tous ont déja été visités) et le visite
* il parcourt les liens
* il stocke un File dans l'index pour chaque lien trouvé
### search
* Iel propose une API web
* Iel recherche dans l'index
* Iel répond en JSON au client
## Types de données
### Site
* id: int64
* hostip: string
* domain: string
* lastseen : datetime ou *nil*
### File
* id: int64
* name: string
* url: string
* site_id: int64
* lastseen : datetime ou *nil*
### Database
* dirs: []Dir
* files: []File
## API (pour le search)
### GET /sites
#### Requête
```
{
"query": "...",
"filter: "..."
}
```
| Parametre | Description | Champ obligatoire |
| --- | --- | --- |
| query | La typologie de recherche à faire: soit **by-domain**, soit **by-date** (recherche du plus vieux), soit **by-visit** (recherche ) | Obligatoire |
| filter | Le domaine à chercher (dans le cas d'une `query=by-domain`) | Optionnel |
#### Réponse
```
{
"status": "...",
"sites": [
{
"id": "...",
"hostip": "...",
"domain": "...",
"lastseen"
},
...
]
}
```
| Parametre | Description | Champ obligatoire |
| --- | --- | --- |
| status | Le type de résultat. "ok" si tout se passe bien, "nok" s'il y a une erreur | Obligatoire |
| sites | Liste des sites existants sur le serveur | Obligatoire |
### POST /sites
#### Requête
```
{
"id": ... ,
"hostip": ... ,
"domain": ...
}
```
#### Réponse
```
{
"status": "...",
"site": { ... }
}
```
| Parametre | Description | Champ obligatoire |
| --- | --- | --- |
| status | Le type de résultat. "ok" si tout se passe bien, "nok" s'il y a une erreur | Obligatoire |
| site | Les informations collectées dans la requete | Obligatoire |
### GET /files
#### Requête
```
{
"query": "...",
"filter: "..."
}
```
#### Réponse
```
{
"status": "...",
"query": "..."
"filter": "..."
"files": [
{
id: ...
name: "..."
url: "..."
site_id: "..."
lastseen : ...
},
{ ... },
...
]
}
```
| Parametre | Description | Champ obligatoire |
| --- | --- | --- |
| status | Le type de résultat. "ok" si tout se passe bien, "nok" s'il y a une erreur | Obligatoire |
| query | Le type de recherche effectuée (la même que la requete) | Obligatoire |
| filter | Le filtre utilisé (le même que la requete) | Obligatoire |
| site | Les informations collectées dans la requete | Obligatoire |
#### Exemple
Requête:
Réponse
{
"id": 10423,
"name": "Avatar.2009.EXTENDED.1080p.6CH.mkv"
url: "https://dl3.3rver.org/cdn2/06/film/2009/avatar/Avatar.2009.EXTENDED.1080p.6CH.mkv"
"site_id": 121,
lastseen : 1234214235
### Pour tester l'API
```
curl -X GET http://localhost:8080/sites \
-H 'Content-Type: application/json' \
-d "{ ... }"
```
```
curl -X POST http://localhost:8080/sites \
-H 'Content-Type: application/json' \
-d "{ ... }"
```
```
curl -X GET http://localhost:8080/files \
-H 'Content-Type: application/json' \
-d "{ ... }"
```
## Etapes du TP
### 1. Organiser le projet (2 points)
Créer l'arborescence normale pour un projet go :
```
.
|- cmd
| |- ...
| `- ...
|- pkg
| |- ...
| `- ...
|- internal
| |- ...
| `- ...
`- test
|- ...
`- ...
```
Préparer les 3 commandes : **crawler**, **index**, **search**
Pour chacune vous créerez un dossier du même nom (ex: `cmd/crawler/`) et un fichier `main.go` minimal.
Initialisez votre projet avec
```
go mod init example.com/username/project
```
Et
```
go mod tidy
```
### 2. Ecrire le code des clients et serveurs (2 points)
On veut s'assurer que les différents éléments communiquent bien entre eux, selon les roles suivants:
* Index est un serveur qui accepte les connexions en TCP.
* Crawler et Search sont des clients en TCP qui doivent se connecter à Index.
* Search est également un serveur HTTP.
Ecrire le code des clients et serveurs TCP/IP et échangez des messages textes dans un premier temps. Voir la documentation de `Read()` et de `Write()` dans le module `net` de go.
Vous allez vérifier que:
* crawler et search sont capables de se connecter sur le index,
* index reçoit les messages des clients;
* crawler et search recoivent les réponses de index.
:blue_book: https://pkg.go.dev/net
:blue_book: https://www.golinuxcloud.com/golang-tcp-server-client/
### 3. Définir des données (2 points)
Définir les structures de données internes pour la gestion du crawler, de search et de index.
Définir aussi des structures de données pour les différents messages que les composants peuvent s'envoyer les uns aux autres.
#### Search <=> Index
* CreateSiteRequest / CreateSiteReponse
* crée un site à visiter dans l'index
* GetSiteRequest + params / GetSiteResponse
* obtenir une url
* selon le parametre , choisit le plus vieux, ou le non visité, ou l'url deja existante
* GetFileRequest / GetFileResponse
* obtenir des fichiers
* selon le parametre, filtrer la liste (ex: seulement ceux qui contiennent "1080p")
#### Crawler <=> Index
* GetSiteRequest + params / GetSiteResponse
* obtenir une url
* selon le parametre , choisit le plus vieux, ou le non visité, ou l'url deja existante
* UpdateSiteRequest / UpdateSiteReponse
* mettre à jour une url (dire que c'est visité + date)
* CreateFileRequest / CreateFileResponse
* met à jour une url d'apres les infos
* GetFileRequest / GetFileResponse
* obtenir des fichiers
* selon le parametre, filtrer la liste (ex: seulement ceux qui contiennent "1080p")
* UpdateFileRequest / UpdateFileResponse
* met à jour une url d'apres les infos
**Définir pour chaque message un type de données.**
### 4. Préparer les données pour le réseau (2 points)
Préparer les conversions JSON des différents types.
1. Ajoutez les annotations JSON sur les différents types définis à l'étape précédente.
2. Pour chaque type de données, vérifiez que vous arrivez bien à créer le json correct (tel que défini dans le protocole) avec `json.Marshal(...)` et `fmt.Printf(...)`
:blue_book: [Golang Cafe: Json Marshal Example](https://golang.cafe/blog/golang-json-marshal-example.html)
### 5. Envoyer/Recevoir des messages et implémenter le protocole
Utiliser les types précédents pour envoyer des messages entre les différents composants.
Vérifiez que vous arrivez à transmettre ces différentes infos entre clients / serveur (encodés en json).
#### Dans Index
* Faire une boucle de réception des messages coté serveur.
* Traiter chacun des messages reçus
* Retourner un message réponse à l'émetteur
#### Dans Crawler
* Envoyer à l'index un site à visiter (GetUrlRequest)
* Recevoir une réponse de l'index
* Visiter le site (sera fait plus tard)
* Envoyer à l'index plusieurs GetFileRequest / CreateFileRequest ou UpdateFileRequest
* Recevoir une réponse de l'index
* Envoyer à l'index UpdateSiteRequest
* Recevoir une réponse de l'index
Note: s'il n'y a pas de site à visiter, le Crawler attend 5 sec et recommence.
#### Dans Search
* Envoyer à l'index une demande de listing de site (GetSiteRequest) avec différents type de listing
* Recevoir une réponse de l'index
* Envoyer à l'index une demande de listing de fichiers (GetFileRequest)
* Recevoir une réponse de l'index
### 6. Logique métier de Index
Implémenter la logique métier de Index, c'est à dire implémenter les différentes fonctions nécessaires pour retourner des réponses valides à Crawler et Search
:blue_book: Voir https://pkg.go.dev/time pour la gestion du temps.
:blue_book: Voir https://pkg.go.dev/time#Time.Before pour comparer des dates.
### 7. Crawler le contenu
Utiliser net/http et la bilbioteque https://github.com/PuerkitoBio/goquery pour vous connecter et analyser le contenu.
Pour chaque lien trouvé, créer un objet File et l'envoyer à l'index avec soit createFileRequest, soit updatefileRequest.
:blue_book: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
### 7. Implémenter l'API
Vous pouvez utiliser net/http ou gorilla/mux pour la gestion de la partie http.