{"id":20574759,"url":"https://github.com/macpaw/together-app","last_synced_at":"2025-08-18T13:33:08.105Z","repository":{"id":39849439,"uuid":"470537725","full_name":"MacPaw/together-app","owner":"MacPaw","description":":ukraine: A self-hosted app for keeping track of employee wellbeing and dislocation during the Russo-Ukrainian war, with an interactive map.","archived":false,"fork":false,"pushed_at":"2024-06-18T14:17:02.000Z","size":2874,"stargazers_count":126,"open_issues_count":3,"forks_count":25,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-12-11T07:52:04.778Z","etag":null,"topics":["employee-management","hr","map","maps","standwithukraine","war","wellbeing"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MacPaw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-16T10:40:23.000Z","updated_at":"2024-12-05T17:37:12.000Z","dependencies_parsed_at":"2023-02-08T18:45:47.646Z","dependency_job_id":null,"html_url":"https://github.com/MacPaw/together-app","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MacPaw%2Ftogether-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MacPaw%2Ftogether-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MacPaw%2Ftogether-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MacPaw%2Ftogether-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MacPaw","download_url":"https://codeload.github.com/MacPaw/together-app/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230238281,"owners_count":18194988,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["employee-management","hr","map","maps","standwithukraine","war","wellbeing"],"created_at":"2024-11-16T05:37:01.056Z","updated_at":"2024-12-18T08:07:58.853Z","avatar_url":"https://github.com/MacPaw.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/images/logo.png\" alt=\"Logo\" width=\"600px\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ch3 align=\"center\"\u003eHelping organizations stay together and help their members in times of disaster\u003c/h3\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    On February 24th, 2022, the lives of the entire Ukrainian nation were disrupted and thrown into uncertainty because of the unprovoked, illegal invasion by the Russian Federation. As a result, millions have been dislocated and are at risk within war zones throughout Ukraine.\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    Ukrainian businesses have become key in the resistance – providing aid to employees, coordinating humanitarian aid nation-wide, and contributing to anti-war initiatives, all while trying to keep their organizations viable.          \n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    \u003cstrong\u003eTogether App\u003c/strong\u003e aims to help organizations be tight-knit communities for their members. It’s a simple way to know where your people are, to know when and how to help each other, and to have moments of real human connection when they are most needed.\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    After all, we are in this \u003cstrong\u003etogether\u003c/strong\u003e.     \n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    \u003ca href=\"#floppy_disk--deploying-together-app\"\u003e\u003cstrong\u003eDeploying Together App\u003c/strong\u003e\u003c/a\u003e\n    ·\n    \u003ca href=\"#wrench--custom-app-configuration\"\u003e\u003cstrong\u003eCustomization\u003c/strong\u003e\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/MacPaw/together-app/issues\"\u003e\u003cstrong\u003eOpen a GitHub Issue\u003c/strong\u003e\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/p\u003e\n\n***\n\n\u003cp align=\"center\"\u003e\n    \u003ch3 align=\"center\"\u003eKeep track of member well-being with daily check-ins.\u003c/h3\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    In times of crisis, people move around quickly, connection gets disrupted, and safety conditions change rapidly. Every day, the \u003cstrong\u003eTogether App\u003c/strong\u003e Slack bot asks members to check in – providing information about their whereabouts, wellbeing, and their ability to contribute to team efforts. \n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    The check-in only takes seconds but it provides a clear, actionable overview of organization members for the wellbeing stakeholders. Nobody is left behind and everybody is on the same page. \n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/images/check-in-demo.gif\" alt=\"Daily Check Ins\"\u003e\n\u003c/p\u003e\n\n*** \n\n\u003cp align=\"center\"\u003e\n    \u003ch3 align=\"center\"\u003eStay secure – physically and digitally.\u003c/h3\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cstrong\u003eTogether App’s\u003c/strong\u003e check-in requests are sent to all members via its Slack bot. We use Slack because it’s a trusted and reachable tool for companies worldwide.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    Additionally, the application never stores a member’s exact location to avoid collecting sensitive data like a refugee camp or bomb shelter in a city under attack. It stores only the coordinates of the actual city in which the member is located.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    These protective measures are crucial now that many Ukrainian companies are withstanding constant cyber threats coming from Russia. With \u003cstrong\u003eTogether App\u003c/strong\u003e, your company’s and your people’s data is safe.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/images/members-demo.gif\" alt=\"Daily Check Ins\"\u003e\n\u003c/p\u003e\n\n***\n\n\u003cp align=\"center\"\u003e\n    \u003ch3 align=\"center\"\u003eKnow where your people are and coordinate with an interactive map.\u003c/h3\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    In addition to providing a dashboard accessible for wellbeing stakeholders, \u003cstrong\u003eTogether App\u003c/strong\u003e puts every team member on an interactive map. Using the map, members can see who is staying in the same city, meet up, and help each other. \n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    The map also became crucial for coordinating the logistics of humanitarian efforts and connecting members worldwide. Plus, it just feels good to see everybody at the same time, on the same map. \n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/images/map-demo.gif\" alt=\"Organization Map\"\u003e\n\u003c/p\u003e\n\n***\n\n### :zap: \u0026nbsp; What Can Together App Do?\n\n* Automatically request wellbeing check-ins from organization members on a regular basis.\n* Allow members to check in other peers who may not have connectivity if they know their current status.\n* Repeat the previous wellbeing check-in in two taps if conditions haven't changed.\n* Monitor member wellbeing, sending notifications to wellbeing stakeholders (HR, direct managers, etc.) when a member is at risk.\n* Keep track of who has been mobilized. \n* Display check-in data for members in its thorough dashboard.\n* Allow you to control who can see what data in the dashboard.\n* Assist in building reports and queries based on check-in data for assisting members (i.e., \"show me all of the members in Mykolaiv to help me coordinate an evacuation\").\n* Display an interactive map of an organization's members, allowing members to find help, community, and support in their current location.\n* It respects privacy – members can opt-out of being displayed on the map, be exempt from checking in, and **Together App** will *never* save the exact location of any member. Although it uses browser geolocation to make the check-in process fast and effortless, only the coordinates of the member's city are stored in the database.  \n\n***\n\n### :computer: \u0026nbsp; Usage\n\n**Together App** has two key components:\n\n* The **Together App** Slack bot.\n* The **Together App** dashboard.\n\n#### The Slack Bot\n\nAside from automatically requesting check-ins, members can proactively check in using the `/together` slash command and the **Together App** global shortcuts.\n\n#### The Dashboard\n\nUpon deploying your self-hosted version of **Together App**, a dashboard will be available on the domain, where you and other organization members can view members and their check-in data, as well as the interactive map.\n\nActual check-in data is limited to **Together App** administrators (which can be added in the dashboard), while the map is visible to all members of the organization who have access through your SSO or OAuth provider. \n\n***\n\n### :floppy_disk: \u0026nbsp; Deploying Together App \n\nBelow is an in-depth guide on how you can deploy your own instance of **Together App** for your organization. This is intended to take no longer than an hour.\n\n#### Before You Start\n\nThe following guide assumes that your organization already has:\n\n* A Slack workspace.\n* A single sign on (SSO) or OAuth provider (Okta, Google, etc.).\n\nIf you do not, you will need to set these up in order to use **Together App**.\n\nThis guide will walk you through deploying **Together App** on Heroku. However, almost all the steps here are globally true for and easily applied to any hosting.\n\n#### Configure The Environment\n\nBelow is the list of required environment variables. Depending on where you are deploying **Together App**, these may need to be set either in the `.env` file or elsewhere (i.e. the Heroku dashboard).\n\nThis deployment guide will walk you through populating the values of the environment variables and will explain which each is used for.\n\n```bash\nNODE_ENV=\nPORT=\nHOST=\nDATABASE_URL=\nSLACK_TOGETHER_APP_TOKEN=\nSLACK_TOGETHER_APP_SIGNING_SECRET=\nSLACK_MONITORING_CHANNEL_ID=\nSLACK_ORGANIZATION_CHANNEL_ID=\nSLACK_WORKSPACE_ID=\nTOGETHER_ADMINISTRATOR_SLACK_USER_ID=\nGOOGLE_GEOCODING_API_TOKEN=\nGOOGLE_PLACES_API_TOKEN=\nMAPBOX_MAP_TOKEN=\nOKTA_CLIENT_ID=\nOKTA_CLIENT_SECRET=\nOKTA_ISSUER=\nNEXTAUTH_URL=\nNEXTAUTH_SECRET=\nALLOWED_REFERRER_ID=\nJOBS_API_TOKEN=\n```\n\n#### Create a Heroku Application\n\nThe easiest way to get started with **Together App** is to deploy it to Heroku.\n\n* Create a new Heroku application according to [the documentation](https://devcenter.heroku.com/articles/getting-started-with-nodejs).\n* Since **Together App** relies on Slack, due to response time allowances for Slack's events, it is recommended that you purchase a Hobby plan, so that the app does not sleep.\n* Head to the **Settings** tab and, if needed, set up your custom domain (SSL and a Heroku domain are provided for free).\n* There, in the **Settings** tab, click **Reveal Config Vars** to set up the environment. This is where you'll be setting all of the environment variables.\n* Set the `HOST` and `NEXTAUTH_URL` to your custom or Heroku domain.\n* Set the `NODE_ENV` to `production`. \n* Make sure that you *do not* set the `PORT` environment variable, as Heroku application ports are dynamic.\n\n#### Set Up PostgreSQL\n\n**Together App** requires a PostgreSQL database. \n\n* If you are working with Heroku, provision the [Heroku Postgres add-on](https://www.heroku.com/postgres).\n* Once you do that, the `DATABASE_URL` environment variable is set for you.\n* However, if you are not using Heroku Postgres, you will need to set the `DATABASE_URL` environment variable. \n\n#### Create The Slack Application\n\n**Together App** integrates with Slack. It is there that your organization's members will receive check in requests and where member wellbeing stakeholders will receive notifications. The reasoning behind using Slack is that it is the most popular collaboration tool for organizations and is generally a very safe, trusted environment, which is important in light of currently heightened worries of phishing and other cyber attacks.\n\nTo create your Slack application:\n\n* Copy the YML contents below to your clipboard (note the this is also available in the `manifest.yml` file in the [`slack`](https://github.com/MacPaw/together-app/tree/main/slack) folder.\n* Make sure you replace the `{{host}}` variable with the domain on which you'll be hosting **Together App**.\n* Follow the [instructions laid out by Slack](https://api.slack.com/reference/manifests) to install an app via an app manifest.\n* Go to the app's **Basic Information** page, scroll to the bottom, and upload the app's icon. You can find the default **Together App** icon in the [`slack`](https://github.com/MacPaw/together-app/tree/main/slack) folder.\n* Here, on the **Basic Information** page, click **Install to Workspace**.\n* Once it has been installed, copy the **Signing Secret** from the app's **Basic Information** page to the `SLACK_TOGETHER_APP_SIGNING_SECRET`.\n* Go to the **OAuth \u0026 Permissions** page and copy the **Bot User OAuth Token** to the `SLACK_TOGETHER_APP_TOKEN` environment variable.\n\n:warning: Note that **Together App** has been created to work in a single Slack workspace. If your organization is on the Enterprise Grid, you will need to create an instance of **Together App** for each workspace.\n\n```yaml\ndisplay_information:\n  name: Together App\n  description: An app to help us stay together during these tough times.\n  background_color: \"#36373d\"\nfeatures:\n  bot_user:\n    display_name: Together App\n    always_online: true\n  shortcuts:\n    - name: Check In Someone Else\n      type: global\n      callback_id: renderCheckInOtherMemberConfirmation\n      description: Check in another member of your organization with Together App.\n    - name: Check In\n      type: global\n      callback_id: renderCheckInSelfConfirmation\n      description: Check in using Together App.\n    - name: Repeat Last Check In\n      type: global\n      callback_id: renderRepeatCheckInConfirmation\n      description: Repeat your last check in with Together App.\n  slash_commands:\n    - command: /together\n      url: https://{{host}}/api/slack/command\n      description: Call the Together App menu to check in.\n      should_escape: false\noauth_config:\n  scopes:\n    bot:\n      - commands\n      - users:read\n      - users:read.email\n      - chat:write\nsettings:\n  interactivity:\n    is_enabled: true\n    request_url: https://{{host}}/api/slack/action\n  org_deploy_enabled: false\n  socket_mode_enabled: false\n  token_rotation_enabled: false\n```\n\n#### Configure Other Slack-Related Environment Variables\n\nThere is some other information from your Slack workspace that **Together App** needs in order to function.\n\nNamely:\n\n* The **Team ID** of your Slack workspace.\n* The **Channel ID** of an organization-wide channel where check in requests will be sent.\n* The **Channel ID** of a channel where **Together App** can send notifications to member wellbeing stakeholders.\n* The **User ID** of the initial member who will have administrator privileges in **Together App**.\n\nTo get this information:\n\n* Install the [**Slack Developer Tools** application](https://macpaw.slack.com/apps/APXEJ79UY-slack-developer-tools) from the Slack App Directory.\n* Inside of the Slack client, open the [Shortcuts Menu](https://slack.com/help/articles/360057554553-Take-actions-quickly-with-shortcuts-in-Slack), and search for and click **Display IDs**.\n* In the modal that opens, copy the value of **Your Workspace ID** to the `SLACK_WORKSPACE_ID` environment variable.\n* In the same modal, find the member who will have initial administrator privileges and copy their ID to the `TOGETHER_ADMINISTRATOR_SLACK_USER_ID` environment variable.\n* In the same modal, find the channel to which **Together App** should send organizational-wide check in requests and copy the ID to the `SLACK_ORGANIZATION_CHANNEL_ID` environment variable.\n* Do the same to find the channel ID of the channel to which **Together App** can send notifications to member wellbeing stakeholders and copy the ID to the `SLACK_MONITORING_CHANNEL_ID` environment variable.\n\n:warning: Make sure that **Together App** has been added to those channels.\n\n#### Generate Google API Tokens\n\n**Together App** uses two Google APIs to make the check in process as easy as possible – the [**Geocoding API**](https://developers.google.com/maps/documentation/geocoding/overview) and the [**Places API**](https://developers.google.com/maps/documentation/places/web-service/overview). You need to create API tokens for both.\n\nFollow these steps:\n\n* In order for **Together App** to work, you need to make sure that billing has been turned on and a payment method has been added. You can learn more about in [this documentation](https://cloud.google.com/billing/docs/how-to/payment-methods).\n* Create an API key for the Geocoding API:\n    * Create an API token.\n    * In the **API restrictions** section, restrict the token to only the **Geocoding API**.\n    * If you decide to further restrict the token using the **Application Restrictions**, note that this token is used on the back end, so using the **HTTP Referrer** restriction is not applicable. If you wish to restrict it for security reasons and you have a static IP address, you can restrict it by IP. \n* Copy the Geocoding API key to the `GOOGLE_GEOCODING_API_TOKEN` environment variable.  \n* Create an API key for the Places API:\n    * Create an API token.\n    * In the **API restrictions** section, restrict the token to include both the **Places API** and the **Maps JavaScript API**.\n    * Since this API key is exposed to the client, you need to restrict it by **HTTP Referrer** in the **Application Restrictions** section.\n* Copy the Places API key to the `GOOGLE_PLACES_API_TOKEN` environment variable.\n\n:warning: If you run into issues with detecting location while checking in, please go through the outlined restrictions above and double check that your tokens comply.\n\n#### Generate a MapBox Public Key\n\n**Together App** uses MapBox to render the interactive map of an organization's members. \n\nFollow these steps:\n\n* Create an access token (that starts with `pk`) for MapBox according to their [documentation](https://docs.mapbox.com/accounts/guides/tokens/#default-public-access-token).\n* Since the access token is exposed to the client, you'll want to [restrict it by URL](https://docs.mapbox.com/accounts/guides/tokens/#url-restrictions).\n* Copy the MapBox access token to the `MAPBOX_MAP_TOKEN` environment variable.\n\n#### Setting Up SSO or OAuth\n\nOut of the box, **Together App** supports signing in with Okta as the main SSO provider. However, the app is built with [NextJS](https://nextjs.org), and the package [NextAuth](https://next-auth.js.org) supports multiple SSO and OAuth providers. \n\nIn this section are the instructions for setting up Okta authentication. View the section on customizations to learn how to set up a custom provider.\n\nTo set up Okta authentication:\n\n* Create [an OIDC custom app integration](https://help.okta.com/en/prod/Content/Topics/Apps/Apps_App_Integration_Wizard_OIDC.htm) for **Together App**.\n* Set the redirect URL for the app to be `https://{{host}}/api/auth/callback/okta`.\n* Find your [app's credentials](https://developer.okta.com/docs/guides/find-your-app-credentials/main/#find-your-app-integration-credentials), and copy in the **Client ID** to the `OKTA_CLIENT_ID` environment variable.\n* Copy the **Client Secret** to the `OKTA_CLIENT_SECRET` environment variable.   \n* Copy your organization's [Okta domain](https://developer.okta.com/docs/guides/find-your-domain/) to the `OKTA_ISSUER` environment variable.\n* Provision access to your organization's members as needed. Note that priveleges are handled inside of the **Together App** and managed in the dashboard – not through Okta groups.\n \n#### Setting Secrets\n\nThere are additional secrets that are needed for **Together App** to function. Each one is a random string. You can use key generators to create them:\n\n* `NEXTAUTH_SECRET` – a random string used to hash tokens, sign/encrypt cookies and generate cryptographic keys.\n* `ALLOWED_REFERRER_ID` – random string used to verify whether or not to render the check in page when redirected from Slack (other protection methods are in place, too).\n* `JOBS_API_TOKEN` – a random string used to verify whether or not a request to the `https://{{host}}/api/jobs/*` endpoints are valid. These endpoints are used for scheduled jobs and do not return any data.\n\n#### Turning On Heroku Auto Deploys \n\nNow that the environment has been populated, you're ready to deploy. \n\nFirst, we need to deploy the code to the Heroku app:\n\n* Head over to the **Deploy** tab of your Heroku app.\n* Configure GitHub integration.\n* Select the forked repository in your GitHub account.\n* Click **Deploy** branch.\n* At this point, you will also want to install the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli).\n\nTo view the build and deploy progress, open your **Terminal** and type in:\n\n```bash\nheroku logs -t -a {{heroku-app-name}}\n```\n\n#### Running Migrations\n\nBefore you can use **Together App**, you'll need to initiate the database scheme and seed data. \n\nFirst, open the command line within your Heroku application:\n\n```bash\nheroku run bash -a {{heroku-app-name}}\n```\n\nThen, to update the schema, run the following command inside the application:\n\n```bash\nnpm run migrate:production\n```\n\nThen, you need to seed the data. Please note that your server needs to be up and running before running this command: \n\n```bash\nnpm run initialize\n```\n\nThis command syncs the database with your Slack workspace and provides administrator permissions to the member whose Slack ID was set in the `TOGETHER_ADMINISTRATOR_SLACK_USER_ID` environment variable.\n\n### Setting Up Scheduled Jobs\n\n**Together App** relies on scheduled jobs for syncing users with your Slack workspace, sending check in requests, reminding members to check in, and notifying stakeholders about members who haven't checked in recently. \n\nThese are invoked calling the following scripts:\n\n* `npm run sync` – this syncs the **Together App** member base with your Slack workspace, importing newly added members and updating those with status changes (deleted, restricted, etc.).\n* `npm run request` – this sends a check-in request message to the organization-wide channel and as a direct message to each member.\n* `npm run remind` – this sends a check in reminder for members who haven't checked within a certain number of hours. This defaults to 24 hours, but is configurable.\n* `npm run notify` – this sends a notification to the monitoring channel for members who haven't checked in within a certain number of hours. This defaults to 24 hours, but is configurable. \n\nTo set these up on Heroku:\n\n* For the app you created above, provision the free [Heroku Scheduler add-on](https://devcenter.heroku.com/articles/scheduler).\n* Navigate to your app's **Overview** tab, and go to the Heroku Scheduler settings.\n* Click **Add Job**.\n* Choose a time interval and paste in the command from above.\n\nNote that it is up to you how often and when to call these scripts. You might want to just request check-ins once per week. Aside from that, there are other customizations available, too.\n\nAn example algorithm for daily check-ins might be:\n\n* 8:00 AM `npm run sync`\n* 11:00 AM `npm run request`\n* 14:00 PM `npm run remind`\n* 16:00 PM `npm run notify`\n* 11:00 PM `npm run sync`\n\nThis will:\n\n* Keep your member database up to date.\n* Request that members check in towards the start of the business day. \n* When members don't check in within three hours, remind them.\n* Two hours later, notify stakeholders of members who are potentially off the grid or at risk.\n\nBut it's completely up to you and your organization. You will, however, want to call the `npm run sync` script at least once on a daily basis, at a time when most of your members are not active.\n\n### Deploying Locally\n\n**Together App** comes with a `docker-compose.yml` file to make it easy to deploy locally. Overall, most of the steps above apply to deploying locally. There are just a few changes needed to do so.\n\n* Make sure that Docker is installed.\n* Set up the environment variables in a `.env` file in the project's root.\n* Set the `NODE_ENV` environment variable to `local`.\n* Set the `DATABASE_URL` environment variable to `postgres://admin:admin@db:5432/together`.\n* Set the `PORT` environment variable to `3000`.\n* You'll need to use NGROK to proxy requests to the local host, since Slack requests are proxied from the client to the server. You can read more about this [here](https://slack.dev/node-slack-sdk/tutorials/local-development).\n* Set the `HOST` and `NEXTAUTH_URL` environment variables to the NGROK host.\n* You'll need to create a second Slack application, changing the host to the NGROK host in the provided URLs, and changing the name of the slash command.\n\nOnce you've set up the environment, open the **Terminal** and run the following commands.\n\nTo install dependencies: \n\n```bash\nnpm install\n```\n\nTo build the project: \n\n```bash\nnpm run build\n```\n\nTo start up the server:\n\n```bash\ndocker-compose up\n```\n\nTo sync the database schema, you first need to navigate to the container:\n\n```bash\ndocker exec -it together sh\n```\n\nThen run the migrations:\n\n```bash\nnpm run migrate:local\n```\n\nThen seed data. Please note that your server needs to be up and running before running this command:\n\n```bash\nnpm run initialize\n```\n\nThe same goes with scheduled tasks – they need to run inside of the Docker container.\n\n***\n\n### :wrench: \u0026nbsp; Custom App Configuration\n\n**Together App** has a few ways to customize the business logic for your instance. There are configuration files with type definitions location in `config/custom` that you can directly edit – the content of these files will not change with further releases.\n\n#### Using a Custom SSO or OAuth Provider\n\nAuthentication is implemented using [NextAuth](https://next-auth.js.org), so **Together App** supports any of the authentication providers supported by [NextAuth](https://next-auth.js.org).\n\nTo use a provider other than the default Okta, you can add it to `config/custom/auth-provider-config.ts`:\n\n```typescript\nimport type { Nullable, AuthProviderConfig } from '../../types';\nimport SlackProvider from 'next-auth/providers/slack';\n\nconst SLACK_CLIENT_ID = process.env.SLACK_CLIENT_ID!;\nconst SLACK_CLIENT_SECRET = process.env.SLACK_CLIENT_SECRET!;\n\nexport const authProviderConfig: Nullable\u003cAuthProviderConfig\u003e = {\n  provider: SlackProvider({\n    clientId: SLACK_CLIENT_ID,\n    clientSecret: SLACK_CLIENT_SECRET,\n  }),\n  type: 'slack',\n};\n```\n\n#### Slack Member Filtration Rules\n\nBy default, members that are either restricted or ultra-restricted in your Slack workspace will not be included in check-in requests, the dashboard, or the map. They won't be able to check in or access data.\n\nThis can be configured by modifying `config/custom/filter-slack-member-rule.ts`:\n\n```typescript\nimport type { Nullable, FilterSlackMemberRule } from '../../types';\n\nexport const filterSlackMemberRule: Nullable\u003cFilterSlackMemberRule\u003e = {\n  filterRestricted: false, // Restricted members will also be in Together App\n  filterUltraRestricted: true,\n};\n```\n\n#### Check In Request Configuration\n\nBy default, when the scheduled job `npm run request` is invoked, a request is sent to both the organization-wide channel and to each member as a direct message, from the Slack app.\n\nThis can be configured by modifying `config/custom/check-in-request-rule.ts`:\n\n```typescript\nimport type { Nullable, CheckInRequestRule } from '../../types';\n\nexport const checkInRequestRule: Nullable\u003cCheckInRequestRule\u003e = {\n  requestCheckInDirectMessage: false, // The message will not be sent as a direct message\n  requestCheckInOrganizationChannel: true,\n};\n```\n\n#### Notification Configuration\n\nBy default, when running the `npm run notify` scheduled job, **Together App** notifies you in the monitoring channel about members that haven't checked in within the 24 hours previous to the job being invoked.\n\nThis can be configured by modifying `config/custom/notify-if-not-checked-in-within-rule.ts`:\n\n```typescript\nimport type { Nullable, NotifyOfLateCheckInRule } from '../../types';\n\nexport const notifyIfNotCheckedInWithinRule: Nullable\u003cNotifyOfLateCheckInRule\u003e = { \n  hours: 12, // Will notify stakeholders of members not checked in within last 12 hours\n};\n```\n\n#### Reminder Configuration\n\nBy default, when running the `npm run remind` scheduled job, **Together App** queries members who have not checked in within the last 24 hours and sends each one a reminder, requesting a check in.\n\nThis can be configured by modifying `config/custom/remind-if-not-checked-in-within-rule.ts`:\n\n```typescript\nimport type { Nullable, RemindMemberOfLateCheckInRule } from '../../types';\n\nexport const remindIfNotCheckedInWithinRule: Nullable\u003cRemindMemberOfLateCheckInRule\u003e = {\n  hours: 48, // Will remind members not checked in within last 48 hours to check in\n};\n```\n\n#### Member At Risk Criteria\n\nEvery time a member checks in **Together App** determines whether or not the member is at risk or in need of assistance, and sends a notification to the monitoring channel. \n\nBy default, it considers a member at risk if they meet one of the following criteria:\n\n* Have checked themselves in as not being in a safe place\n* Have been mobilized\n* Have not checked in within the past 24 hours\n\nThis can be configured by declaring a function in `config/custom/member-is-at-risk-rule.ts`:\n\n```typescript\nimport type { Nullable, MemberIsAtRiskRule } from '../../types';\nimport type { Member } from '../../entities';\n\nexport const memberIsAtRiskRule: Nullable\u003cMemberIsAtRiskRule\u003e = (member: Member): boolean =\u003e {\n  const isSafe = member.checkIn \u0026\u0026 member.checkIn.isSafe;\n  const isNotMobilized = !member.isMobilized; \n\n  return !Boolean(isSafe \u0026\u0026 isNotMobilized); // No check in within 24 hours is no longer considered at risk\n};\n```\n\n#### Custom Logger\n\nBy default, **Together App** uses `console.log()` for errors. You can also declare your own logger that complies to the provided `Logger` interface:\n\n```typescript\nimport type { Logger } from '@slack/logger';\nimport type { Nullable } from '../../types';\nimport SomeLogger from '../../logger';\n\nexport const logger: Nullable\u003cLogger\u003e = new SomeLogger();\n```\n\n***\n\n### :ukraine: \u0026nbsp; Authors\n\n\u003cimg src=\"https://github.com/raycharius.png\" alt=\"@raycharius\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e Ray East ([@raycharius](https://github.com/raycharius))\n\n\u003cimg src=\"https://github.com/hraboviyvadim.png\" alt=\"@hraboviyvadim\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e Vadym Grabovyi ([@hraboviyvadim](https://github.com/hraboviyvadim))\n\n\u003cimg src=\"https://github.com/BZahorodnii.png\" alt=\"@BZahorodnii\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e Boris Zagorodniy ([@BZahorodnii](https://github.com/BZahorodnii))\n \n\u003cimg src=\"https://github.com/korywka.png\" alt=\"@korywka\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e Taras Neporozhniy ([@korywka](https://github.com/korywka))\n\n\u003cimg src=\"https://cdn.dribbble.com/users/683635/avatars/normal/ee2c7c826bfe244b573d145376fe0b5a.png?1510328842\" alt=\"@ft502\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e Alexey Chernyshov ([@ft502](https://dribbble.com/ft502))\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmacpaw%2Ftogether-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmacpaw%2Ftogether-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmacpaw%2Ftogether-app/lists"}