monorepo for LingDocs Pashto dictionary, account, and backend
Go to file
adueck 6673bbb2bb infup 2024-06-13 15:09:58 -04:00
.github Create FUNDING.yml 2024-04-29 22:54:30 +04:00
account infup 2024-06-13 15:09:58 -04:00
dist/website/src/types update to GPLv3 license 2022-11-05 16:36:05 +05:00
functions infup 2024-06-13 15:09:58 -04:00
public more in repo 2021-08-18 15:54:00 +04:00
website infup 2024-06-13 15:09:58 -04:00
.firebaserc more in repo 2021-08-18 15:54:00 +04:00
.gitignore update icons 2024-04-02 16:01:08 +03:00
.npmrc more in repo 2021-08-18 15:54:00 +04:00
LICENSE update to GPLv3 license 2022-11-05 16:36:05 +05:00
LingDocsLogo-Complex.jpg add logo and readme 2023-07-26 13:24:04 +04:00
LingDocsLogoSimple.pxd add logo and readme 2023-07-26 13:24:04 +04:00
README.md update README, not using couchdb_peruser anymore 2023-12-16 23:14:50 +04:00
architecture-source.svg update readme 2021-08-18 16:03:11 +04:00
architecture.svg update readme 2021-08-18 16:03:11 +04:00
firebase.json more 2021-08-24 18:11:55 +04:00
library.ts oops 2022-09-02 10:51:25 +04:00
netlify.toml more in repo 2021-08-18 15:54:00 +04:00
package-lock.json update icons 2024-04-02 15:59:58 +03:00
package.json update icons 2024-04-02 15:59:58 +03:00
tsconfig.json transpiling on main package 2022-01-24 11:19:50 +04:00
update-inflector.sh simpler ts query 2022-11-02 10:28:14 +05:00
yarn.lock update icons 2024-04-02 15:59:58 +03:00

README.md

LingDocs Dictionary Monorepo

License: GPL v3 Netlify Status Website CI Functions CI Account Deploy Functions Deploy

LingDocs Logo

Contents

This monorepo contains:

  • /dictionary-client the frontend of the dictionary, a React SPA
  • /account a backend authentication server
  • /functions backend Firebase functions for use with the dictionary

To update the @lingdocs/pashto-inflector dependency accross the project you can use the shell script included:

./update-inflector.sh [version]

Dictionary Client

SPA Dictionary Frontend

Use Yarn.

cd website
yarn install

Development

yarn start

Account

Backend authentication server build on express / passport

Development

Use npm.

cd account
npm install
npm run dev

Functions

Backend Firebase functions

Use npm.

cd functions
npm install

Development

firebase login
# get envars locally
firebase functions:config:get > .runtimeconfig.json
# start functions emulator
npm run serve

Architecture

LingDocs Pashto Dictioanry App Architecture

Source Layer

GitHub Git Repo

The monorepo contains both a website folder for the frontend PWA and a functions folder for the backend functions. Both parts are written in TypeScript and are tied together using the types found in the @lingdocs/pashto-inflector package used by both as well as the types found in ./website/src/lib/backend-types.ts

./website frontend

The front-end website code in ./website is made with create-react-app and written in typescript with jest testing. It is a SPA and PWA.

The deployment is done automatically by netlify upon pushing to the master branch.

./functions backend

The backend code found in ./functions and is written in TypeScript.

It is compiled and deployed automatically by the repo's GitHub Actions to Firebase Cloud Functions upon pushing to the master branch.

Google Sheets Dictionary Source

The content of the dictionary is based on a Google Sheets documents containing rows with the information for each dictionary entry. This can be edited by an editor directly, or through the website frontend with editor priveledges.

A cloud function in the backend compiles the dictionary into binary form (protobuf) then uploads it into a Google Cloud Storage bucket. The deployment is triggered from the website by an editor.

Backend Layer

Firebase Functions

Serverless functions are used in conjungtion with Firebase Authentication to:

  • check if a user has elevated priveledges
  • receive edits or suggestions for the dictionary
  • compile and publish the dictionary
  • create and clean up elevated users in the CouchDB database

Account Server

Deployed through a self-hosted actions runner. It runs on an Ubuntu 20.04 machine and it requries node and redis.

The runner is launched by this line in a crontab

@reboot ./actions-runner/run.sh

Process managed by pm2 using this ecosystem.config.js

module.exports = {
  apps : [{
    name   : "account",
    cwd : "./actions-runner/_work/lingdocs-main/lingdocs-main/account",
    script: "npm",
    args: "start",
    env: {
        NODE_ENVIRONMENT: "************",
        LINGDOCS_EMAIL_HOST: "**************",
        LINGDOCS_EMAIL_USER: "**************",
        LINGDOCS_EMAIL_PASS: "*****************",
        LINGDOCS_COUCHDB: "****************",
        LINGDOCS_ACCOUNT_COOKIE_SECRET: "******************",
        LINGDOCS_ACCOUNT_GOOGLE_CLIENT_SECRET: "******************",
        LINGDOCS_ACCOUNT_TWITTER_CLIENT_SECRET: "******************",
        LINGDOCS_ACCOUNT_GITHUB_CLIENT_SECRET: "******************",
        LINGDOCS_ACCOUNT_RECAPTCHA_SECRET: "6LcVjAUcAAAAAPWUK-******************",
    }
  }]
}
pm2 start ecosystem.config.js
pm2 save

Put behind a NGINX reverse proxy with this config (encryption by LetsEncrypt)

server {
    server_name account.lingdocs.com;

    location / {
        proxy_pass http://localhost:4000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Access-Control-Allow-Origin *;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }

    error_page 500 /500.json;
    location /500.json {
        return 500 '{"ok":false,"error":"500 Internal Server Error"}';
    }

    error_page 502 /502.json;
    location /502.json {
        return 502 '{"ok":false,"error":"502 Bad Gateway"}';
    }

    error_page 503 /503.json;
    location /503.json {
        return 503 '{"ok":false,"error":"503 Service Temporarily Unavailable"}';
    }

    error_page 504 /504.json;
    location /504.json {
        return 504 '{"ok":false,"error":"504 Gateway Timeout"}';
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/account.lingdocs.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/account.lingdocs.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = account.lingdocs.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        server_name account.lingdocs.com;

        listen 80;
        listen [::]:80;
    return 404; # managed by Certbot

}

CouchDB

When a user upgrades their account level to student or editor:

  1. A doc in the _users db is created with their Firebase Authentication info, account level, and a password they can use for syncing their personal wordlistdb
  2. A user database is created which they use to sync their personal wordlist.

There is also a review-tasks database which is used to store all the review tasks for editors and syncs with the review tasks in the app for the editor(s).

Google Cloud Storage

Contains:

  • dict - the dictionary content in protobuf format
  • dict-info - information about the version of the currently available dictionary in protobuf format

The website fetches dict-info and dict as needed to check for the latest dictionary version and download it into memory/lokijs.

Frontend Layer

PWA

The frontend is a static-site PWA/SPA built with create-react-app (React/TypeScript) and deployed to Netlify.