Stocker ses commentaires de blog dans DynamoDB avec API Gateway et AWS Lambda

Menu commentaire dans un CSM

Quand j'ai créé ce blog, j'ai cherché à mettre en place une solution simple pour les commentaires et j'ai tout simplement utilisé Disqus que je connaissais.

A l'usage, c'est très simple à installer mais ca demande quand même aux utilisateurs (et à moi-même) d'être authentifié, et donc tracké..

Puis, en lisant l'article de TFerdinant.net , je me suis mis moi aussi en quête d'une alternative mais Serverless :)

Voici les caractéristiques de mon blog:

  • J'utilise GatsbyJS, un moteur de blog statique
  • Les pages sont construites en React
  • GatsbyJS compile mes articles en page statiques et je dépose le tout sur S3
  • AWS CloudFront fait ma terminaison HTTPS et le cache

Mon besoin:

  • Que les utilisateurs puissent poster des commentaires sur des articles de manière anonyme s'ils le souhaitent, sans authentification et sans tracking par cookie
  • Simplement avec un composant React
  • Avec une protection anti-spam (auto-modération)

Comment j'y réponds

Pour le stockage et la récupération des commentaires

Je vais partir sur un stockage dans une table DynamoDB avec une structure tout simple.

| Primary Key: article-slug | Sort Key: timestamp | [Commentaire, Author, Email]

Pour récupérer les commentaires, je pourrais requêter tout simplement la clé primaire

Pour l'API

Je met en place une API Gateway en mode {proxy+} vers une Lambda en Python. Sur l'API Gateway, je vais activer une Api Key afin de pouvoir gérer des quotas.

Pour la modération des commentaires

J'ai lu cet été le livre "En route vers Symfony 5" et dans l'un des chapitres, l'enjeu était d'empêcher le spam en utilisant un service tiers nommé Akismet.

N'ayant pas un trafic énorme et ne faisant aucune utilisation commercial de ce blog, je peux rester sur plan gratuit. (Plans). Il suffit de s'inscrire pour recevoir une API Key.

Comment ca fonctionne ?

J'ai défini dans API Gateway une ressource de type {proxy+}, ce qui signifie que toutes les routes (les urls) seront capturés et transmise à la lamba.

Resource {proxy+} ]

L'éxecution de la ressource est configurée pour fonctionner en mode: LAMBDA_PROXY, l'avantage est que API Gateway rajoutera du contexte dans l'event transmis à la Lambda.

Resource Method

La requête transite par API Gateway est transmise à la fonction Lambda.

Voici le code ma fonction Python: lambda_function.py

La fonction observe le path (ici /comments ) en fonction de la méthode GET OU POST sait si elle doit procéder à la récupèration des commentaires (GET) ou le faire vérifier (POST).

Si elle doit récupérer les commentaires, une requête vers DynamoDB est réalisée:

response = table.query(
        ProjectionExpression="comment_timestamp, author, email, content, article_slug",
        ScanIndexForward=True,
        KeyConditionExpression=Key('article_slug').eq(article_slug)
    )

Si elle doit faire vérifier le commentaire par Akismet, elle lui soumet:

akismet_config = get_askimet_config()
    akismet = Akismet(
        akismet_config.key,
        akismet_config.site_url,
        "Devops.in.net Lambda Moderator")

    response = akismet.check(
        comment.ip, comment.user_agent,
        comment_author=comment.author_name,
        comment_content=comment.content,
        comment_author_email=comment.author_email
    )

En fonction du résultat (SPAM ou non), on peux le stocker dans DynamoDB:

dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table(environ.get('DYNAMODB_TABLE'))
    table.put_item(
        Item={
            'article_slug': comment_to_write.article_slug,
            'comment_timestamp': comment_to_write.timestamp,
            'author': comment_to_write.author_name,
            'email': comment_to_write.author_email,
            'content': comment_to_write.content
        }
    )

Vue X-Ray des commentaires sur ce blog Vue CloudWatch Service Map

Et maintenant ?

Il y a plusieurs points qui pourraient être améliorés:

  • Eviter de requêter DynamoDB à chaque consultation d'un article

    En effet, nous pourrions générer un JSON (ou autre chose) après chaque écriture dans DynamoDB et le stocker les commentaires de manière statique sur le bucket S3 du site.

  • Pouvoir répondre à un autre commentaire

  • Etre notifié lorsqu'il y a nouveau commentaire

    Une notification sur Slack serait très sympa

  • Rendre ré-utilisable facilement ce qui a été fait

    Tranformer la configuration de la Lambda, de API Gateway, de la table DynamoDB en template CloudFormation ou Construct CDK

  • Ajouter le nombre de commentaire sur l'article sur les autres pages

    EDIT: C'est maintenant fait, j'ai juste ajouté une requête de type COUNT sur ma table DynamoDB.

    (J'ai basculé en query plutôt qu'en ressource boto3 car je n'ai pas trouvé la bonne syntaxe..)

    def fetch_comments_count(article_slug: str):
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table(environ.get('DYNAMODB_TABLE'))
    response = table.query(
        TableName=environ.get('DYNAMODB_TABLE'),
        Select='COUNT',
        KeyConditionExpression=Key('article_slug').eq(article_slug)
    )
    return response['Count']
    
  • Améliorer le design et le code JS

    Bon là, il va falloir être patient ^^

  • Implémenter un analyseur de commentaire comme AWS Comprehend

    Test Amazon Comprehend

Allez vous pouvez-tester, c'est juste en dessous :)


Chargement en cours

Ajouter un commentaire

Photo Thibault Cordier

Thibault Cordier

Freelance DevOps & Cloud