{"id":16446100,"url":"https://github.com/michaldziuba03/purly","last_synced_at":"2026-03-06T08:32:46.047Z","repository":{"id":144524411,"uuid":"411784319","full_name":"michaldziuba03/purly","owner":"michaldziuba03","description":" SaaS platform for managing your brand links. Shorten links, generate QR codes, track traffic and more 🔗 📊","archived":false,"fork":false,"pushed_at":"2025-07-27T23:24:35.000Z","size":2626,"stargazers_count":7,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-01T20:55:18.285Z","etag":null,"topics":["bitly","bull","nodejs","postgresql","queue","redis","s3","saas","shorten-urls","shortener","stripe","tinyurl","typescript","url-shortener"],"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/michaldziuba03.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-09-29T18:17:36.000Z","updated_at":"2024-09-28T13:14:06.000Z","dependencies_parsed_at":"2025-08-01T20:48:05.584Z","dependency_job_id":null,"html_url":"https://github.com/michaldziuba03/purly","commit_stats":null,"previous_names":["michaldziuba03/purly"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/michaldziuba03/purly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaldziuba03%2Fpurly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaldziuba03%2Fpurly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaldziuba03%2Fpurly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaldziuba03%2Fpurly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michaldziuba03","download_url":"https://codeload.github.com/michaldziuba03/purly/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaldziuba03%2Fpurly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30167962,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T07:56:45.623Z","status":"ssl_error","status_checked_at":"2026-03-06T07:55:55.621Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["bitly","bull","nodejs","postgresql","queue","redis","s3","saas","shorten-urls","shortener","stripe","tinyurl","typescript","url-shortener"],"created_at":"2024-10-11T09:46:25.201Z","updated_at":"2026-03-06T08:32:46.028Z","avatar_url":"https://github.com/michaldziuba03.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg width=\"1024px\" src=\"https://github.com/michaldziuba03/purly/assets/43048524/97c35411-8126-4157-afbf-ac445345e869\" /\u003e\n\n# purly ✂\n\nSaaS platform for managing your brand links. Under active development. Primary goal of this project is to create sample that **does more** than just simple CRUD.\n\n\u003e Frontend is still in very early stage of development. It lacks proper error handling and will receive major upgrade when core features will be done.\n\n![links-page](https://github.com/michaldziuba03/purly/assets/43048524/f93af5b3-e843-4f91-a561-e1d72a277cc8)\n\n![team-page](https://github.com/michaldziuba03/purly/assets/43048524/62af6087-d89d-4f1f-9437-6312b694db70)\n\n![qr-code-preview](https://github.com/michaldziuba03/purly/assets/43048524/c325bf99-8777-499f-9df7-ff6800566ebd)\n\n\u003e Currently only downloading as PNG is possible. In the future SVG and JPEG will be added.\n\n\u003cimg width=\"290px\" src=\"https://github.com/michaldziuba03/purly/assets/43048524/af3b62c4-0b9e-4308-8147-617095ab7d35\" /\u003e\n\u003cimg width=\"290px\" src=\"https://github.com/michaldziuba03/purly/assets/43048524/e2193f3f-3e51-418c-85f7-de2bbb224e58\" /\u003e\n\u003cimg width=\"290px\" src=\"https://github.com/michaldziuba03/purly/assets/43048524/41cf8df1-24ac-4caf-a467-5054cbd09041\" /\u003e\n\n\u003e Account settings interactions example\n\n![launchpad-links](https://github.com/michaldziuba03/purly/assets/43048524/61b98547-5eaf-4379-9db3-bb859d99a589)\n\n![launchpad-appearance](https://github.com/michaldziuba03/purly/assets/43048524/aa630bab-eae2-4bc8-ba03-227c49cf8664)\n\n\u003e Link in Bio feature. More features to come after major refactor.\n\n## Tech stack\n\n- Node.js\n- NestJS\n- PostgreSQL ([Drizzle ORM](https://orm.drizzle.team/))\n- Redis ([ioredis](https://github.com/redis/ioredis))\n- Next.js\n- React\n- Docker\n- Zookeeper ([node-zookeeper](https://github.com/yfinkelstein/node-zookeeper))\n- Clickhouse ([clickhouse-js](https://github.com/ClickHouse/clickhouse-js))\n- AWS S3 ([MinIO](https://min.io/))\n\n## Advanced concepts used in app\n\n- [x] Asynchronous messaging (with BullMQ)\n- [ ] Caching\n- [ ] Rate-limiting\n- [x] Error monitoring (with Sentry)\n\n## Features\n\n- [ ] Email Verification\n- [x] Social Authentication (Google, GitHub)\n- [x] Email \u0026 Password Authentication\n- [x] Reset Password flow\n- [x] Google Recaptcha support\n- [x] Paid monthly subscriptions (Stripe)\n- [x] URL shortening\n- [x] QR Codes generation\n- [x] Reporting malicious URLs\n- [x] UTM Generator\n- [x] Mobile Redirects\n- [x] Link Redirect Expiration\n- [ ] Statistics for shortened URLs\n- [x] Health checks\n- [x] Link in bio\n- [ ] Custom domains\n- [x] Workspaces with members management\n\n### Todo\n\n- write e2e tests (currently partially written for user, workspace and auth modules)\n- create CI pipeline\n\n## Development\n\nProject uses [Nx workspaces](https://nx.dev/) for building and handling monorepo structure.\n\n### Run databases and services\n\n```sh\ncd docker\ndocker compose up\n```\n\n### Run setup scripts\n\n```sh\n# will install depedencies and create .env file\nnpm run setup\n# run migrations (will run migrations against database defined in env.POSTGRES_URI)\nnpm run db:migrate\n# run migrations for stats database (definied CLICKHOUSE_ env variables)\nnpm run stats:migrate\n```\n\n### Configure S3 with MiniO (optional)\n\nPurly uses [MinIO](https://min.io/) to simulate AWS S3 for local development. We suggest to use AWS CLI for initial configuration.\n\nRead about S3 local setup in `/tools/s3-local/README.md`\n\n### Run applications (api, worker and frontend web app)\n\n```sh\nnpm run serve\n```\n\n### Forward Stripe webhook locally\n\nPurly uses `STRIPE_WEBHOOK_PATH` variable for webhook URL obfuscation. Don't forget to replace it in production with your own random value.\n\n```sh\nstripe login # only once - stripe will remember your session\nstripe listen --forward-to localhost:8000/api/stripe/webhook/e7a42abb5d31ec92bdfeec3cb0a4fa1b\n```\n\n\u003e Stripe CLI will give you webhook signing secret like `whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`. Save this value in .env as **STRIPE_WEBHOOK_SECRET** and restart API. CLI gives you the same secret so no need to repeat this step every time you run listen command.\n\n## Contributing\n\nCurrently project is in early stage of development and many things are changing so often. Keep it in mind if you want to contribute.\n\n1. Use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) for commits and PRs.\n2. If you want to introduce bigger change I recommend to create issue first with feature request and get approval from mantainer :) Otherwise your pull request can be declined and you will waste your time.\n3. After changes in table schemas, run `db:generate` script to generate migrations and run `db:migrate` to run migration.\n\n## License\n\nDistributed under the MIT License. See `LICENSE` for more information.\n\n## Contributors\n\n![contributors](https://contributors-img.web.app/image?repo=michaldziuba03/purly)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaldziuba03%2Fpurly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichaldziuba03%2Fpurly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaldziuba03%2Fpurly/lists"}