Init project
This commit is contained in:
parent
f4fabfc950
commit
58fbb48fc8
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.idea
|
||||
build/
|
||||
44
Makefile
Normal file
44
Makefile
Normal file
@ -0,0 +1,44 @@
|
||||
BINARY_DEFAULT="build/myurls.service"
|
||||
BINARY_LINUX="build/linux-amd64-myurls.service"
|
||||
BINARY_DARWIN="build/darwin-amd64-myurls.service"
|
||||
BINARY_WINDOWS="build/windows-amd64-myurls.service"
|
||||
|
||||
GOFILES="main.go"
|
||||
VERSION=1.0.0
|
||||
BUILD=`date +%FT%T%z`
|
||||
|
||||
default:
|
||||
@echo ${BINARY_DEFAULT}
|
||||
@go build -o ${BINARY_DEFAULT} ${GOFILES}
|
||||
|
||||
all:
|
||||
@echo ${BINARY_LINUX}
|
||||
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY_LINUX} ${GOFILES}
|
||||
@echo ${BINARY_DARWIN}
|
||||
@CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o ${BINARY_DARWIN} ${GOFILES}
|
||||
@echo ${BINARY_WINDOWS}
|
||||
@CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ${BINARY_WINDOWS} ${GOFILES}
|
||||
|
||||
linux:
|
||||
@echo ${BINARY_LINUX}
|
||||
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY_LINUX} ${GOFILES}
|
||||
|
||||
darwin:
|
||||
@echo ${BINARY_DARWIN}
|
||||
@CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o ${BINARY_DARWIN} ${GOFILES}
|
||||
|
||||
windows:
|
||||
@echo ${BINARY_WINDOWS}
|
||||
@CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ${BINARY_WINDOWS} ${GOFILES}
|
||||
|
||||
install:
|
||||
@go mod tidy
|
||||
|
||||
fmt:
|
||||
@go fmt ./...
|
||||
|
||||
clean:
|
||||
@if [ -f ${BINARY_DEFAULT} ] ; then rm ${BINARY_DEFAULT} ; fi
|
||||
@if [ -f ${BINARY_LINUX} ] ; then rm ${BINARY_LINUX} ; fi
|
||||
@if [ -f ${BINARY_DARWIN} ] ; then rm ${BINARY_DARWIN} ; fi
|
||||
@if [ -f ${BINARY_WINDOWS} ] ; then rm ${BINARY_WINDOWS} ; fi
|
||||
87
README.md
Normal file
87
README.md
Normal file
@ -0,0 +1,87 @@
|
||||
# bitly
|
||||
|
||||
基于 golang1.13 与 Redis 实现的本地短链接服务,用于缩短请求链接与短链接还原。
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Update](#update)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Docker](#Docker)
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [API](#api)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
# Dependencies
|
||||
|
||||
本服务依赖于 Redis 提供长短链接映射关系存储,你需要本地安装 Redis 服务来保证短链接服务的正常运行。
|
||||
|
||||
```shell script
|
||||
sudo apt-get update
|
||||
|
||||
# 安装Redis
|
||||
sudo add-apt-repository ppa:chris-lea/redis-server -y
|
||||
sudo apt-get update
|
||||
sudo apt-get install redis-server -y
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
TODO
|
||||
|
||||
## Install
|
||||
|
||||
安装项目依赖
|
||||
|
||||
```shell script
|
||||
make install
|
||||
```
|
||||
|
||||
生成可执行文件,目录位于 build/ 。默认当前平台,其他平台请参照 Makefile 或执行对应 go build 命令。
|
||||
|
||||
```shell script
|
||||
make
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
前往 [Release](https://github.com/CareyWang/MyUrls/releases) 下载对应平台可执行文件。
|
||||
|
||||
```shell script
|
||||
./build/myurls -h
|
||||
|
||||
Usage of ./build/myurls.service:
|
||||
-domain string
|
||||
短链接域名 (default "s.wcc.best")
|
||||
-port int
|
||||
服务端口 (default 8002)
|
||||
-ttl int
|
||||
短链接有效期,单位(天),默认90天。 (default 90)
|
||||
```
|
||||
|
||||
建议配合 [pm2](https://pm2.keymetrics.io/) 开启守护进程。
|
||||
|
||||
```shell script
|
||||
pm2 start myurls.service --watch --name myurls -- -domain example.com
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
[参考文档](https://myurls.mydoc.li)
|
||||
|
||||
|
||||
## Maintainers
|
||||
|
||||
[@CareyWang](https://github.com/CareyWang)
|
||||
|
||||
## Contributing
|
||||
|
||||
PRs accepted.
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
MIT © 2020 CareyWang
|
||||
12
go.mod
Normal file
12
go.mod
Normal file
@ -0,0 +1,12 @@
|
||||
module github.com/CareyWang/MyUrls
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.5.0
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.4 // indirect
|
||||
)
|
||||
57
go.sum
Normal file
57
go.sum
Normal file
@ -0,0 +1,57 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
129
main.go
Normal file
129
main.go
Normal file
@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Code int
|
||||
Message string
|
||||
LongUrl string
|
||||
ShortUrl string
|
||||
}
|
||||
|
||||
const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
const defaultPort int = 8002
|
||||
const defaultDomain = "s.wcc.best"
|
||||
const defaultExpire = 90
|
||||
const redisConfig = "127.0.0.1:6379"
|
||||
|
||||
func main() {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.Default()
|
||||
|
||||
port := flag.Int("port", defaultPort, "服务端口")
|
||||
domain := flag.String("domain", defaultDomain, "短链接域名")
|
||||
ttl := flag.Int("ttl", defaultExpire, "短链接有效期,单位(天),默认90天。")
|
||||
flag.Parse()
|
||||
|
||||
router.POST("/short", func(context *gin.Context) {
|
||||
res := &Response{
|
||||
Code: 1,
|
||||
Message: "",
|
||||
LongUrl: "",
|
||||
ShortUrl: "",
|
||||
}
|
||||
|
||||
longUrl := context.Query("longUrl")
|
||||
_longUrl, _ := base64.StdEncoding.DecodeString(longUrl)
|
||||
longUrl = string(_longUrl)
|
||||
|
||||
shortKey := longToShort(longUrl, *ttl * 24 * 3600)
|
||||
if shortKey == "" {
|
||||
res.Code = 0
|
||||
res.Message = "短链接生成失败"
|
||||
context.JSON(500, *res)
|
||||
return
|
||||
}
|
||||
|
||||
res.LongUrl = longUrl
|
||||
res.ShortUrl = "http://" + *domain + "/" + shortKey
|
||||
context.JSON(200, *res)
|
||||
})
|
||||
|
||||
router.GET("/:shortKey", func(context *gin.Context) {
|
||||
shortKey := context.Param("shortKey")
|
||||
longUrl := shortToLong(shortKey)
|
||||
|
||||
if longUrl == "" {
|
||||
context.String(http.StatusNotFound, "短链接不存在或已过期")
|
||||
} else {
|
||||
context.Redirect(http.StatusMovedPermanently, longUrl)
|
||||
}
|
||||
})
|
||||
|
||||
router.Run(fmt.Sprintf(":%d", *port))
|
||||
}
|
||||
|
||||
// 短链接转长链接
|
||||
func shortToLong(shortKey string) string {
|
||||
redisClient := initRedis()
|
||||
|
||||
longUrl, _ := redis.String(redisClient.Do("get", shortKey))
|
||||
return longUrl
|
||||
}
|
||||
|
||||
// 长链接转短链接
|
||||
func longToShort(longUrl string, ttl int) string {
|
||||
redisClient := initRedis()
|
||||
|
||||
// 是否生成过该长链接对应短链接
|
||||
_existsKey, _ := redis.String(redisClient.Do("get", longUrl))
|
||||
if _existsKey != "" {
|
||||
_, _ = redisClient.Do("expire", _existsKey, ttl)
|
||||
|
||||
return _existsKey
|
||||
}
|
||||
|
||||
// 重试三次
|
||||
var shortKey string
|
||||
for i := 0; i < 3; i++ {
|
||||
shortKey = generate(6)
|
||||
|
||||
_existsLongUrl, _ := redis.String(redisClient.Do("get", longUrl))
|
||||
if _existsLongUrl != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if shortKey != "" {
|
||||
_, _ = redisClient.Do("mset", shortKey, longUrl, longUrl, shortKey)
|
||||
|
||||
_, _ = redisClient.Do("expire", shortKey, ttl)
|
||||
_, _ = redisClient.Do("expire", longUrl, ttl)
|
||||
}
|
||||
|
||||
return shortKey
|
||||
}
|
||||
|
||||
// 产生一个63位随机整数,除以字符数取余获取对应字符
|
||||
func generate(bits int) string {
|
||||
b := make([]byte, bits)
|
||||
for i := range b {
|
||||
b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func initRedis() redis.Conn {
|
||||
client, _ := redis.Dial("tcp", redisConfig)
|
||||
|
||||
return client
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user