This repo specifies the web backend and frontend for the Orcasound app that plays a live audio stream through the user's browser of choice. The backend is an Elixir app using the Phoenix framework. The frontend is built in Next.js.
Please check out the CONTRIBUTING doc for tips on making a successful contribution, as well as learning resources!
- Load the Docker Configuration:
docker compose up - Wait for containers to start up
- Navigate to localhost:3000 to view the website
- Navigate to localhost:4000 to access the API server
Note This assumes you have installed docker. If you're running Windows, ensure Docker Desktop is installed and running.
Docker is the quickest way to get the project up and running, especially if you haven't already set up Erlang/Elixir/Node. The only requirement is that you have docker installed on your machine.
Once you clone the repository, you can just run docker compose in the root directory:
docker compose upThis will build an image locally with all the dependencies you need. It will also pull a pre-built image from Docker Hub for the database, automatically configure everything, and both the Phoenix and Next.js servers. The orcasite page will be accessible at http://localhost:3000 as soon as the web container finishes starting up.
The quick start setup is great for getting the project up and running, but if you want to do development, you'll want to be able to modify the source code without re-building an entire Docker image.
This project comes with a devcontainer.json configuration which can be used with VS Code. This takes care of all the docker compose stuff in the background so you don't have to worry about it. When you open the project in VS Code, it should prompt you to start it in a dev container (assuming you've installed docker and the dev containers extension). Once the dev container starts, you can open a new terminal window in VS Code to run commands. See the commands below for how to get everything started.
Tip For a better VS Code experience, copy the example settings file:
cp .vscode/settings.example.json .vscode/settings.jsonThis provides recommended editor settings for this project. You can also see & install recommended extensions by opening the command palette (Cmd+Shift+P) and selecting "Extensions: Show Recommended Extensions".
Once you have your environment setup via one of the options above, you can start the project. You'll need to run both the Phoenix server and the Next.js server.
In a new terminal session, from the root directory:
> cd server/
> mix deps.get
> mix ecto.setup
> iex -S mix phx.serverNote For future runs, you can skip running the
mixcommands and just start the server withiex -S mix phx.server
The server should soon be available at http://localhost:4000.
Open another terminal session and run these commands to start Next.js:
> cd ui/
> npm i
> npm run devOnce everything finishes starting up, you'll be able to access the UI at http://localhost:3000.
The development environment automatically seeds the database with real data from the production server, including detections, audio images, and other records. This happens via background jobs that sync data every 1 minute.
Note: Seeded resources will only work with feeds that match production (i.e., with the same
idandslug). Clear your data withmix ecto.dropand re-initialize it withmix ecto.setupto get the latest data from the production servers.
Running mix run priv/repo/seeds.exs will seed all production data from the last hour, and the last 100 bouts, audio images, and detections. This script is included when mix ecto.setup is run.
Three environment variables control this behavior:
ENABLE_SEED_FROM_PROD- Enables the seeding feature (default:truein dev)AUTO_UPDATE_SEEDED_RECORDS- Automatically syncs new data every 1 minute (default:truein dev)AUTO_DELETE_SEEDED_RECORDS- Automatically removes all data hourly if the data is older than 7 days, excluding feeds (default:truein dev).
Note If you pull data older than 7 days and the auto-delete setting is turned on, that data will be deleted on the hour. Turn this setting off if you'd like to work with data older than 7 days.
To disable automatic seeding, set these to false in your environment (e.g. in docker-compose.dev.yml) or in server/config/dev.secret.exs:
config :orcasite, enable_seed_from_prod: falseYou can also manually trigger seeding from the /seed page in the UI. This lets you choose whatever resources you want to pull and within a time range of your choosing.
The new version (v3) is currently under development, rapidly changing, and has no tests yet.
For the moment, this app is running in a Heroku instance with mix phx.server. A single prebuilt artifact is promoted through the pipeline — dev to staging to production — rather than each environment building its own.
Because one artifact serves every environment, anything captured while it is built describes the environment it was built in rather than the one serving the request. Environment-specific values must therefore be read at runtime:
- Server.
config/runtime.exsandconfig/prod.exsare evaluated at boot, so they see each app's own config vars. Values reached throughApplication.compile_env/2are the exception: they are captured when the artifact is compiled, so they cannot express per-app configuration, and Elixir validates them against the runtime value at startup. Avoidcompile_envfor anything that differs between environments. The cache adapter incache.exis one, keyed off whetherREDIS_URLis set. - UI. Next inlines
NEXT_PUBLIC_*into the client bundle at build time, soui/.env.productiondeliberately defines none. The GraphQL and socket endpoints resolve same-origin in the browser (client.ts,useSocket.ts); remaining per-environment values are injected per request byruntime-config.tsand read throughruntimeConfig.ts. - Pages.
getStaticPropsandgetStaticPathsrun during the build, so pages showing environment-specific data must not rely on them.listen/index.tsxusesgetServerSidePropsand renders on every request;listen/[feed].tsxprerenders no paths, generating each page on first request and revalidating it after 60 seconds.
Local development builds and runs in a single environment, where inlining is harmless, so ui/.env.development sets NEXT_PUBLIC_* normally.
Run bin/smoke against an app after promoting to it:
bin/smoke https://live.orcasound.netIt exits non-zero if anything fails. Most checks are self-configuring: they ask the app's own API what it should be showing and confirm the pages agree, so the same command works against any environment. An app serving another environment's data disagrees with its own API, which is the failure this deployment model invites and the reason these exist. The feed comparison runs in both directions, because the failure that prompted this showed up as a page listing extra feeds — production serving a page built from development's list.
Two checks carry knowledge the app cannot supply, and both take an override:
EXPECT_SEED_PAGE=1where seeding is deliberately enabled; elsewhere/seedis expected to be absent.ABSENT_SLUGSlists feeds that exist only in development, so elsewhere they must not resolve. A nonsense slug cannot cover this: a slug that never existed anywhere was never prerendered either.
Nothing runs these automatically. The health-check job in
heroku.yaml fires on GitHub deployment
events, which slug promotions no longer produce.
heroku run -a <app_name> FEED_STREAM_QUEUE_URL="" REDIS_URL="" POOL_SIZE=1 iex -- -S mixThe POOL_SIZE config var is necessary due to the current Postgres db having 20 connections. You can read more about it here.
Application logs are collected by Mezmo.
Orcasite uses MJML for email templating. There are a few online MJML renderers, including: mjml.io and grapes.js
An API is available using the JSON API spec. For access to available endpoints, navigate to the server's /api/json/swaggerui or /api/json/redoc path for documentation and examples. As an example, you may access the full list of feeds with:
curl -s https://beta.orcasound.net/api/json/feeds \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json"