{"id":36985293,"url":"https://github.com/nodesire7/nsl-go","last_synced_at":"2026-01-13T23:01:25.255Z","repository":{"id":328960261,"uuid":"1117528850","full_name":"nodesire7/nsl-go","owner":"nodesire7","description":"New short link (NSL GO) - A complete short link system with user management, multi-domain support, QR code generation, Redis cache, and API rate limiting","archived":false,"fork":false,"pushed_at":"2025-12-19T11:25:05.000Z","size":242,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-20T02:52:22.484Z","etag":null,"topics":["api","docker","go","golang","jwt","meilisearch","microservice","postgresql","qrcode","redis","rest-api","short-link","url-shortener"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nodesire7.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-16T12:48:00.000Z","updated_at":"2025-12-19T11:17:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nodesire7/nsl-go","commit_stats":null,"previous_names":["nodesire7/nsl-go"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/nodesire7/nsl-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nodesire7%2Fnsl-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nodesire7%2Fnsl-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nodesire7%2Fnsl-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nodesire7%2Fnsl-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nodesire7","download_url":"https://codeload.github.com/nodesire7/nsl-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nodesire7%2Fnsl-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28399535,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: 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":["api","docker","go","golang","jwt","meilisearch","microservice","postgresql","qrcode","redis","rest-api","short-link","url-shortener"],"created_at":"2026-01-13T23:01:24.130Z","updated_at":"2026-01-13T23:01:25.243Z","avatar_url":"https://github.com/nodesire7.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🔗 New short link (NSL GO)\n\n一个功能完整的短链接生成和管理系统，使用Go语言重构，支持PostgreSQL数据库和Meilisearch全文搜索。\n\n## ✨ 特性\n\n* 🚀 **高性能**: Go语言编写，性能优异\n* 🗄️ **PostgreSQL**: 使用PostgreSQL作为主数据库\n* 🔍 **全文搜索**: 集成Meilisearch，支持快速搜索\n* 🔢 **动态链接长度**: 自动扩展链接长度（6位起，用完自动扩展）\n* 🔐 **内容哈希一致性**: 同一用户同一域名下，相同URL返回相同短链接（幂等粒度：`user + domain + hash`）\n* 📊 **数据统计**: 完整的访问统计和分析\n* 🎨 **Web UI**: 美观的前台管理面板，支持登录页面\n* 🐳 **Docker部署**: 一键部署，零配置\n* 👤 **用户系统**: 支持用户注册、登录、JWT认证\n* 🔑 **用户Token**: 每个用户自动生成永久Token，用于API调用\n* 👨‍💼 **Admin管理**: 自动创建admin用户，提供命令行管理工具\n* 🌐 **多域名支持**: 每个用户可自定义多个短链接域名\n* 📱 **二维码生成**: 自动生成短链接二维码\n* ⚡ **Redis缓存**: 支持Redis缓存提升性能\n* 🛡️ **API限流**: 内置限流保护，防止滥用\n* 🔒 **权限控制**: 新用户默认限制10条链接，可联系管理员提升\n\n## 🚀 快速开始\n\n### 一键安装（推荐）\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/nodesire7/nsl-go/main/install.sh | bash\n```\n\n### 使用Docker Compose\n\n```bash\ndocker-compose up -d\n```\n\n**首次启动后**：\n1. 查看日志确认已创建 admin 用户：\n   ```bash\n   docker-compose logs app | grep \"Admin用户已创建\"\n   ```\n2. 出于安全原因，**不会在日志中打印明文密码/Token**。请使用管理工具生成/重置 admin 密码后登录：\n   ```bash\n   make build-admin\n   ./bin/nsl-admin -action=reset-password\n   ```\n3. 访问 `http://localhost:9110/login` 登录\n\n### 手动安装\n\n1. 从 [Releases](https://github.com/nodesire7/nsl-go/releases) 下载对应平台的二进制文件\n2. 解压并运行：\n\n```bash\ntar -xzf nsl-go-linux-amd64.tar.gz\n./nsl-go\n```\n\n**首次启动后**：\n- 查看控制台输出确认已创建 admin 用户\n- 使用管理工具重置 admin 密码后登录：\n  ```bash\n  ./bin/nsl-admin -action=reset-password\n  ```\n- 访问 `http://localhost:9110/login` 登录\n\n### Docker Hub\n\n```bash\ndocker pull nodesire77/nsl-go:latest\n```\n\n## 📋 环境变量\n\n| 变量名 | 默认值 | 说明 |\n|--------|--------|------|\n| `BASE_URL` | http://localhost:9110 | 服务基础URL |\n| `JWT_SECRET` | 必需 | **Cookie 登录鉴权**的JWT签名密钥（建议 `openssl rand -hex 32`） |\n| `DB_HOST` | localhost | PostgreSQL主机 |\n| `DB_PORT` | 5432 | PostgreSQL端口 |\n| `DB_USER` | postgres | 数据库用户 |\n| `DB_PASSWORD` | postgres | 数据库密码 |\n| `DB_NAME` | shortlink | 数据库名 |\n| `MEILI_HOST` | http://localhost:7700 | Meilisearch地址 |\n| `MEILI_KEY` | | Meilisearch主密钥 |\n| `REDIS_HOST` | | Redis地址（可选） |\n| `REDIS_PASSWORD` | | Redis密码（可选） |\n| `MIN_CODE_LENGTH` | 6 | 最小短代码长度 |\n| `MAX_CODE_LENGTH` | 10 | 最大短代码长度 |\n| `LOG_LEVEL` | INFO | 日志级别 |\n| `SERVER_PORT` | 9110 | 服务端口 |\n\n## ⚠️ 重要说明（请务必读）\n\n### 多域名重定向（按 Host 解析）\n\n- **当你使用自定义短链域名时**，服务端会根据请求的 `Host`（访问的域名）去匹配 `domains.domain`，然后再用 `(domain_id, code)` 精确查询，避免多域名下 code 冲突导致误跳转。\n- 如果请求 Host 无法匹配任何 domain：会回退到“全库按 code 查询”，**仅当全库只命中 1 条**才允许跳转，否则返回 404。\n\n\u003e 建议：`domains.domain` 保存为纯域名（例如 `s.example.com`），不要带路径；如果是本地测试带端口，也支持 `localhost:9110` 的匹配。\n\n### 依赖校验（go.sum）\n\n当前仓库可能尚未提交 `go.sum`。CI 已做兼容处理，但**建议你在本地安装 Go 后补齐并提交**：\n\n```bash\ngo mod tidy\ngit add go.sum\ngit commit -m \"chore: add go.sum\"\ngit push\n```\n\n## 🔧 API接口\n\n### 认证方式\n\n系统支持两种认证方式：\n\n1. **用户API Token**（推荐，永久有效）：\n```\nAuthorization: Bearer nsl_xxxxxxxxxxxxx\n```\n\n2. **JWT Token**（用于Web登录，HttpOnly Cookie）：\n```\nAuthorization: Bearer YOUR_JWT_TOKEN\n```\n或通过 Cookie：`Cookie: access_token=YOUR_JWT_TOKEN`\n\n### 用户注册\n\n```bash\ncurl -X POST http://localhost:9110/api/v2/auth/register \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"username\": \"testuser\",\n    \"email\": \"test@example.com\",\n    \"password\": \"password123\"\n  }'\n```\n\n**响应**（包含用户的API Token，永久有效，仅返回一次）：\n```json\n{\n  \"token\": \"JWT_TOKEN\",\n  \"user\": {\n    \"id\": 1,\n    \"username\": \"testuser\",\n    \"email\": \"test@example.com\",\n    \"api_token\": \"nsl_xxxxxxxxxxxxx\",\n    \"role\": \"user\",\n    \"max_links\": 10\n  }\n}\n```\n\n\u003e ⚠️ **重要**：`api_token` 仅在注册时返回一次，请妥善保存。后续登录/资料接口不会返回 `api_token`（已改为 hash 存储）。\n\n### 用户登录\n\u003e 注意：登录接口不再返回长期 `api_token`。如需创建/轮换 API Token，请调用 `/api/v2/profile/token`。\n\n```bash\ncurl -X POST http://localhost:9110/api/v2/auth/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"username\": \"testuser\",\n    \"password\": \"password123\"\n  }'\n```\n\n**响应**（Web UI 会设置 HttpOnly Cookie）：\n```json\n{\n  \"token\": \"JWT_TOKEN\",\n  \"user\": {\n    \"id\": 1,\n    \"username\": \"testuser\",\n    \"email\": \"test@example.com\",\n    \"role\": \"user\",\n    \"max_links\": 10\n  }\n}\n```\n\n### 更新用户Token\n\n```bash\ncurl -X POST http://localhost:9110/api/v2/profile/token \\\n  -H \"Authorization: Bearer YOUR_JWT_TOKEN\" \\\n  -H \"X-CSRF-Token: YOUR_CSRF_TOKEN\"\n```\n\n**响应**（包含新的 API Token）：\n```json\n{\n  \"success\": true,\n  \"api_token\": \"nsl_yyyyyyyyyyyy\",\n  \"message\": \"Token已更新，旧Token已失效\"\n}\n```\n\n\u003e ⚠️ **注意**：更新 Token 后，旧 Token 立即失效，请保存新的 Token。\n\n### 创建短链接\n\n```bash\ncurl -X POST http://localhost:9110/api/v2/links \\\n  -H \"Authorization: Bearer nsl_xxxxxxxxxxxxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://www.example.com\",\n    \"title\": \"示例网站\",\n    \"code\": \"custom\",\n    \"domain_id\": 1\n  }'\n```\n\n**响应包含二维码**：\n```json\n{\n  \"id\": 1,\n  \"code\": \"custom\",\n  \"short_url\": \"https://s.example.com/custom\",\n  \"original_url\": \"https://www.example.com\",\n  \"title\": \"示例网站\",\n  \"qr_code\": \"data:image/png;base64,iVBORw0KGgo...\",\n  \"click_count\": 0,\n  \"created_at\": \"2025-01-XX...\"\n}\n```\n\n### 获取链接列表\n\n```bash\ncurl -X GET \"http://localhost:9110/api/v2/links?page=1\u0026limit=20\" \\\n  -H \"Authorization: Bearer nsl_xxxxxxxxxxxxx\"\n```\n\n### 搜索链接\n\n```bash\ncurl -X GET \"http://localhost:9110/api/v2/links/search?q=example\" \\\n  -H \"Authorization: Bearer nsl_xxxxxxxxxxxxx\"\n```\n\n### 删除链接\n\n```bash\ncurl -X DELETE \"http://localhost:9110/api/v2/links/custom\" \\\n  -H \"Authorization: Bearer nsl_xxxxxxxxxxxxx\" \\\n  -H \"X-CSRF-Token: YOUR_CSRF_TOKEN\"\n```\n\n### 获取统计信息\n\n**基础统计**：\n```bash\ncurl -X GET \"http://localhost:9110/api/v2/stats\" \\\n  -H \"Authorization: Bearer nsl_xxxxxxxxxxxxx\"\n```\n\n**聚合统计**（日/周/月、来源、UA 等维度）：\n```bash\ncurl -X GET \"http://localhost:9110/api/v2/stats/aggregated?days=30\u0026weeks=12\u0026months=12\u0026limit=10\" \\\n  -H \"Authorization: Bearer nsl_xxxxxxxxxxxxx\"\n```\n\n**响应示例**：\n```json\n{\n  \"total_links\": 100,\n  \"total_clicks\": 5000,\n  \"today_clicks\": 50,\n  \"daily_stats\": [\n    {\"date\": \"2025-01-15\", \"click_count\": 50},\n    {\"date\": \"2025-01-14\", \"click_count\": 45}\n  ],\n  \"weekly_stats\": [\n    {\"week\": \"2025-W03\", \"click_count\": 300}\n  ],\n  \"monthly_stats\": [\n    {\"month\": \"2025-01\", \"click_count\": 1200}\n  ],\n  \"top_referers\": [\n    {\"referer\": \"https://example.com\", \"click_count\": 100}\n  ],\n  \"top_user_agents\": [\n    {\"user_agent\": \"Mozilla/5.0...\", \"click_count\": 200}\n  ],\n  \"top_ips\": [\n    {\"ip\": \"192.168.1.1\", \"click_count\": 50}\n  ],\n  \"top_links\": [...]\n}\n```\n\n## ✅ redo.md 完成度对照（当前仓库状态）\n\n- **已完成**\n  - ✅ `JWT_SECRET` 强制配置；移除“系统级 API_TOKEN 超级通行证”\n  - ✅ Web UI：HttpOnly Cookie + SameSite + CSRF（双提交）\n  - ✅ 短码生成：`crypto/rand` + DB 唯一约束冲突重试（并发安全）\n  - ✅ 幂等：按 `(user_id, domain_id, hash)` 粒度返回已有短链\n  - ✅ Redis：热点重定向缓存（v2 已按域名隔离缓存 key）\n  - ✅ 安全头、基础 SSRF 校验、请求 request_id、限流中间件\n  - ✅ 重写架构：`internal/config + internal/db(pgxpool) + internal/repo + internal/service + internal/httpv2`\n  - ✅ **统计写入异步化**：使用 `internal/jobs` worker 批量写入点击数/访问日志，跳转路径极速化\n  - ✅ **API Token 存储**：已停止写入 `users.api_token` 明文字段，历史数据会回填 `api_token_hash` 并清空明文列；鉴权优先按 hash 匹配\n  - ✅ **V1 代码完全删除**：已彻底删除 legacy 代码，全面迁移到 `internal/*` 架构\n\n- **已完成（全部）**\n  - ✅ **审计日志**：管理员操作、敏感操作记录（redo.md 1.2, 2.7, 7.1）\n  - ✅ **RBAC 权限点**：细粒度权限点（`link:create`, `link:delete`, `link:view`, `link:list`, `stats:view` 等）（redo.md 4.2, 6.2）\n  - ✅ **Meilisearch 写入失败补偿/重试/后台任务**：异步队列 + 重试机制（最大3次，间隔5秒）（redo.md 2.6）\n  - ✅ **结构化日志统一**：已统一使用 `utils` logger，移除所有 `log.Printf`（redo.md 2.7）\n  - ✅ **集成测试**：使用 testcontainers 实现 PG/Redis 集成测试（redo.md 6.3）\n  - ✅ **CI 质量工具**：`golangci-lint` / `gosec` 已在 CI 中落地（redo.md 6.3）\n  - ✅ **Metrics（Prometheus）**：指标收集和暴露（HTTP 请求、业务指标、限流等）（redo.md 6.3）\n  - ✅ **Tracing**：分布式追踪（OpenTelemetry + Jaeger）（redo.md 3.1）\n  - ✅ **聚合统计扩展**：日/周/月、来源、UA、IP 等维度统计（redo.md 5.3）\n  - ✅ **限流策略优化**：滑动窗口 + 令牌桶算法（redo.md 2.5）\n  - ✅ **代理链路真实 IP 处理**：正确处理 X-Forwarded-For / X-Real-IP（redo.md 2.5）\n\n## 🔑 用户Token说明\n\n- **自动生成**: 用户注册时自动生成永久Token（格式：`nsl_xxxxxxxxxxxxx`）\n- **永久有效**: Token没有过期时间，除非：\n  - 用户被删除\n  - 用户主动更新Token（通过 `/api/v2/profile/token` 接口）\n- **用途**: 用于API调用，替代JWT Token进行长期访问\n- **安全**: Token 以 SHA256 hash 存储在数据库中，不再存储明文；建议定期更新\n\n## 👤 Admin用户管理\n\n### 自动创建Admin用户\n\n系统首次启动时会**自动创建admin用户**。出于安全原因，日志中**不输出明文密码/Token**，请使用管理工具重置密码：\n\n```\n✅ Admin用户已创建（出于安全原因，不在日志中输出明文密码/Token）\n```\n\n### 重置Admin密码\n\n使用管理工具重置admin密码：\n\n```bash\n# 编译管理工具\nmake build-admin\n# 或\ngo build -o bin/nsl-admin ./cmd/admin\n\n# 随机生成新密码（推荐）\n./bin/nsl-admin -action=reset-password\n\n# 指定新密码\n./bin/nsl-admin -action=reset-password -password=MyNewPassword123\n\n# 查看admin用户信息\n./bin/nsl-admin -action=show-info\n```\n\n**Windows用户**：\n```powershell\n# 编译\ngo build -o bin\\nsl-admin.exe ./cmd/admin\n\n# 使用\n.\\bin\\nsl-admin.exe -action=reset-password\n.\\bin\\nsl-admin.exe -action=show-info\n```\n\n### 登录页面\n\n访问 `http://localhost:9110/login` 进入登录页面，使用admin账户登录。\n\n**首次登录后建议**：\n1. 修改admin密码（使用管理工具）\n2. 创建普通用户账户\n3. 妥善保管API Token\n\n## 🎨 Web UI\n\n### 登录\n\n访问 `http://localhost:9110/login` 进入登录页面。\n\n**默认admin账户**：\n- 用户名：`admin`\n- 密码：请使用管理工具重置生成（不会写入日志）\n\n### 管理面板\n\n登录后访问 `http://localhost:9110` 查看Web管理面板，可以：\n- 📊 查看统计信息\n- 🔗 创建和管理短链接\n- 🔍 搜索链接\n- 📱 查看二维码\n- ⚙️ 管理域名设置\n\n## 📄 许可证\n\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnodesire7%2Fnsl-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnodesire7%2Fnsl-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnodesire7%2Fnsl-go/lists"}