Rodney Lab narcissus Github banner

Rodney Lab logo

Narcissus — Backendless Blog API as a Service

Narcissus is a proof of concept backend as a service app which lets you create a blog site quicker by managing important blog features like comment and message forms as well as post views and likes. You create the styling and implementation your way on your preferred framework and just use the API to connect the data your site needs. The API runs on Rust Cloudflare Workers interfacing with a Supabase PostgreSQL database and packs features like bot and spam detection. We also use bots for good — a Telegram bot sends you contact form messages! There is a demo frontend built in SvelteKit with the demo site running on Cloudflare Pages at narcissus-blog.rodneylab.com.

Features

These features are provided by REST endpoints which the Rust Cloudflare Worker listens on:

  • Message form — lets website visitors send a message to admins. To avoid abuse by bots hCaptcha runs a challenge in browser. On the Cloudflare worker side, there is also a check with the Akismet spam detection service. The worker sends you or admins the details of the message using a Telegram bot.

  • View count — pages views are counted automatically and displayed, letting visitors know what the most popular content is.

  • Likes — the Cloudflare worker records new blog post likes to a Supabase database.

  • Comments — comments left by users on blog posts, like contact form messages are checked for spam and bots.

  • Developer friendly — you style the pages and implement any or all of the features the way you want. Just fetch data from the API using REST calls. Your site becomes backendless and you save on having to configure and connect multiple services.

Possible Future Features

Trying it Out

You will need a Cloudflare account as well as an Akismet, hCaptcha and Telegram Bot API key to use all features. These services all have a free tier and there are details below on how you can set them up.

  1. Start by cloning this repo:

    git clone https://github.com/rodneylab/narcissus
    cd narcissus
    
  2. Continue by setting up a Cloudflare account if you do not yet have one.

  3. Now get you can get API keys for Akismet, hCaptcha and Telegram. Follow these links for instructions:

  4. If you do not yet have a Rust development environment set up on your machine, head over to the official Rust site for the recommended one-line terminal command to get that up and running.

  5. Install the wrangler tool on your machine:

    cargo install wrangler
    
  6. Next link your Cloudflare account to your local environment:

    wrangler login
    
  7. Now we will define some variables. Start with your Akismet API key:

    wrangler secret put AKISMET_API_KEY
    

    paste in your API key when prompted.

  8. Repeat with the following keys:

    wrangler secret put HCAPTCHA_SECRETKEY
    wrangler secret put HCAPTCHA_SITEKEY
    wrangler secret put SUPABASE_URL
    wrangler secret put SUPABASE_SERVICE_API_KEY
    wrangler secret put TELEGRAM_BOT_API_TOKEN
    wrangler secret put TELEGRAM_BOT_CHAT_ID
    

    we also need the url of your user site (e.g. "http://example.com") for Akismet spam detection:

     wrangler secret put SITE_URL
    
  9. Finally we will define the CORS origins. This is a comma separated list of valid domains you want to be able to send requests from (typically your live client site and a local development site). If you have the the following domains:

    Enter the secret as https://www.example.com,http://127.0.0.1:3000 when prompted.

    Let's define this now then:

    wrangler secret put CORS_ORIGIN
    
  10. Be sure to add appropriate disclaimers and information on privacy policies and terms of use on any sites you link up.

That should be everything set up.

Testing

To test you can build the SvelteKit demo site, create you own new site in your framework of preference or update an existing site.

As an example, using fetch in JavaScript, you can query the data endpoint like so

const response = await fetch(`${process.env["VITE_WORKER_URL"]}/post/data`, {
  method: "POST",
  credentials: "omit",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    slug,
  }),
});
const data = await response.json();
const { comments, likes, views } = data;

Endpoints:

  • post/data get the likes, views and comments for a particular blog post:

    method
    POST
    input
    JSON object: { "slug": "example-post-slug-to-identify-post" }
    response
    JSON object: { "likes": 53, "views": 598 , "comments": [ [ { "uid": "66df2153-0db5-4c90-a04f-652fbf93098a", "created_at": "2021-10-20T04:01:07.753+00:00", "updated_at": "2021-10-20T04:01:07.753+00:00", "author": "John", "text": "Hello, just a test comment", }, { "uid": "cb090530-9b2a-4a53-a9cf-f974b9fe8bb6", "created_at": "2021-10-20T04:28:38.973051+00:00", "updated_at": "2021-10-20T04:28:38.973051+00:00", "author": "Matthew", "text": "Another test comment", } ] ], }
  • post/comment submit a new comment for a particular blog post:

    method
    POST
    input
    JSON object:
      ```json
      {
        "author": "River Costa",
        "text": "Comment text",
        "email": "[email protected]",
        "response": "This is the hCaptcha response received in the client browser on completing the challenge",
        "slug": "example-post-slug-to-identify-post"
      }
      ```
    </dd>
    <dt>response</dt><dd>400 status code if everything is good</dd>
    
    Email is only used with Akismet for spam detection as is not stored to the database.
  • post/like submit a new like or rescind an existing one for a particular blog post:

    method
    POST
    input
    JSON object:
      ```json
      {
        "id": "id of like being deleted or empty string for a new like",
        "unlike": "boolean true is like is being deleted and false otherwise",
        "slug": "example-post-slug-to-identify-post"
      }
      ```
    </dd>
    <dt>response</dt><dd>JSON object:
    
      ```json
      {
        "likes": 53,
        "id": "string: id of new like (needed to rescind it later)",
      }
      ```
    </dd>
    
  • post/message submit a message, for example from a contact page message form:

    method
    POST
    input
    JSON object:
      ```json
      {
        "name": "River Costa",
        "text": "Message text",
        "email": "[email protected]",
        "response": "This is the hCaptcha response received in the client browser on completing the challenge",
      }
      ```
    </dd>
    <dt>response</dt><dd>400 status code if everything is good</dd>
    
    The message details are forwarded by the Telegram bot including spam check result.
  • post/view submit a view, for example once the user has scrolled part way down the page of a blog post:

    method
    POST
    input
    JSON object:
      ```json
      {
        "slug": "example-post-slug-to-identify-post"
      }
      ```
    </dd>
    <dt>response</dt><dd>JSON object:
    
      ```json
      {
        "views": 599,
      }
      ```
      </dd>
    

Setup

Supabase data schema - to be added.

Troubleshooting

Top categories

Loading Svelte Themes