ohUrlShortener
适合中小型社区网站使用的短链接服务系统,支持短链接生产、查询及302转向,并自带点击量统计、独立IP数统计、访问日志查询:
- 支持 Docker One Step Start 部署、Makefile 编译打包
- 支持短链接生产、查询、存储、302转向
- 支持访问日志查询、访问量统计、独立IP数统计
- 支持 HTTP API 方式新建短链接、禁用/启用短链接、查看短链接统计信息、新建管理员、修改管理员密码
- 支持访问日志导出,方便线下分析

docker/one_step_start.sh
,该命令将会:docker/admin.Dockerfile
)docker/portal.Dockerfile
)docker/pull_build.yml
其他描述内容构建 redis
和 postgresql
镜像及服务,并对其运行状态做判断,等待缓存和数据库服务正常之后,再启动其他必要服务 (本地构建镜像请查阅 local_build.yml
)network_ohurlshortener
的虚拟网络供上述服务使用9091
、9092
端口分别应对 ohUrlShortener-Portal
及 ohUrlShortener-Admin
应用ohUrlShortener
默认密码: -2aDzm=0(ln_9^1
storage/users_storage.go
中storage/users_storage_test.go
中的 TestNewUser()
方法ohurlshortener [-c config_file] [-s admin|portal|<omit to start both>]
[app] # 应用是否以 debug 模式启动,主要作用会在go-gin 框架上体现(eg:日志输出等) debug = false # 短链接系统本地启动端口 port = 9091 # 短链接系统管理后台本地启动端口 admin_port = 9092 # 例如:https://t.cn/ 是前缀(不要忘记最后一个/符号) url_prefix = http://localhost:9091/ [redis] ... [postgres] ...
func PasswordBase58Hash(password string) (string, error) { data, err := utils.Sha256Of(password) if err != nil { return "", err } return base58.Encode(data), nil }
Authorization: Bearer {sha256_of_password}
curl --request POST \ --url http://localhost:9092/api/url \ --header 'Authorization: Bearer EZ2zQjC3fqbkvtggy9p2YaJiLwx1kKPTJxvqVzowtx6t' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data dest_url=http://localhost:9092/admin/dashboard \ --data memo=dashboard
{ "code": 200, "status": true, "message": "success", "result": { "short_url": "http://localhost:9091/BUUtpbGp" }, "date": "2022-04-10T21:31:29.36559+08:00" }
curl --request PUT \ --url http://localhost:9092/api/url/33R5QUtD/change_state \ --header 'Authorization: Bearer EZ2zQjC3fqbkvtggy9p2YaJiLwx1kKPTJxvqVzowtx6t' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data enable=false
{ "code": 200, "status": true, "message": "success", "result": true, "date": "2022-04-10T21:31:25.7744402+08:00" }
curl --request GET \ --url http://localhost:9092/api/url/33R5QUtD \ --header 'Authorization: Bearer EZ2zQjC3fqbkvtggy9p2YaJiLwx1kKPTJxvqVzowtx6t' \ --header 'Content-Type: application/x-www-form-urlencoded'
{ "code": 200, "status": true, "message": "success", "result": { "short_url": "33R5QUtD", "today_count": 3, "yesterday_count": 0, "last_7_days_count": 0, "monthly_count": 3, "total_count": 3, "d_today_count": 1, "d_yesterday_count": 0, "d_last_7_days_count": 0, "d_monthly_count": 1, "d_total_count": 1 }, "date": "2022-04-10T21:31:22.059596+08:00" }
curl --request POST \ --url http://localhost:9092/api/account \ --header 'Authorization: Bearer EZ2zQjC3fqbkvtggy9p2YaJiLwx1kKPTJxvqVzowtx6t' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data account=hello1 \ --data password=12345678
{ "code": 200, "status": true, "message": "success", "result": null, "date": "2022-04-10T21:31:39.7353132+08:00" }
curl --request PUT \ --url http://localhost:9092/api/account/hello/update \ --header 'Authorization: Bearer EZ2zQjC3fqbkvtggy9p2YaJiLwx1kKPTJxvqVzowtx6t' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data password=world123
{ "code": 200, "status": true, "message": "success", "result": null, "date": "2022-04-10T21:31:32.5880538+08:00" }
func GenerateShortLink(initialLink string) (string, error) { if utils.EemptyString(initialLink) { return "", fmt.Errorf("empty string") } urlHash, err := utils.Sha256Of(initialLink) if err != nil { return "", err } number := new(big.Int).SetBytes(urlHash).Uint64() str := utils.Base58Encode([]byte(fmt.Sprintf("%d", number))) return str[:8], nil }
const ACCESS_LOG_CLEAN_INTERVAL = 1 * time.Minute func startTicker() error { ticker := time.NewTicker(ACCESS_LOG_CLEAN_INTERVAL) for range ticker.C { log.Println("[StoreAccessLog] Start.") if err := service.StoreAccessLogs(); err != nil { log.Printf("Error while trying to store access_log %s", err) } log.Println("[StoreAccessLog] Finish.") } return nil }