[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80302":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":15,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":22,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":29,"discoverSource":30},80302,"renewlet","zhiyingzzhou\u002Frenewlet","zhiyingzzhou","Self-hosted subscription tracker for recurring renewals, with timezone-aware reminders, seven notification channels, budgets, and spending insights.","",null,"TypeScript",96,16,72,2,0,18,24,9,3.69,"MIT License",false,"main",true,[],"2026-06-12 02:04:00","# Renewlet\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\".\u002Fpackages\u002Fclient\u002Fpublic\u002Flogo.svg\" alt=\"Renewlet\" width=\"320\">\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"README.zh-CN.md\">简体中文\u003C\u002Fa> · \u003Ca href=\"README.md\">English\u003C\u002Fa>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cimg alt=\"Self-hosted\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fself--hosted-0f172a?style=flat-square\">\n  \u003Cimg alt=\"React\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FReact-19-149eca?style=flat-square\">\n  \u003Cimg alt=\"Go and PocketBase\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FGo%20%2B%20PocketBase-00a884?style=flat-square\">\n  \u003Cimg alt=\"Docker\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FDocker-ready-2496ed?style=flat-square\">\n  \u003Cimg alt=\"Cloudflare Workers\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FCloudflare%20Workers-ready-f38020?style=flat-square\">\n  \u003Cimg alt=\"Mobile web ready\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fmobile%20web-ready-2563eb?style=flat-square\">\n  \u003Cimg alt=\"Memory 20-30MiB\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fmemory-20--30MiB-10b981?style=flat-square\">\n  \u003Cimg alt=\"MIT License\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-111827?style=flat-square\">\n\u003C\u002Fp>\n\nRenewlet is a self-hosted subscription ledger that reminds you before renewals. Add a subscription or any recurring charge, set its renewal date and reminder days, and it will notify you through the channels you configure. You can also track price, currency, budget, logo, category, and payment method.\n\nMobile web is first-class: open it in a phone browser to add subscriptions, filter lists, review stats, and configure notifications.\n\nIdle memory usage is around 20-30MiB in local testing, making it comfortable for small VPS, NAS, and homelab boxes.\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\".\u002Fdocs\u002Fscreenshots\u002Frenewlet-dashboard-en.png\" alt=\"Renewlet dashboard showing monthly spend, upcoming renewals, and spending distribution\" width=\"100%\">\n\u003C\u002Fp>\n\n## Highlights\n\n- Track each subscription clearly: name, logo, price, currency, billing cycle, renewal date, status, category, payment method, tags, website, and notes.\n- Understand spending: normalize costs by month and year, then review budget usage, category breakdowns, payment-method charts, and inactive-subscription savings.\n- Get renewal reminders: jobs are generated from each user's IANA time zone and local notification time, with reminder days, repeat reminders, delivery history, and failed-send retries.\n- Send notifications through six channels: Telegram, Notifyx, Webhook, WeCom Bot, SMTP email, and Bark.\n- Handle multiple currencies: choose Exchange API or FloatRates JSON Feeds, with fallback rates when remote providers are unavailable.\n- Customize your lists: categories, payment methods, and currencies can be adjusted in settings, with built-in icons for common payment methods.\n- Self-host one container: React frontend, Go\u002FPocketBase backend, SQLite data, and static assets run together, with data persisted to `data\u002F`.\n- Deploy to Cloudflare Workers: React static assets, Worker API, D1, R2, and Cron Triggers can run without the Go\u002FPocketBase server.\n- Mobile-web friendly: bottom navigation, subscription cards, tag-filter drawers, and settings screens are adapted for small screens.\n- Switch languages in the app: Simplified Chinese and English are supported.\n\n## Cloudflare Workers Deploy\n\n\u003Ca href=\"https:\u002F\u002Fdeploy.workers.cloudflare.com\u002F?url=https:\u002F\u002Fgithub.com\u002Fzhiyingzzhou\u002Frenewlet\">\u003Cimg src=\"https:\u002F\u002Fdeploy.workers.cloudflare.com\u002Fbutton\" alt=\"Deploy to Cloudflare\">\u003C\u002Fa>\n\n[Cloudflare Workers Deploy](docs\u002Fcloudflare-workers-deploy.md)\n\n## Quick Deploy\n\nOn a machine with Docker and Docker Compose v2:\n\n```bash\nmkdir -p renewlet && cd renewlet\ncurl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002Fzhiyingzzhou\u002Frenewlet\u002Fmain\u002Fdeploy\u002Fdocker-deploy.sh | bash\ndocker compose up -d\n```\n\nThen open:\n\n```text\nhttp:\u002F\u002Flocalhost:3000\u002Fsetup\n```\n\nCreate the first admin user. The deploy script creates `docker-compose.yml`, `.env`, and `data\u002F`, then generates `PB_ENCRYPTION_KEY` and `CRON_SECRET` for you.\n\nIf Docker Hub is unavailable, switch the image in `.env` to GHCR:\n\n```env\nRENEWLET_IMAGE=\"ghcr.io\u002Fzhiyingzzhou\u002Frenewlet:latest\"\n```\n\nThen pin a released version when you use Renewlet in production, pull, and restart:\n\n```bash\nsed -i.bak 's#RENEWLET_IMAGE=.*#RENEWLET_IMAGE=\"ghcr.io\u002Fzhiyingzzhou\u002Frenewlet:0.1.0\"#' .env\ndocker compose pull\ndocker compose up -d\n```\n\n`latest` only moves on stable GitHub Releases. For production, prefer a concrete version tag such as `0.1.0`; release candidates use tags like `0.1.0-rc.1` and never update `latest`.\n\n### Upgrade\n\nBack up data and config before upgrading:\n\n```bash\ntar -czf renewlet-backup-$(date +%F).tgz .env docker-compose.yml data\n```\n\nUpgrade to a specific stable image:\n\n```bash\nsed -i.bak 's#RENEWLET_IMAGE=.*#RENEWLET_IMAGE=\"zhiyingzzhou\u002Frenewlet:0.1.0\"#' .env\ndocker compose pull\ndocker compose up -d\ndocker compose logs -f\n```\n\n### Common commands\n\nCheck status and logs:\n\n```bash\ndocker compose ps\ndocker compose logs -f\n```\n\nStop the service while keeping data:\n\n```bash\ndocker compose down\n```\n\nCommon settings live in `.env`:\n\n| Variable | Purpose |\n| --- | --- |\n| `PORT` | Public port, `3000` by default. |\n| `RENEWLET_IMAGE` | Docker image, `zhiyingzzhou\u002Frenewlet:latest` by default. |\n| `TZ` | Container time zone, mainly for logs; reminders use each user's time zone. |\n| `PB_ENCRYPTION_KEY` | Encryption key for sensitive PocketBase settings. Do not rotate it casually after deployment. |\n| `CRON_SECRET` | Bearer secret for external Cron calls to `\u002Fapi\u002Fcron\u002Fnotifications`. |\n| `NOTIFICATION_SCHEDULER_ENABLED` | Enables the built-in notification scheduler. Defaults to `true`. |\n\nThe full Docker environment template is in `.env.example`.\n\n## Releases\n\nRenewlet publishes stable versions from GitHub tags such as `v0.1.0`. Each stable release includes Docker Hub and GHCR images plus a `renewlet-docker-vX.Y.Z.zip` deployment package. Release candidates are published as prereleases with `rc` Docker tags and are meant for validation before a stable release.\n\nDevelopment happens on `dev`; `main` represents the latest stable release. Release and hotfix work use `release\u002FvX.Y.Z` and `hotfix\u002FvX.Y.Z` branches. Pull requests and commits should follow Conventional Commits, for example `feat: add notification channel` or `fix: prevent duplicate reminders`.\n\nSee [Release Process](docs\u002Frelease-process.md) for the full workflow.\n\n## Screenshots\n\n\u003Ctable>\n  \u003Ctr>\n    \u003Ctd width=\"50%\">\n      \u003Cstrong>Subscriptions\u003C\u002Fstrong>\u003Cbr>\n      \u003Cimg src=\".\u002Fdocs\u002Fscreenshots\u002Frenewlet-subscriptions-en.png\" alt=\"Renewlet subscriptions view with filters, tags, statuses, and service logos\">\n    \u003C\u002Ftd>\n    \u003Ctd width=\"50%\">\n      \u003Cstrong>Statistics\u003C\u002Fstrong>\u003Cbr>\n      \u003Cimg src=\".\u002Fdocs\u002Fscreenshots\u002Frenewlet-statistics-en.png\" alt=\"Renewlet statistics view with budget usage, category spending, and payment method charts\">\n    \u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n    \u003Ctd width=\"50%\">\n      \u003Cstrong>Renewal Calendar\u003C\u002Fstrong>\u003Cbr>\n      \u003Cimg src=\".\u002Fdocs\u002Fscreenshots\u002Frenewlet-calendar-en.png\" alt=\"Renewlet renewal calendar showing monthly renewal events and estimated spend\">\n    \u003C\u002Ftd>\n    \u003Ctd width=\"50%\">\n      \u003Cstrong>Notifications\u003C\u002Fstrong>\u003Cbr>\n      \u003Cimg src=\".\u002Fdocs\u002Fscreenshots\u002Frenewlet-notifications-en.png\" alt=\"Renewlet notification settings showing channels and email configuration\">\n    \u003C\u002Ftd>\n  \u003C\u002Ftr>\n\u003C\u002Ftable>\n\n### Mobile\n\n\u003Ctable>\n  \u003Ctr>\n    \u003Ctd width=\"50%\">\n      \u003Cstrong>Mobile subscriptions\u003C\u002Fstrong>\u003Cbr>\n      \u003Cimg src=\".\u002Fdocs\u002Fscreenshots\u002Frenewlet-subscriptions-h5-en.png\" alt=\"Renewlet mobile subscriptions view with filters, subscription cards, logos, prices, and tags\">\n    \u003C\u002Ftd>\n    \u003Ctd width=\"50%\">\n      \u003Cstrong>Mobile notification methods\u003C\u002Fstrong>\u003Cbr>\n      \u003Cimg src=\".\u002Fdocs\u002Fscreenshots\u002Frenewlet-notifications-h5-en.png\" alt=\"Renewlet mobile notification methods view showing the email channel and SMTP email configuration\">\n    \u003C\u002Ftd>\n  \u003C\u002Ftr>\n\u003C\u002Ftable>\n\n## Local Development\n\nInstall dependencies:\n\n```bash\npnpm install\n```\n\nStart the backend:\n\n```bash\npnpm --dir packages\u002Fserver start\n```\n\nStart the frontend:\n\n```bash\npnpm --filter @renewlet\u002Fclient dev\n```\n\nLocal Vite runs at `http:\u002F\u002Flocalhost:5173` and proxies `\u002Fapi` and `\u002F_` to `http:\u002F\u002F127.0.0.1:3000`.\n\nBuild:\n\n```bash\npnpm build\n```\n\nCommon checks:\n\n```bash\npnpm check:file-lines\npnpm check:deploy\npnpm --filter @renewlet\u002Fclient typecheck\npnpm --dir packages\u002Fserver test\npnpm test:all\n```\n\n## Contributing\n\nIssues, docs improvements, tests, and pull requests are welcome. For larger changes, please open an issue first with the goal, use case, and rough approach so the direction can be aligned before implementation.\n\n## Acknowledgements\n\n- [LINUX DO](https:\u002F\u002Flinux.do\u002F): Renewlet recognizes and thanks the LINUX DO community for supporting open-source project discussion.\n\n## License\n\nRenewlet is open-sourced under the [MIT License](LICENSE).\n","Renewlet 是一个自托管的订阅跟踪器，用于管理定期续订，并提供时区感知的提醒服务。它支持六种通知渠道（包括Telegram、Notifyx等），帮助用户追踪预算和支出情况。项目采用TypeScript编写，前端基于React构建，后端使用Go语言配合PocketBase框架，可选Docker或Cloudflare Workers部署方式，使得其在小内存VPS、NAS及家庭实验室环境中也能高效运行。适用于需要对个人或企业订阅服务进行管理和成本控制的场景。","2026-06-11 04:00:12","CREATED_QUERY"]