In our daily work and life, there are always various domain name links that need to be shared with colleagues, friends or family. However, there are always some problems due to the length of domain name is too long and there are various restrictions, or it is impossible to copy all of them. In order to solve this problem, we need a short link generator.
Based on the above idea, I wrote a short link generator:
https://github.com/icowan/shorter
Project brief introduction
The service is developed based on the go kit component, and the database is stored based on Redis or Mongo.
It can be deployed through containers or in kubernetes.
directory structure
- cmd: app startup portal
- dist: front end static file directory
- install: installation directory
-
pkg
- Endpoint: endpoint
- http: transport processing
- logging: log Middleware
- repository: implementation of warehouse storage logic
- service: logical implementation
├── Dockerfile ├── Makefile ├── README.md ├── cmd │ ├── main.go │ └── service ├── dist ├── go.mod ├── go.sum ├── install └── pkg ├── endpoint ├── http ├── logging ├── repository └── service
API
The service has two interfaces, one is to generate short address, the other is to jump short address.
Data can be stored through Redis or MongoDB.
Repository
model
Data structure of storage, mainly three fields
- Code: generated unique code
- url: source address
- Created at: created at
// pkg/service/model.go type Redirect struct { Code string `json:"code"` URL string `json:"url"` CreatedAt time.Time `json:"created_at"` }
repository
repository provides two methods: Find and Store
- Find queries url information according to code
- Store store url information
// pkg/service/repository.go type Repository interface { Find(code string) (redirect *Redirect, err error) Store(redirect *Redirect) error }
Repository is an Interface type structure, which has no specific implementation. Here, different storage methods are implemented according to the different needs of the storage database used.
- mongodb: pkg/repository/mongo/repository.go
- redis: pkg/repository/redis/repository.go
In the file cmd/service/service.go of the startup portal, you can see how to select startup:
// cmd/service/service.go var repo service.Repository switch *dbDrive { case "mongo": repo, err = mongodb.NewMongoRepository(*mongoAddr, "redirect", 60) if err != nil { _ = level.Error(logger).Log("connect", "db", "err", err.Error()) return } case "redis": db, _ := strconv.Atoi(*redisDB) repo, err = redis.NewRedisRepository(redis.RedisDrive(*redisDrive), *redisHosts, *redisPassword, "shorter", db) if err != nil { _ = level.Error(logger).Log("connect", "db", "err", err.Error()) return } }
Service
service provides two methods, Get and Post.
// pkg/service/service.go type Service interface { Get(ctx context.Context, code string) (redirect *Redirect, err error) Post(ctx context.Context, domain string) (redirect *Redirect, err error) }
- Get: pass in the code code, and search the stored address information according to the code
- Post: pass in the original url address, generate the code code, store it in the database, and return to the structure
Transport Post
To generate the address, you need to pass it to the JSON structure through POST to receive the reference file:
// pkg/endpoint/endpoint.go type PostRequest struct { URL string `json:"url" validate:"required,url,lt=255"` } type dataResponse struct { Url string `json:"url"` Code string `json:"code"` CreatedAt time.Time `json:"created_at"` ShortUri string `json:"short_uri"` } type PostResponse struct { Err error `json:"err"` Data dataResponse `json:"data"` }
The interface only receives one parameter "url" and returns four parameters.
- url: original address
- short_uri: short address of jump
- Code: code to jump to short address
- Created at: build time
Transport Get
Query by uri code, for example:
r.Handle("/{code}", kithttp.NewServer( endpoints.GetEndpoint, decodeGetRequest, encodeGetResponse, options["Get"]...)).Methods(http.MethodGet)
Resolve Request:
func decodeGetRequest(_ context.Context, r *http.Request) (interface{}, error) { vars := mux.Vars(r) code, ok := vars["code"] if !ok { return nil, ErrCodeNotFound } req := endpoint.GetRequest{ Code: code, } return req, nil }
Jump:
func encodeGetResponse(ctx context.Context, w http.ResponseWriter, response interface{}) (err error) { if f, ok := response.(endpoint.Failure); ok && f.Failed() != nil { ErrorRedirect(ctx, f.Failed(), w) return nil } resp := response.(endpoint.GetResponse) redirect := resp.Data.(*service.Redirect) http.Redirect(w, &http.Request{}, redirect.URL, http.StatusFound) return } // Error back to home page func ErrorRedirect(_ context.Context, err error, w http.ResponseWriter) { http.Redirect(w, &http.Request{}, os.Getenv("SHORT_URI"), http.StatusFound) }
Docker compose deployment
Docker compose is easy to start, and you can directly enter the directory install / docker compose/
Then execute:
$ docker-compose up
Deploy on Kepler cloud platform
Because this project depends on databases: Redis and MongoDB, we need to deploy the Redis or MongoDB persistent application before creating the application. In the project, I have given the Demo of two database deployment. You can try to start it in your own environment.
Kepler cloud platform tends to deploy stateless applications, i.e. Deployment type. Applications that need to be persistent, such as stateful set type, are better deployed to form distributed clusters or master-slave nodes.
Single point Redis service: install/kubernetes/redis/
Single point MongoDB service: install/kubernetes/mongo/
Create an app
We create an application called shorter:
- Enter the address of github: icowan/shorter
- Select version: v0.1.8
- Select the number of containers to start: 2
- The maximum memory is 64Mi. The application is simple and does not need too much memory.
- Port started: 8080
- Submit to administrator for review
Administrator reviews and initializes publishing application
After receiving the notice, the administrator enters the basic details page for review:
Mainly check whether the basic information submitted is correct, whether the YAML file generated automatically is correct, whether the Jenkins template generated automatically is correct and whether the Dockerfile file in the user project is wrong. If there is no problem, click the "start deployment" button to directly build and publish the application.
After the application is deployed successfully, the system will send notifications to e-mails and wechat like to inform the application of the release. (WeChat notification requires you to set up the "personal settings > >" account binding "> > bindings (focus on WeChat public number can be automatically bound) > > message subscription settings in the message subscription check the type and mode of notification.
- Bind wechat notification:
- Type and method of subscription notice: https://r.nsini.com/hi2hy1bWg
If you receive a successful release message, the app will start successfully.
In order to upgrade the application later, the application creator or group member can directly select the "Build" button on the application details page and select the corresponding application version.
The rollback application is also convenient:
Just click the "rollback" button, select the version to be rolled back in the pop-up window, click "rollback" and confirm, the platform will start the Docker Image of this version.
Generate external address
After completion, in order for the agent to be accessible externally, an externally accessible address needs to be generated.
There is an "external address" card at the bottom of the application details. If the application is created for the first time, there is an "add" button on the right side of the card header. Click it and confirm to generate an external address.
The above is the generated address, through which we can access the shorter application.
test
I have deployed an example of a student resolving to the application through short domain name. Click the address below to generate a short chain page.
Paste the address of the short link to the input box, and click the "generate short link" button to generate.
Click the "copy" button to copy and use the short address.
tail
Golang is a very efficient and easy to learn programming language. Based on the characteristics of golang, we can write many interesting tools or platforms.
Your reward is my motivation to update