Git Product home page Git Product logo

gogovsg's Introduction

GoGovSG

Build Status Coverage Status Gitpod Ready-to-Code

The official Singapore government link shortener.

Content

Introduction

GoGovSg is the official Singapore government link shortener, built by the Open Government Products team in GovTech. This repository serves as the codebase to serve three link shortener environments: Go.gov.sg, for.edu.sg, and for.sg.

There are multiple reasons why we built an official government link shortener:

  • URLs are too long to fit into tweets or SMSes, and difficult to remember
  • Email clients might block other commercial link shorteners if they are listed as spam on their site
  • Citizens are afraid of phishing when receiving a shortened link and unsure of where it goes

With GoGovSg, citizens are safe in the knowledge that the links are official and safe. Any authorized user can log in with their government emails and immediately create authenticated and recognisable short links.

Getting Started

Make sure you have node version 18, docker-compose version >= 1.23.1 and Docker version >= 18.09.0 installed.

*For Mac computers with Apple silicon, you will need Rosetta to be installed in order for docker-compose@v1 to work. You can do so using the following command: softwareupdate --install-rosetta.

Start by cloning the repository and installing dependencies.

# Clone the repository
git clone [email protected]:opengovsg/GoGovSG.git gogovsg

# Go inside the directory
cd gogovsg

# Install dependencies
npm install --legacy-peer-deps

Running Locally

npm run dev

Docker-compose will spin up a postgresql database and redis container to be connected to the backend server. Once the setup is complete, the local version of GoGovSG can be accessed on your browser via localhost:3000. Note that 3000 is the port number that the webpack dev server listens on; the backend server actually listens on port 8080 instead.

Because redirects are served directly from the backend, shortlinks can be accessed via localhost:3000/shortlink, but that is really being proxied to localhost:8080/shortlink. One-time passwords for all log-in attempts on localhost are obtained using maildev and accessed via http://localhost:1080/.

Setting up the infrastructure

Much of this step will involve setting up key infrastructure components since we do not have docker-compose to do that for us. On top of running the server, GoGovSG minimally requires the following infrastructure to be available:

  • A PostgreSQL database (for storing short-long URL mappings)
  • A Redis server (transient storage of sessions, one-time passwords, click statistics and frequently used shortlinks)

Other optional infrastructure used in GoGovSG:

  • Serverless functions (for migrating user links)
  • Batch jobs (for backups of our database to external source)

After these have been set up, set the environment variables according to the table below:

Server

Environment Variable Required Description/Value
NODE_ENV Yes production
DB_URI Yes The postgres connection string, e.g. postgres://postgres:postgres@postgres:5432/postgres
OG_URL Yes The origin url, used for both google analytics and circular-redirect prevention. E.g. https://go.gov.sg
AWS_S3_BUCKET Yes The bucket name used for storing file uploads.
REDIS_OTP_URI Yes Redis connection string, e.g. redis://redis:6379/0
REDIS_SESSION_URI Yes Redis connection string, e.g. redis://redis:6379/1
REDIS_REDIRECT_URI Yes Redis connection string, e.g. redis://redis:6379/2
REDIS_STAT_URI Yes Redis connection string, e.g. redis://redis:6379/3
REDIS_SAFE_BROWSING_URI Yes Redis connection string, e.g. redis://redis:6379/4
SESSION_SECRET Yes For hashing browser sessions, e.g. change-this
VALID_EMAIL_GLOB_EXPRESSION Yes The glob expression used to test if a provided email address is valid. For safety, we have disabled the use of negations, ext-glob, glob stars (**) and braces, e.g. *@youremaildomain.com
GA_TRACKING_ID No The Google Analytics tracking ID, e.g. UA-12345678-9
SENTRY_AUTH_TOKEN No To get relevant permissions to upload the source maps.
SENTRY_DNS No The Sentry DNS used for bug and error tracking. e.g. https://[email protected]/12345
SENTRY_ORG No Our Sentry organisation name, e.g. example-org
SENTRY_PROJECT No The relevant Sentry project. e.g. project-prod
SENTRY_URL No The Sentry url. e.g. https://sentry.io/
LOGIN_MESSAGE No A text message that will be displayed on the login page as a snackbar
USER_MESSAGE No A text message that will be displayed as a banner, once the user has logged in
ANNOUNCEMENT_MESSAGE No The message in the announcement displayed as a modal to users on login
ANNOUNCEMENT_TITLE No The title in the announcement displayed as a modal to users on login
ANNOUNCEMENT_SUBTITLE No The subtitle in the announcement displayed as a modal to users on login
ANNOUNCEMENT_URL No The hyperlink for the button in the announcement displayed as a modal to users on login
ANNOUNCEMENT_IMAGE No The image in the announcement displayed as a modal to users on login
ANNOUNCEMENT_BUTTON_TEXT No The text on the button in the announcement displayed as a modal to users on login
ROTATED_LINKS No List of comma separated path of links to rotate on the landing page
CSP_REPORT_URI No A URI to report CSP violations to.
CSP_ONLY_REPORT_VIOLATIONS No Only report CSP violations, do not enforce.
CLOUDMERSIVE_KEY No API key for access to Cloudmersive.
SAFE_BROWSING_KEY No API key for access to Google Safe Browsing.
SAFE_BROWSING_LOG_ONLY No Boolean, whether to log only, or throw error if unsafe link is found by Google SafeBrowsing. Defaults to false
ASSET_VARIANT Yes Asset variant specifying environment for deployment, one of gov, edu, health
COOKIE_MAX_AGE No Session duration of cookie in milliseconds. Defaults to 86400000 (1 day)
BULK_UPLOAD_MAX_NUM No Maximum number of links that can be bulk uploaded at once. Defaults to 1000
BULK_UPLOAD_RANDOM_STR_LENGTH No String length of randomly generated shortUrl in bulk upload. Defaults to 8
BULK_QR_CODE_BATCH_SIZE No Maximum batch size of QR codes to generate in a single Lambda run. Defaults to 1000
BULK_QR_CODE_BUCKET_URL No Link to download QR codes from
ACTIVATE_BULK_QR_CODE_GENERATION No Whether to start Lambda for bulk QR code generation or not. Defaults to false
REPLICA_URI Yes The postgres connection string, e.g. postgres://postgres:postgres@postgres:5432/postgres
SQS_BULK_QRCODE_GENERATE_START_URL No The SQS queue for starting QR code bulk generation Lambda
SQS_TIMEOUT No Duration of time in ms for sending to SQS queue before timeout. Defaults to 10000ms (10s)
SQS_REGION No AWS Region of SQS queue for starting QR code bulk generation Lambda
JOB_POLL_ATTEMPTS No Number of attempts for long polling of job status before timeout of 408 is returned. Defaults to 12
JOB_POLL_INTERVAL No Interval of time between attempts for long polling of job status in ms. Defaults to 5000ms (5s)
API_LINK_RANDOM_STR_LENGTH No String length of randomly generated shortUrl in API created links. Defaults to 8
FF_EXTERNAL_API No Boolean, feature flag for enabling the external and admin API. Defaults to false
ADMIN_API_EMAILS No Emails with admin API access, separated by commas without spaces. Defaults to none.
FF_USE_REPLICA_FOR_REDIRECTS No Boolean, feature flag for using the replica database to look up redirects to long URLs. Defaults to false

Serverless functions for link migration

Secrets Required Description/Value Shared across environments
DATABASE_URL Yes The postgres connection string, e.g. postgres://postgres:postgres@postgres:5432/postgres No

Batch functions for backups

Secrets Required Description/Value Shared across environments
DB_URI Yes The postgres connection string, e.g. postgres://postgres:postgres@postgres:5432/postgres No
GCS_CREDENTIALS Yes Authorization credentials for writing to backup buckets in GCS Yes
Environment Variable Required Description/Value Shared across environments
GCS_BUCKET Yes Name of bucket in GCS to write to No
CRONITOR_MONITOR_CODE No ID for Cronitor monitor to monitor batch jobs No

Trigger the typescript compilation and webpack bundling process by calling npm run build.

Finally, start the production server by running npm start.

Deploying

GoGovSG uses Github Actions and Serverless to deploy to AWS Elastic Beanstalk and AWS Lambda. We also use Sentry.io to track client-side errors.

Secrets Required Description/Value
AWS_ACCESS_KEY_ID Yes AWS credential ID used to deploy to Elastic and Modify files on S3
AWS_SECRET_ACCESS_KEY Yes AWS credential secret used to deploy to Elastic Beanstalk and Modify files on S3
SENTRY_AUTH_TOKEN No To get relevant permissions to upload the source maps
GITHUB_TOKEN Yes* Used by Coveralls to verify test coverage on repo. Does not need to be manually specified as it is specified by Github Actions. More Info
DD_API_KEY Yes* Datadog API Key used for integration with Datadog to Trace/Logs collection
DD_SERVICE No Datadog service name to be used for the application
DD_ENV No Datadog application environment, e.g. staging, production
Environment Variable Required Description/Value
EB_ENV_(EDU_/HEALTH_)PRODUCTION, EB_ENV_(EDU_/HEALTH_)STAGING Yes Elastic Beanstalk environment name
EB_APP_PRODUCTION, EB_APP_STAGING Yes Elastic Beanstalk application name
EB_BUCKET_PRODUCTION, EB_BUCKET_STAGING Yes S3 bucket used to store the application bundle
PRODUCTION_BRANCH, STAGING_BRANCH Yes Name of Git branches for triggerring deployments to production/staging respectively
ECR_URL Yes AWS ECR Docker container registry URI to push built images to
ECR_REPO Yes Name of repository in AWS ECR containing images
SENTRY_ORG No Sentry.io organisation name
SENTRY_PROJECT_PRODUCTION, SENTRY_PROJECT_STAGING No Sentry.io project name
SENTRY_URL No Sentry.io URL e.g. https://sentry.io/
SENTRY_DNS_PRODUCTION,SENTRY_DNS_STAGING No Sentry.io endpoint to post client-side errors to
API_KEY_SALT Yes Salt used for APIKey hashing, guide to salt generation here

Operations

Transferring links to a new owner or email address

Functions to safely transfer links to new owners can be accessed on AWS Lambda console (for authorized users only).

To transfer a single link to a new email address (must be lowercase), please use the relevant Lambda function (gogov-production, edu-production, health-production) to create an event with the following event body:

{
  "shortUrl": "<short url to be transfered>",
  "toUserEmail": "<user email to transfer to>"
}

To transfer all links belonging to an account to another account, please use the relevant Lambda function (gogov-production, edu-production, health-production) to create an event with the following event body:

{
  "fromUserEmail": "<user email to transfer from>",
  "toUserEmail": "<user email to transfer to>"
}

Developer Documentation

Folder Structure

All source code resides in the src directory. Inside src, there is client and server directory. Frontend code (react, css, js and other assets) will be in client directory. Backend Node.js/Express code will be in the server directory.

Asset variants

This repository serves as the codebase to serve three link shortener environments: Go.gov.sg, for.edu.sg, and for.sg. These environments are run on separate infrastructure, and the deployment pipeline is set up to deploy any code changes in this codebase across all infrastructure environments. The environments are identical apart from the assets, copy and list of authorized users.

Babel

Babel helps us to write code in the latest version of JavaScript. If an environment does not support certain features natively, Babel will help us to compile those features down to a supported version. It also helps us to convert JSX to Javascript. babel.config.json file is used to describe the configurations required for Babel.

Babel requires plugins to do the transformation. Presets are the set of plugins defined by Babel. Preset env allows to use babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017 and it will transform them to ES5. Preset react allows us to use JSX syntax and it will transform JSX to Javascript.

ESLint

ESLint is a pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript.

.eslintrc.json file (alternatively configurations can we written in Javascript or YAML as well) is used describe the configurations required for ESLint.

I am using Airbnb's Javascript Style Guide which is used by many JavaScript developers worldwide. Since we are going to write both client (browser) and server side (Node.js) code, I am setting the env to browser and node. Optionally, we can override the Airbnb's configurations to suit our needs. I have turned off no-console, comma-dangle and react/jsx-filename-extension rules.

Webpack

Webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser.

webpack.config.js file is used to describe the configurations required for webpack.

  1. entry: entry:ย ./src/client/app/index.tsx is where the application starts executing and webpack starts bundling. Note: babel-polyfill is added to support async/await. Read more here.
  2. output path and filename: the target directory and the filename for the bundled output
  3. resolve: We use aliasing at bundle time to inject and resolve the right asset variant path, which allows us to easily switch between asset folders for the different environments.
  4. module loaders: Module loaders are transformations that are applied on the source code of a module. We pass all the js file through babel-loader to transform JSX to Javascript. Fonts and images are loaded through file-loader.
  5. Dev Server: Configurations for the webpack-dev-server which will be described incoming section.
  6. plugins: clean-webpack-plugin is a webpack plugin to remove the build folder(s) before building. html-webpack-plugin simplifies creation of HTML files to serve your webpack bundles. It loads the template (public/index.html) and injects the output bundle.

Webpack dev server

Webpack dev server is used along with webpack. It provides a development server that provides live reloading for the client side code. This should be used for development only.

The devServer section of webpack.config.js contains the configuration required to run webpack-dev-server which is given below.

devServer: {
    port: 3000,
    open: true,
    proxy: {
        "/api": "http://localhost:8080"
    }
}

Port specifies the Webpack dev server to listen on this particular port (3000 in this case). When open is set to true, it will automatically open the home page on startup. Proxying URLs can be useful when we have a separate API backend development server and we want to send API requests on the same domain. In our case, we have a Node.js/Express backend where we want to send the API requests to.

ts-node-dev

ts-node-dev will monitor for any changes in the server source code and automatically restart the server. This is used in development only. It watches changes to all files required from the entry point onwards, and will restart the node server whenever such files are modified.

ts-node-dev also allows debugging via Inspector. The quickest way to debug the application will be to use Chrome DevTools via chrome://inspect. VSCode users may want to add the following to their launch.json to quickly attach VSCode to the application for debugging:

    {
      "type": "node",
      "request": "attach",
      "name": "Inspect",
      "protocol": "inspector",
      "port": 9229,
      "restart": true,
      "cwd": "${workspaceFolder}"
    }

Express

Express is a web application framework for Node.js. It is used to build our backend API's.

src/server/index.ts is the entry point to the server application. This starts a server and listens on port 8080 for connections. It is also configured to serve the static files from dist directory.

Concurrently

Concurrently is used to run multiple commands concurrently. I am using it to run the webpack dev server and the backend node server concurrently in the development environment. Below are the npm/yarn script commands used.

"client": "webpack-dev-server --mode development --devtool inline-source-map --hot",
"server": "nodemon src/server/index.js",
"dev": "concurrently \"npm run server\" \"npm run client\""

VSCode + ESLint

VSCode is a lightweight but powerful source code editor. ESLint takes care of the code-quality.

Redux Devtools

Developer Tools to power-up Redux development workflow.

It can be used as a browser extension (for Chrome, Edge and Firefox), as a standalone app or as a React component integrated in the client app.

Infrastructure

Diagrams for our infrastructure setup can be found here.

Mics

Salt generation

let salt = bcrypt.genSaltSync(10)

gogovsg's People

Contributors

barnettx avatar dependabot[bot] avatar developerdenesh avatar diskeith avatar fterh avatar gweiying avatar halfwhole avatar ilikepieogp avatar jasonchong96 avatar jimvae avatar koalatasha avatar kylerwsm avatar liangyuanruo avatar lonerifle avatar orbitalsqwib avatar oversparkling avatar oxiang avatar pikkapikkachu avatar reiallenramos avatar snyk-bot avatar stanleynguyen avatar thamsimun avatar thanhdatle avatar theodoregc avatar wanlingt avatar waynetee avatar xming13 avatar yong-jie avatar zwliew avatar zxt-tzx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gogovsg's Issues

Improve invalid domain toaster messages

Describe the enhancement
Currently, our domain expression is fetched from the backend when user is routed to the login page. But since we are using client side routing, there may cases where users fetch the app but not the domain expression (i.e. bad or no connectivity when clicking on the login button). When that happens, the app does not prompt the user when he/she is using an email with an invalid domain.

While our backend does our good job in picking invalid domains, the message shown in the error toaster might not be intuitive enough for the user to think that the domain they are using is invalid. Relevant screenshot below.

Edit for more context: Some relevant files to look at may be the verify email api endpoint (which returns the verification toaster text) and the environment variables (to find the valid email domain used) in our app.

Screenshot
image

Security review of upload implementation

S3 bucket

  • Verify upload permissions are correctly set
  • CORS headers for PUT requests are limited to go.gov.sg domain

Presigned URL

  • API route to generate a presigned URL requires login
  • Verify that implementation uses MD5 hash for upload integrity
  • Upload links expire after 5 minutes

Prerequisite #35

Coveralls.io for sharing test coverage

Is your feature request related to a problem? Please describe.
Get the great coverage reporting of coveralls.io, and add a cool coverage button to the README.

Describe the solution you'd like
Integrate jest with coveralls.
Side quest - add the Travis build button to the README at the same time.

Login snackbar persists through the User page

Describe the bug
A race condition happens between a server request and a ReactRouter redirection.

When a logged in user refreshes the /user route, LoginPage requests from the backend for a message to be supplied to the snackbar:

    get('/api/login/message').then((response) => {
      if (response.ok) {
        response.text().then((text) => {
          if (text) setLoginInfoMessage(text)
        })
      }
    })

But once the function resolves, ReactRouter has finished re-routing to the /user route -- displaying a misleading/unnecessary message.

To Reproduce
Steps to reproduce the behavior:

  1. Login with email, enter OTP
  2. While in /#/user, refresh the page

Expected behavior

  1. Your OTP might take awhile to get to you. should not be displayed during a successful login.

Screenshots
snackbar-persists

Desktop (please complete the following information):

  • OS: Ubuntu 18.04
  • Browser: Chrome

Additional context
Digging through the codebase tells me you're reading the environment variables server-side, even the strings intended for snackbar use. Why not just store those in translation.js to prevent this race condition?

Get a basic file upload flow working

To unblock work, we first need to get a basic upload flow working from the UI

  • Generate pre-signed URL on the server and return it to the client
  • PUT file to S3 from the browser
  • Shorten the link to S3
  • Able to GET the file from the short link

Design for UI and upload flow

Come up with a design for the upload flow for engineers to work towards.

  • Able to upload on creation
  • Able to replace previously uploaded files
  • Must not be able to edit the long URL
  • Error message for
    • invalid file extensions
    • Upload failure

Login credentials for contributors

Is your feature request related to a problem? Please describe.
I got the app to work but can't look around further without login credentials.

Describe the solution you'd like
A task to migrate admin credentials to the database.

Describe alternatives you've considered
Maybe it's documented somewhere and I just missed it.

Introduce unit tests for logout middleware

Is your feature request related to a problem? Please describe.
Introduce backend tests for the /logout.ts middleware.

Describe the solution you'd like

  1. Refactor logout.ts for testability by sharding out a data access service and introducing inversion of control (IoC) using inversify.js. Refer to redirect.ts, login.ts for examples.
  2. Mock the data access service when testing the middleware functions
  3. Add unit tests

Words in text boxes got cut off on iphone7

Describe the bug
Upon logging in on an iOS device (iPhone7), the text inside the text boxes got cut off

To Reproduce
Steps to reproduce the behavior:

  1. Log into go.gov.sg
  2. Click on 'Create link' button
  3. When you type something into the textboxes, the words will be cut off (see below)
    image (2)

Expected behavior

The words in the textboxes should not be cut off
Screenshot_2020-04-08-17-48-42-23

Smartphone (please complete the following information):

  • Device: iPhone7
  • OS: -
  • Browser: Chrome
  • Version: -

S3 service for local development

Is your feature request related to a problem? Please describe.
Development today requires the use of an actual S3 bucket to be set up on AWS.

Describe the solution you'd like
Add an S3 service such as localstack to the local development environment.

Describe alternatives you've considered
An alternative service to consider is Minio.

Landing page redirects to user page after sign out on Internet Explorer

Describe the bug
User is redirected to login page when signing out, but is redirected back to the user page when he visits the landing page

Even though the user can then see the user page with the links displayed, the user cannot create or modify links while logged out

To Reproduce
Steps to reproduce the behavior:

  1. Sign in on IE
  2. Sign out
  3. Click the logo
  4. User redirected to user page

Expected behavior
Landing page displayed

Desktop (please complete the following information):

  • OS: Windows 10
  • IE 11

Do not cache frontend assets

Describe the bug
Today the application returns cache-control public, max-age=14400 for bundle.js. This can cause issues with new releases.

To Reproduce
Steps to reproduce the behavior:

  1. Go to Network tab in Chrome devtools
  2. Inspect the returned headers

Expected behavior
We don't want cache behaviors from our browsers.

Animate links on landing page that are clickable

Is your feature request related to a problem? Please describe.
Members of public on landing page might be wondering why only one link go.gov.sg/whatsapp is present

Describe the solution you'd like
To illustrate go.gov.sg as a link shortener for many links, animate multiple links moving in and out of this box. Also make these links clickable. Can choose ten hard coded links for now:
go.gov.sg/circuitbreaker
go.gov.sg/filetax
go.gov.sg/passport
go.gov.sg/1nsea100
go.gov.sg/covid-19-dashboard
go.gov.sg/mohupdates
go.gov.sg/generalexemption
go.gov.sg/armydeclare
go.gov.sg/moe-hbl
go.gov.sg/going-overseas
go.gov.sg/temporary-relief-fund

Ideally make these links editable through an env var that perhaps is comma separated.

Fix QR code generation on Internet Explorer

Describe the bug
Currently, the QR codes cannot be seen or downloaded on Internet Explorer

To Reproduce
Steps to reproduce the behavior:

  1. Log into go.gov.sg
  2. Click on QR code icon

Expected behavior
QR code should be visible upon clicking the QR code icon

Screenshots
image

Desktop (please complete the following information):
OS: Windows 10
Browser: IE 11

Mass link creation by uploading csv

Describe feature request
Many users are of go.gov.sg are public officers from the communications team in the organisation. Due to the volume of external/internal mass communication that these public officers are doing such as creation of marketing campaigns, they are often generating a large number of go.gov.sg links.

Instead of creating these links individually, we should allow users to do upload short links via a csv.

Describe the solution you'd like

  1. Upload csv button that on click takes in long link and optional short link columns
  2. Download sample csv with two labelled columns
  3. If short link not present randomly generate one

This is still pending design and put on hold for dev.

Boxicons local development issue

Describe the bug
When using icons from the box-icons library in development, we frequently encounter situations whereby the icons do not load in development. This occurrence can be observed in Safari and IE11. This is a problem as it causes us long term hindrance.

To Reproduce
Steps to reproduce the behavior:

  1. Run GoGovSg locally.
  2. Launch the application locally on Safari or IE11.
  3. Observe that some icons are not showing.

Expected behavior
All icons should be showing even while in local development.

Inconsistent SVG QR code sizes

Describe the bug
QR code .svg size changes after .png version is download.

To Reproduce
Steps to reproduce the behavior:

  1. Log into your go.gov.sg with your gov.sg email.
  2. Show the QR code by creating a new short link or use an existing one.
  3. Download the .svg version, followed by .png and then .svg version again.
  4. In your download folder, you will see that the two .svg files have slightly different sizes.

Expected behavior
The above-mentioned .svg files downloaded should be identical.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: MacOS
  • Browser: Chrome
  • Version 80.0.3987.163 (Official Build) (64-bit)

Basic API to shorten links, edit long links, make link active/inactive and fetch links

This is an enhancement request

A few of our users are using go.gov.sg to shorten a bulk of their links. Rather than creating the link one by one, it would be much faster for them to do so through an API directly.

Suggested approach/solution

  • Login to go.gov.sg to manage API keys (create, delete)
  • Supply API key in call

APIs:

  • Shorten link
  • Edit long link
  • Make link active/inactive
  • Fetch all your long links

After tags are out, can tag and fetch links based on tags.

Additional contexts
Additional details for go.gov.sg team is linked separately in our internal documentation.

Upload pdf/image to a short link

This is an enhancement request
Instead of just shortening long links can we allow user to upload pdf/image which will generate a short link? Users might wish to share a poster but not as intuitive for them to host the image themselves and then shorten it.

An example of how an existing user is doing this: https://www.iras.gov.sg/irashome/uploadedfiles/irashome/quick_links/r1.pdf

Longer term considerations
If we move go.gov.sg to government intranet network, we can start shortening email attachments too. We could look at building an Outlook plugin to auto shorten large attachments into links like those in Gmail.

Note that we should make it clear this pdf/image that the user is uploading should be not be a sensitive content (unclassified in government terms). If we want to support classified pdf/image, we can plug the private key into the link, encrypt the content, then send it to our server (although in this case we won't be shortening that link anymore).

Transition page

Describe feature request
A few reasons why a transition page would be helpful:

  1. Anti phishing: Citizens should land on a page to verify go.gov.sg before being led off to long link. Phishers can easily place go.gov.sg in front of their QR codes pointing to malicious domains.
  2. User experience: Users can anticipate if the long link is something they want to visit before actually visiting

Describe the solution you'd like

  1. Serve transition page once a user visits any go.gov.sg link.
  2. Button click to move past the page
  3. If button not clicked, transition page lasts for 5 seconds (To be confirmed by designers if this should be removed because it can be destabilising)
  4. Transition page should still serve meta tag information on destination page on social media

In the future, there can be another issue to customise message that appears on the transition page.

Sample screenshot:
image

Migrate name servers from Central DNS to Cloudflare

Describe feature request

All gov.sg domain has to be purchased/procured through the IT Service Management (ITSM) portal. The DNS is also managed centrally by Govtech.

Go.gov.sg is currently managed centrally through ITSM and we're using Cloudflare as CDN.
Additionally, setting up CNAME on Cloudflare requires a Business account, which is incurring us cost.

Suggested solution
In order to avoid such complicated setup, we should move DNS management from government ITSM over to Cloudflare.

This saves us cost, and it could also potentially speed up DNS lookup.

Introduce unit tests for links middleware

Is your feature request related to a problem? Please describe.
Introduce backend tests for the /links.ts middleware.

Describe the solution you'd like

  1. Refactor links.ts for testability by sharding out a data access service and introducing inversion of control (IoC) using inversify.js. Refer to redirect.ts, login.ts for examples.
  2. Mock the data access service when testing the middleware functions
  3. Add unit tests

Sentry should upload source maps

Sentry should receive source maps during webpack build, so that the client-side stack trace can be made available to developers within the Sentry.io dashboard.

UI looks weird on IE11

Describe the bug
UI does not look right on IE11. This might got to do with buggy flexbox support by IE11.

To Reproduce
Steps to reproduce the behavior:

  1. On a IE11 browser, go to go.gov.sg.
  2. Sign in with your gov.sg credentials.
  3. Observe the UI.

Expected behavior
The table layout should be similar to what is shown on other modern browsers, like Chrome, Firefox, or Safari.

Screenshots
image

Desktop (please complete the following information):

  • OS: Windows 10
  • Browser: IE
  • Version: 11

Introduce unit tests for user middleware

Is your feature request related to a problem? Please describe.
Introduce backend tests for the /user.ts middleware.

Describe the solution you'd like

  1. Refactor user.ts for testability by sharding out a data access service and introducing inversion of control (IoC) using inversify.js. Refer to redirect.ts, login.ts for examples.
  2. Mock the data access service when testing the middleware functions
  3. Add unit tests

Introduce unit tests for redirect middleware

Is your feature request related to a problem? Please describe.
Introduce backend tests for the system, starting with the /redirect middleware.

Describe the solution you'd like

  1. Refactor redirect.ts for testability by sharding out a data access service and introducing inversion of control (IoC).
  2. Introduce Jest as a test framework
  3. Mock the data access service when testing the middleware redirect function

Additional context
Add any other context or screenshots about the feature request here.

Edit release branch to master and staging branch to develop

Describe feature request

The current naming convention of our branches may be misleading. There are currently 2 branches:

  • Release - our stable and production branch
  • Staging - where all active development is being done

This is especially important since we are opensourcing our repository. Aim here is to make our repo to be easily accessible and understood.

Suggested approach/solution

  • Release should be renamed to master
  • Staging should be renamed to develop

HomePage tweaks

Is your feature request related to a problem? Please describe.
There are proposed improvements to the HomePage, these updates can be found on Zeplin. The changes include some rearrangements of elements and adding of new graphics.

Describe the solution you'd like
As much as possible, graphics added should be .svg. And the rearrangements of elements should maintain the current mobile-first and responsive layout approach.

Notify agency when content on long links change

Is your feature request related to a problem? Please describe.
How do I know if the content of my long link has changed, or become not relevant? Should go.gov tell me that I should be turning the link off?

Describe the solution you'd like
One possible solution could be to change the colour of that particular long link on the dashboard.
And when use hovers, they should see a tooltip showing that the content has changed.

Introduce code formatter

Is your feature request related to a problem? Please describe.
Currently the codebase has a linter in the form of ESLint but not a formatter. Introducing an opinionated formatter will standardise syntax and help developers focus on code delivery.

Describe the solution you'd like
Integrate prettier into ESLint and the CI toolchain. The solution should include formatting for JS/JSX/TS, and employ pre-commit hooks for staged files.

A sample implementation for AngularJS is available in the formsg repo.

Go.gov.sg support for poor connectivity

Is your feature request related to a problem? Please describe.
Under poor connectivity (2G or less), UI might show weird states, for e.g. #31

Describe the solution you'd like
A possible solution is to detect poor connectivity, and disable the UI to show a generic error page saying low connectivity. This error page can be a similar design to that of a non-existent link, e.g. https://go.gov.sg/329343ejrgjr, with the copy of header being "Poor connectivity", and body being "You seem to be disconnected to the Internet. Kindly revisit the page later."

Use Joi for backend endpoint validation

Is your feature request related to a problem? Please describe.
Currently, validators are done in an imperative way via middlewares for every API endpoint on express. Although this gives us a lot of control, these validator middlewares can get quite messy for endpoints that serve multiple functions.

Describe the solution you'd like
Joi validators allow us to write validation logic in a declarative way without losing too much control.

Modify long URL to use file.go.gov.sg

The first cut implementation saves the S3 link to the database. Modify the long upload links to use file.go.gov.sg instead so that citizens can see that files are available from a government domain.

Tag links with background info

Describe feature request
This feature would help users who manage a large number of links by implementing tags on their links.

Using tags, users can easily group and sort their links, they can also view the aggregated analytics/stats from the links that share the same tags.

Describe the solution you'd like

  1. Assign a tag on link drawer
  2. Filter by tags

Scaling of homepage on large viewports

Is your feature request related to a problem? Please describe.
The homepage scales fine on smaller viewports but the section with the words Are you a public officer? Sign in is abnormally tall on larger viewports with respect to other elements on the page.

Describe the solution you'd like
More "natural" scaling when it comes to a larger viewport

Screenshots
The following is the current state of the homepage on large viewports.

image

Viewport size: 2148x1336

Additional context
As seen in the screenshot, the solid colour section is a lot taller than its elements.

Pre-push hook allows improper formatting to be pushed

Describe the bug
The current pre-push hook runs npm run lint which runs eslint with the --fix flag. This allows the linter to run successfully even though the pushed code contains formatting issues that are fixed by the --fix flag. Since the commit has already been done, the flag does nothing to truly resolve the issue.

To Reproduce
Steps to reproduce the behavior:

  1. Change any .ts/.js file to have 4 space indentation
  2. Create a commit
  3. Push to upstream (Github)

Expected behavior
The pre-push hook should have exited with a non-zero exit code to signify that there are formatting issues.

Additional context
One way to fix this would be removing the --fix flag from the lint script and creating another script with the flag for developers.

Analytics panel

Is your feature request related to a problem? Please describe.
It is important to agencies to monitor effectiveness of their materials. And will like to measure basic metrics such as unique users visiting, browsers visited from, channels, etc.

Describe the solution you'd like

  1. Already hooked link information to Google Analytics
  2. Extend link drawer to include charts from Google Analytics data

image

To get started on this, we will have to share staging read access to Google Analytics with you.

Introduce unit tests for sentry middleware

Is your feature request related to a problem? Please describe.
Introduce backend tests for the /sentry.ts middleware.

Describe the solution you'd like

  1. Refactor sentry.ts for testability by sharding out a data access service and introducing inversion of control (IoC) using inversify.js. Refer to redirect.ts, login.ts for examples.
  2. Mock the data access service when testing the middleware functions
  3. Add unit tests

Link click history

Is your feature request related to a problem? Please describe.
Today we only show one number for links - cumulative count. This does not help agencies who repeatedly use the same link for multiple campaigns through the year analyse popularity per campaign. Not as feasible to recirculate a new link per campaign.

Describe the solution you'd like

  1. Start storing click timestamps
  2. Display link clicks in a graph on Analytics panel
  3. Given high volume of link clicks (40mil till date), scalable solution needed

See graph of clicks over time below (related issue #54 ):
image

Bundle QR code with link in same image

Is your feature request related to a problem? Please describe.
Today QR codes are in their own image. However this is a phishing risk if link is not displayed below the QR code so user has the option to check that QR code indeed directs to the link. It is also a usability issue if citizens prefer to type links than scan QR codes.

Describe the solution you'd like
Include short link inside image of QR code so both are shared together. Short link can be below the QR code.

Unify PATCH endpoints for link edit operations

Is your feature request related to a problem? Please describe.
At present, two endpoints exist for editing links in GoGovSG. The first allows one to modify the longUrl, and another to toggle the ACTIVE or INACTIVE state of the link.

Describe the solution you'd like
Perhaps a better approach would be to unify these two endpoints into a single PATCH endpoint. To counter the potential complication in logic, we could make use of dependency injection provided by inversify to route different operations to different 'resolvers' in the same endpoint handler.

Customise transition page

Is your feature request related to a problem? Please describe.
Users don't often read content surrounding the Go link. For example, a public bus stop banner can include instructions on filling in a form with a Go QR, but users might not read the banner and jump straight to scan the QR.

Describe the solution you'd like
Allow agency to customise a paragraph of text on the transition page. Agencies could include instructions and details of the destination link.

A challenge here is edits to this description will not propagate to all users because they only see the transition page once. We can consider storing a description updated timestamp which is compared against cookie timestamp for this link, and to show the transition page and replace the cookie if description updated timestamp is more recent than cookie timestamp.

This description can also be used for public facing search in the future.

Add Google Analytics to measure transition page drop off

Is your feature request related to a problem? Please describe.
We launched transition page, and there have been no agency or citizen complaints, but we are not quantitatively certain transition page is not locking users out of their long links.

Describe the solution you'd like
Theoretically can track a) each page's view, and b) emit Proceed button click (by passing in short link too so can map back to page view). If users are not locked out, b) should not be that much lower than a).

This is an important P2. Good to do this sooner rather than later. We just launched transition page and want to make sure users are not locked out.

Email QR code to self

Is your feature request related to a problem? Please describe.
Today after generating the QR code agency typically transfers it back to their government email to circulate. As Go.gov.sg is on the Internet, officers might have to use their personal emails to email themselves. No sensitivity concerns as links are public anyway, but is quite troublesome.

Describe the solution you'd like
1. Shift QR code generator to the server done
2. Hook into SES to email QR code to logged in user
3. "Email self" button on the QR code modal

Introduce unit tests for statistics middleware

Is your feature request related to a problem? Please describe.
Introduce backend tests for the /statistics.ts middleware.

Describe the solution you'd like

  1. Refactor statistics.ts for testability by sharding out a data access service and introducing inversion of control (IoC) using inversify.js. Refer to redirect.ts, login.ts for examples.
  2. Mock the data access service when testing the middleware functions
  3. Add unit tests

Problem with PNG downloads on IE11

Describe the bug
In the midst of our v2 redesign, our changes have caused PNG QR code downloads on IE11 to not work. Svg downloads still works as intended.

I re-tested in on other browsers, and PNG and SVG downloads are still working well on Chrome Firefox, and Safari.

Moving forward, I have added new tests to our manual release checklist to ensure that QR codes are also working well also on IE11.

To Reproduce
Steps to reproduce the behavior:

  1. Go to go.gov.sg, and sign in with our gov.sg credentials.
  2. Click on any created links or create one.
  3. Try downloading the QR code as a PNG image.
  4. Observe that nothing is happening.

Expected behavior
The QR code should be downloaded to the user's download folder.

Desktop (please complete the following information):

  • OS: Windows 10
  • Browser: IE
  • Version: 11

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.