Coder Social home page Coder Social logo

davidje13 / refacto Goto Github PK

View Code? Open in Web Editor NEW
18.0 3.0 5.0 7.1 MB

A remote retro facilitation tool.

Home Page: https://retro.davidje13.com/

License: GNU General Public License v3.0

JavaScript 6.60% HTML 0.05% TypeScript 84.92% Less 8.15% EJS 0.28%
retro retrospective collaboration

refacto's Introduction

Refacto

A complete from-scratch re-write of Postfacto, with a focus on simplified code, development, and deployment.

Refacto

Running locally

Requires Node.js 18 or above.

npm start

The site will be available at http://localhost:5000/, using a mock Google authentication server and an in-memory database.

See CONTRIBUTING.md for full guidance on local development.

Building for deployment

You can find pre-built releases at Refacto/releases, or you can build your own:

npm run build

The output is placed in build.

Deploying

You will need to have NodeJS 18 or newer installed in the deployment environment.

cd build
npm install --omit=dev
./index.js

By default:

  • no authentication providers are available (you will need to add at least one to use the service);
  • an in-memory database is used (all data will be lost when the process ends);
  • blank secrets are used for encryption and password hashing (you can use ./scripts/random-secrets.mjs to generate a set of secure random secrets for a deployment);
  • Giphy integration is not enabled;
  • haveibeenpwned integration is enabled;
  • the server listens on port 5000.

See SERVICES.md and SECURITY.md for details.

The full list of recognised configuration options (and their default values) can be found in config/default.json (nested properties are joined and written in UPPER_SNAKE_CASE).

Typical values to configure are:

PORT=5000 \
SSO_GOOGLE_CLIENT_ID="<your-google-client-id>" \
SSO_GITHUB_CLIENT_ID="<your-github-client-id>" \
SSO_GITHUB_CLIENT_SECRET="<your-github-client-secret>" \
SSO_GITLAB_CLIENT_ID="<your-gitlab-client-id>" \
DB_URL="mongodb://localhost:27017/refacto" \
GIPHY_API_KEY="<your-giphy-api-key>" \
TRUST_PROXY="false" \
PASSWORD_WORK_FACTOR=10 \
PASSWORD_SECRET_PEPPER="<value-from-random-secrets.mjs>" \
ENCRYPTION_SECRET_KEY="<value-from-random-secrets.mjs>" \
TOKEN_SECRET_PASSPHRASE="<value-from-random-secrets.mjs>" \
./index.js

Services

See the services documentation for details on setting up a database and integrating with authentication providers.

Extra security

See the security documentation for details on configuring additional security for deployments.

refacto's People

Contributors

davidje13 avatar

Stargazers

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

Watchers

 avatar  avatar  avatar

refacto's Issues

Add ability to change retro URL

Settings page should use same availability check as creation page, but must account for the url not being changed too (i.e. its current url is always available).

When saved, all active connections should be notified (this will already happen for free), and the clients should automatically redirect to the new URL, and store this in their SlugRepository. If done in the correct order, the event should be seamless, as the WebSockets connect by retro ID, not URL.

Add option to trust all visitors

When deployed inside a private network, it may be desirable to trust all visitors and remove the login requirement for a simpler deploy and user experience. This should be off by default and incompatible with external authentication providers.

Uncaught TypeError: crypto.randomUUID is not a function

I get the following error in the console when running refacto locally. The retro page loads, but as soon as I try to submit a comment or action, it generates this console error (no reaction in the browser).

259.65606303.js:2 Uncaught TypeError: crypto.randomUUID is not a function
    at i (index.5303f92b.js:1:441)
    at 74.0b2d8329.js:1:16895
    at index.5303f92b.js:1:12254
    at 74.0b2d8329.js:1:9136
    at 74.0b2d8329.js:1:5903
    at Object.De (259.65606303.js:2:27447)
    at He (259.65606303.js:2:27601)
    at 259.65606303.js:2:47462
    at Ir (259.65606303.js:2:47556)
    at jr (259.65606303.js:2:47970)

Prevent focussing items until retro is "started"

While adding items or voting, it is easy for people to accidentally click on (and focus) an existing item, which can cause confusion.

Possible fixes:

  • add a toggle to the page for enabling item focus, which defaults to off. This could be switched on per-device, allowing the main facilitator to interact while other users still cannot focus items
  • add a "start" button to the page, which changes the shared retro state. This might be more intuitive, but would risk other people affecting the retro once it has begun
  • use another gesture to focus items, such as a double-click (might be better to use double-click-to-edit though)

Combine retro list and create retro page

To encourage re-use of existing retros, they should be displayed when logging in as prominently as creating a new retro. There is plenty of horizontal space on the desktop view, so these could be shown side-by-side.

It might also be possible to merge the import retro option into this combined page.

With these changes, the page would become a user account page, and could live at / (with redirects in place for the old pages).

Investigate dependency injection for frontend

React does not natively support dependency injection, but hooks make this a possibility. Investigate options for using dependency injection for the API services, which currently require nasty hacks to test.

Add JSON export / import

The settings page should offer a JSON export for the current retro. This should contain all retro data and settings (but not state or password information), and all archive data. This data should be unencrypted.

This data should be considered human-friendly, and should be consumable by other applications. Specifically, all times should be ISO formatted strings and no internal IDs should be included.

The retro list page should offer a way to import this data back in. After loading the data, it must be possible to change the slug before submitting, to avoid collisions.

Provide option to delete (rather than archive) items at end of retro

Action items should always be archived, but it should be possible to delete the retro items.

By default, the popup should show 3 options: Archive, Delete, Cancel. There should also be an option in the retro settings to always-archive or always-delete (with each removing the other button in the archive popup).

Alternatively, this could be a tick box in the archive popup; "erase retro items" (unticked by default but configurable as a setting).

Add Slack integration

Should be built on top of #19.

It can be desirable to have action items automatically shown in Slack, ideally with the ability to tick items from inside the message and have this propagate back to Refacto, and maybe even with @mentions for the tagged person (not clear how this would be indicated though). This could be posted when retros are archived, on a schedule, or in response to a Slack request (perhaps a mention or command)

Things to consider:

  • authentication flow: should be simple to configure for a particular retro, but different teams in the same slack organisation might want different retros syncing with different channels. Slack instance needs to trust Refacto (once?) then Refacto needs to have some way of knowing which retros are allowed to sync to which channels. Care must be taken to ensure retro data does not leak to unexpected places or become available without authentication.
  • level of interactivity: does it make sense for the bot to have available commands or respond to mentions? does self-managed scheduling make sense? (alternatively could be possible to use Slack scheduling to post a message which asks the bot to show a message (e.g. /remind #mychannel @refacto-bot what are our remaining actions? at 9:00 every weekday), but this might be more complex for users to understand)
  • key rotation and multiple instances: should the Slack access key be shared between all running instances? Having separate keys for each would hit limits on number of active keys at a time. Also if any scheduling is used, it would not be desirable for all instances to post duplicate messages.

Could use https://www.npmjs.com/package/@slack/bolt but this looks quite heavy and the necessary API seems simple enough that it probably doesn't warrant bringing in so much stuff.

Resources:

Support password managers for multiple retros

Password managers attempt to auto-fill the retro password field, but store a single password regardless of the actual retro being accessed.

It looks like a hidden input element with the retro ID would help at least some password managers understand the form, but it is not clear how well supported this approach is. Also the example uses a regular email field which is hidden via CSS, but it is worth checking if this would also work with a regular type="hidden" field with an appropriate name (according to the spec it should be valid to mark a hidden field with autofill="username", and when applied in this way autofill describes the meaning of the value, not a field to be auto-populated).

ID should be used rather than slug so that moving a retro does not invalidate saved passwords.

This change should also include replacing the current autocomplete="off" on the password (which was originally intended to avoid this problem but is apparently ignored), with autocomplete="current-password". It may also be semantically useful to mark the field as required.

Show nice error on legacy browsers

Currently unsupported browsers will show a blank or broken page. This should be replaced with an explanation that the user must upgrade their browser.

Prompt for password if token expires or is rejected

When the retro auth token expires (either due to passing exp time or due to the keys cycling), the client should detect the failure (e.g. by detecting websocket 4401/4403 or http 401/403) and automatically direct the user to the password page.

Care should be taken to avoid data loss; if the client was trying to sync, the diff should be preserved and sent again once the connection is re-established.

Remember-me option when logging in to retro

Should not be ticked by default. When enabled, the retro token should be stored in local storage / a cookie. This should be stored against the retro ID, so that multiple tokens can be stored on the same browser session.

Depends on #4 (otherwise users could become stuck with an old or invalid token)

Allow retro deletion

This should probably be restricted to the owner (logged in through SSO rather than with a password).

Add an option in the settings to delete the retro. If confirmed, the retro should be irrevocably deleted.

sessionStorage no longer works for SSO in FireFox

FireFox has changed the sessionStorage behaviour; it is no longer possible to set sessionStorage data, redirect the user to another site, get redirected back, and read that data. This is how the login nonce is currently being handled.

From the MDN description this appears to be intentional, so it may be necessary to switch to localStorage instead.

This has also revealed that the user flow if the nonce is not present is not good; currently it invokes setError(''), which makes the page appear to hang. It should show a real error message. It might even be desirable to let the user override the nonce check by clicking a button in this case (intentional user interaction mitigates the same security risks as the nonce)

Add ability to change retro passwords

Changing the password should also cycle the retro auth keys and force all connections to the retro to close immediately with 4403. This will cause all tokens to become invalid, and prompt a re-authentication flow for the users.

The user changing the password should be re-authenticated automatically. Other users should be prompted for the new password.

Might be best to handle this through a new API rather than through the WebSockets.

Depends on #4 for automatic re-authentication flow.

Auto reconnect WebSockets on close

Close events are currently detected, but ignored (except for cancelling keepalive pings)

The client should attempt to automatically reconnect if the connection is lost unintentionally. This should use retry logic with an increasing delay to avoid self-DDoS.

If connection is lost for several seconds without reconnecting, a non-intrusive message should be displayed to the user.

Add API for integration with other tools

Most necessary endpoints already exist, but some could be added for completeness and they should be documented as official APIs rather than internal APIs. Useful endpoints to add:

  • get action item list (e.g. integration with tracking tools or wallboards). Already possible by loading the full retro data but requires some filtering on the client side; could be good to offer a general items endpoint with a filter option
  • mark action item done via POST rather than WebSocket (again, integration with tracking tools). More generally, offering retro editing capabilities without websocket requirements
  • web hooks (registered & managed in settings for retro), called when retro state changes for any reason, but with delay and debouncing (maybe after 5 minutes of inactivity or after 1 hour of continuous activity)

And in the app, API key generation & management in settings (per retro)

Show indicator when somebody is typing into Add Action field

In a distributed retro, it is not obvious when somebody is entering an action; this can lead to multiple participants adding the same action, or actions being missed.

When entering text into the Add Action field, other users should see an indicator. When the action is posted, this indicator should disappear. It may be desirable to show if multiple people are entering actions.

This represents the first piece of "per user" state, so it is not obvious how it should be handled in the API. Also it is important that any text entered this way is secure (i.e. encrypted), so using the general retro state is not ideal. One possibility would be to use items with a new type. The ownership of these items may be tricky to track, and they will need to expire after some period of inactivity to ensure lost connections do not lead to a permanently visible indicator.

Possible UI options:

  • a symbol / animation to indicate somebody is typing
  • the text which is being typed appearing close to the field (e.g. to the side) - could show multiple (perhaps limited to earliest if too many)
  • text stating how many people are typing

When an action item is added, it may be desirable to briefly display it on viewers' screens (sticky) - perhaps toast-style.

Encrypt user-auth keys using master key

Currently, if the database is breached, the key used to sign JWT authentication tokens is available to the attacker. This means that until it is cycled, the attacker can generate and sign login credentials with any details they like, bypassing the encryption of the retro data.

The config table's privateKey should be encrypted in the DB using the same ENCRYPTION_SECRET_KEY which is used for retro data.

Do not mark items as done if they were probably skipped by accident

Pressing "next" closes an item and auto-selects another item, but some users confuse this for a "done" button and press it then click on another item to discuss (which has the side effect of marking the auto-selected item as done).

It may be possible to improve the UI here to make it more intuitive, but as a crutch:

  • If the user advances using the auto-advance (right arrow / "next" button)
  • Then manually selects another item within X seconds (2? configurable?)
  • Then the selection should move to the new item without marking the auto-advanced item as done

Support "break-out" retros

For large teams, it can be useful to divide the team into smaller groups for retro facilitation, but it is still useful to store all items and actions in a combined form.

This could be achieved by adding a category to items, and providing filters in the UI. The current /retros/<id> path would continue to show all items, with new /retros/<id>/sub/<category> path which applies the filtering. It may be useful for the main retro to display an indicator (such as a coloured dot) for which sub-retro an item came from (particularly useful when viewing archives).

Behind-the-scenes, the full retro state will continue to be shared to all participants, making this a display-level filter. The retro will continue to have a single password which works for the main retro and all sub-retros.

Retro state will also need to be per-category (current data structure location could continue to be used for whole retro, with a dictionary in a new location for sub-retros). Retro settings (e.g. theme) will apply to all sub-retros and does not need any change.

Handling of uncategorised items is to be decided; they could appear on all sub-retros, or could appear only in the main retro. For action items, it seems useful to display all past items in all sub-retros, but perhaps filter new items.

It may be desirable to offer functionality for dividing participants into sub-retros randomly, but this would presumably need some integration with video conferencing break-out rooms. Pulling all sub-retros back to the main retro may also be desirable (i.e. pushing a redirect to all participants). Both of these features should be split out as separate work.

Archiving will continue to apply to the whole retro.

Add alternative retro format

The current design allows different retro formats, but only "mood" retros are offered. Add another type of retro.

Remove Neutrino

Neutrino has not been updated to support WebPack 5 and it seems unlikely that it will. This is causing problems for development due to WebPack 4 using deprecated encryption methods for calculating file hashes (requiring a special environment variable)

The build system should be updated to WebPack 5 (or maybe Rollup?) with Babel, Jest, ESLint, and TypeScript managed manually rather than through Neutrino.

Allow optional read-only URLs for retros

Add an option in the settings to enable a read-only URL. This URL must contain the retro ID (not slug) and a randomised password. When provided, the password should give a short-lived (a few hours?) read-only token. This will require a second password field in the retro auth data.

After enabling, should be possible to regenerate this URL and disable it.

Depends on #3 to keep sessions open over long periods and #4 to keep refreshing the short-lived token. The refresh mechanism will be different ("password" comes from URL and a different endpoint will be needed), but should be compatible.

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.