{"id":13911264,"url":"https://github.com/apricote/Listory","last_synced_at":"2025-07-18T11:30:38.587Z","repository":{"id":37000517,"uuid":"236231317","full_name":"apricote/Listory","owner":"apricote","description":"Track your Spotify listens","archived":false,"fork":false,"pushed_at":"2025-07-04T12:32:47.000Z","size":54563,"stargazers_count":109,"open_issues_count":13,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-15T06:07:39.516Z","etag":null,"topics":["analytics","nestjs","nodejs","quantified-self","react","self-hosted","spotify","typescript"],"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/apricote.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-01-25T21:19:02.000Z","updated_at":"2025-05-15T11:34:22.000Z","dependencies_parsed_at":"2024-03-25T21:35:03.287Z","dependency_job_id":"1f154dc9-bee3-4a1f-ae4c-f129c3321502","html_url":"https://github.com/apricote/Listory","commit_stats":{"total_commits":2005,"total_committers":6,"mean_commits":334.1666666666667,"dds":0.3291770573566085,"last_synced_commit":"eee615bec05833bc995c1a33b582a8ff3a288654"},"previous_names":[],"tags_count":75,"template":false,"template_full_name":null,"purl":"pkg:github/apricote/Listory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apricote%2FListory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apricote%2FListory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apricote%2FListory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apricote%2FListory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apricote","download_url":"https://codeload.github.com/apricote/Listory/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apricote%2FListory/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265750881,"owners_count":23822678,"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":["analytics","nestjs","nodejs","quantified-self","react","self-hosted","spotify","typescript"],"created_at":"2024-08-07T00:02:04.107Z","updated_at":"2025-07-18T11:30:38.246Z","avatar_url":"https://github.com/apricote.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cp\u003e\n  \u003ch1 align=\"center\"\u003e\n      Listory\n    \u003cimg src=\"assets/logo.svg\" width=\"32px\" align=\"top\" /\u003e\n  \u003c/h1\u003e\n\u003c/p\u003e\n  \n\u003cp align=\"center\"\u003e\n  \u003ch4 align=\"center\"\u003eLogin with Spotify and Listory will save all tracks you listen to.\u003c/h4\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Latest Release\" src=\"https://img.shields.io/github/v/release/apricote/listory?style=flat-square\" /\u003e\n  \u003cimg alt=\"GitHub branch checks state\" src=\"https://img.shields.io/github/checks-status/apricote/listory/main?style=flat-square\"\u003e\n  \u003cimg alt=\"Codecov\" src=\"https://img.shields.io/codecov/c/github/apricote/listory?style=flat-square\"\u003e\n  \u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/apricote/listory?style=flat-square\"\u003e\n\u003c/p\u003e\n\n\u003ctable align=\"center\"\u003e\n  \u003ctr\u003e\n      \u003ctd colspan=\"2\" align=\"center\"\u003e\n      \u003cimg src=\"docs/listens-report.png\" /\u003e\n      \u003cem\u003eThe listens report shows how many songs you have listened to during the last month (and all months prior to that).\u003c/em\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\" width=\"50%\"\u003e\n      \u003cimg src=\"docs/recent-listens.png\"/\u003e\n      \u003cem\u003eWant to know which song you heard on your drive to work last tuesday? Now you can!\u003c/em\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" width=\"50%\"\u003e\n      \u003cimg src=\"docs/top-genres.png\" /\u003e\n      \u003cem\u003eFind out what genres (or artists, albums, songs) you listen to the most!\u003c/em\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Installation\n\n### Creating the Spotify App\n\nTo connect to the Spotify API, you need to create a new App on the [Spotify\nDeveloper Dashboard](https://developer.spotify.com/dashboard/applications).\n\nOnce the App is created, open the App Overview and click on the \"Edit Settings\"\nbutton. A new modal should open. In this modal, you need to add a Redirect URI,\nso that the login with Spotify actually works. This URL depends on where you\nwant to host your Listory installation, see also `APP_URL` under\n[Configuration / Application](#application). The Redirect URI should be\n`$APP_URL/api/v1/auth/spotify/callback`. For the local example URL which is used\nin development, this would be `http://localhost:3000/api/v1/auth/spotify/callback`.\nIf you have your own domain where you want to host Listory, this is probably\nsomething like `https://listory.your-name.com/api/v1/auth/spotify/callback`.\n\nYou can add multiple Redirect URIs and all will work.\n\nKeep the tab open, as you will need the _Client ID_ and _Client Secret_ in the\nnext step.\n\n### Deployment\n\nListory currently supports two deployment mechanisms: _docker compose_ and\n_Kubernetes Helm Chart_.\n\n#### docker compose\n\nThere are two `docker compose` files in the repository, for a production\ndeployment, you want to use [`docker-compose.prod.yml`](./docker-compose.prod.yml).\n\nYou can copy this file to your server or whereever you want to run Listory. You\nwill also need to copy the `.env.sample` file next to the\n`docker-compose.prod.yml` file and rename it to `.env`.\n\nOpen the `.env` file in an editor and put in the Spotify App _Client ID_ and\n_Client Secret_.\n\nNow you can configure Listory how you like by changing the `environment` of the\n`listory` service in the docker compose file, or by adding new values in the\n`.env` file. For a list of all available options, see section\n[Configuration](#configuration).\n\nIf you deploy Listory on the public internet, I recommend you to add a\nreverse proxy like [Traefik][traefik], which you can configure to\n[automatically add TLS certificates][traefik-tls] (putting the S into HTTPS).\n\n[traefik]: https://doc.traefik.io/traefik/getting-started/quick-start/\n[traefik-tls]: https://doc.traefik.io/traefik/user-guides/docker-compose/acme-tls/\n\nOnce you have set everything up, you can run this command to start Listory:\n\n```\ndocker compose up --daemon --file docker-compose.prod.yml\n```\n\nThis will start Listory in the background. Checkout the [docker compose documentation][docker-compose],\nto learn how you can work with the containers, for example to restart them or to\nread the logs.\n\n[docker-compose]: https://docs.docker.com/compose/reference/\n\n### Helm Chart\n\nWe publish a Kubernetes Helm Chart that installs Listory into a Kubernetes cluster.\n\nI have not yet setup publishing to an actual Chart Registry, so if you would like\nto use the chart, create an issue and I will set this up properly.\n\nYou can find the source code for the Helm Chart under `[charts/listory](./charts/listory/)`.\n\n### Configuration\n\nAll configuration must be set as environment variables. Default values are added in **bold**, values that are required are marked with _Required_.\n\n#### Application\n\n- `PORT`: **3000**: Port the webserver will listen on.\n- `APP_URL`: **http://localhost:3000**: Public URL of the Application, is used to generate Links.\n\n#### Authentication\n\n- `JWT_SECRET`: _Required_, used to sign the JWTs.\n- `JWT_ALGORITHM`: **HS256**: Algorithm used to sign the JWTs. One of `HS256`, `HS384`, `HS512`\n- `JWT_EXPIRATION_TIME`: **1d**: Lifetime of signed JWTs. Accepts strings like `1d`, `2h`, `15m`.\n\n#### Spotify\n\n- `SPOTIFY_CLIENT_ID`: _Required_, Spotify App Client ID\n- `SPOTIFY_CLIENT_SECRET`: _Required_, Spotify App Client Secret\n- `SPOTIFY_FETCH_INTERVAL_SEC`: **60**: Interval for fetching recently listened tracks from Spotify.\n- `SPOTIFY_UPDATE_INTERVAL_SEC`: **60**: Interval for updating previously imported music library entities (artist, album, track). Raise this number if you often hit the Spotify API Ratelimit.\n- `SPOTIFY_WEB_API_URL`: **https://api.spotify.com/**: Spotify WEB API Endpoint.\n- `SPOTIFY_AUTH_API_URL`: **https://accounts.spotify.com/**: Spotify Authentication API Endpoint.\n- `SPOTIFY_USER_FILTER`: **\"\"**: If set, only allow Spotify users with these ids to access the app. If empty, allow all users to access the app. Seperate ids with `,` eg.: `231421323123,other_id`.\n\n#### Database\n\n- `DB_HOST`: _Required_, Database host\n- `DB_USERNAME`: _Required_, Database username\n- `DB_PASSWORD`: _Required_, Database password\n- `DB_DATABASE`: _Required_, Database database\n- `DB_POOL_MAX`: **50**, max concurrent database connections\n\n#### Sentry\n\nYou can use Sentry to automatically detect and report any exceptions thrown.\n\n- `SENTRY_ENABLED`: **false**, Set to `true` to enable Sentry.\n- `SENTRY_DSN`: _Required_, but only if `SENTRY_ENABLED` is `true`. The [DSN](https://docs.sentry.io/product/sentry-basics/dsn-explainer/) for your Sentry project.\n\n#### OpenTelemetry\n\nWe use OpenTelemetry to provide observability into Listory API.\n\nThe metrics will be exposed on a seperate port at `:9464/metrics`. Make sure that this endpoint is not publicly available in your deployment.\n\nTraces will be sent to the specified endpoint.\n\nTo use observability tools locally, check out `docker-compose` setup in `observability/`.\n\n- `OTEL_METRICS_ENABLED`: **false**, Set to `true` to activate metrics.\n- `OTEL_TRACES_ENABLED`: **false**, Set to `true` to activate traces.\n- `OTEL_EXPORTER_OTLP_ENDPOINT`: _Required_, but only if `OTEL_TRACES_ENABLED` is `true`. The endpoint that traces are sent to, see [OpenTelemetry docs](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-http#configuration-options-as-environment-variables)\n- `OTEL_EXPORTER_PROMETHEUS_PORT`: **9464**, Set to configure non-standard port for Prometheus metrics\n\n## Development\n\n### Configure Spotify API Access\n\nCopy the file `.env.sample` to `.env` and add your Spotify API Key.\n\n### Starting the application\n\nWe use `docker compose` to provide a full local development environment.\n\n```bash\n$ docker compose up\n```\n\nYou can now access the frontend at `http://localhost:3000` and the API at `http://localhost:3000/api`.\n\nFrontend and API will automatically reload on any code changes.\n\n### REPL Console\n\nYou can start the REPL console by starting the normal environment, and then running:\n\n```bash\n$ docker compose run console\n```\n\n### Observability\n\nIf you want to start the observability suite (Metrics \u0026 Tracing) locally, you can use the `observability` docker compose profile:\n\n```bash\n$ docker compose --profile observability up\n```\n\nGrafana is then available at `http://localhost:2345` and all sources are preconfigured.\n\n## Test\n\n```bash\n# unit tests\n$ npm run test\n\n# e2e tests\n$ npm run test:e2e\n\n# test coverage\n$ npm run test:cov\n```\n\n## License\n\nListory is [MIT licensed](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapricote%2FListory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapricote%2FListory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapricote%2FListory/lists"}