Initial commit
This commit is contained in:
11
internal/config/config.go
Normal file
11
internal/config/config.go
Normal file
@ -0,0 +1,11 @@
|
||||
package config
|
||||
|
||||
import "github.com/zeromicro/go-zero/rest"
|
||||
|
||||
type Config struct {
|
||||
rest.RestConf
|
||||
ScyllaDB struct {
|
||||
Hosts []string
|
||||
}
|
||||
MachineID int
|
||||
}
|
16
internal/db/models.go
Normal file
16
internal/db/models.go
Normal file
@ -0,0 +1,16 @@
|
||||
package db
|
||||
|
||||
import "github.com/scylladb/gocqlx/v2/table"
|
||||
|
||||
var UrlTable = table.New(table.Metadata{
|
||||
Name: "fivefeeteleven.urls",
|
||||
Columns: []string{"id", "redirect_url", "secret"},
|
||||
PartKey: []string{"id"},
|
||||
SortKey: []string{},
|
||||
})
|
||||
|
||||
type UrlModel struct {
|
||||
Id string
|
||||
RedirectUrl string
|
||||
Secret *string
|
||||
}
|
27
internal/db/seed.go
Normal file
27
internal/db/seed.go
Normal file
@ -0,0 +1,27 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/scylladb/gocqlx/v2"
|
||||
)
|
||||
|
||||
func Seed(session gocqlx.Session) error {
|
||||
err := session.ExecStmt(`
|
||||
CREATE KEYSPACE IF NOT EXISTS fivefeeteleven
|
||||
WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = session.ExecStmt(`
|
||||
CREATE TABLE IF NOT EXISTS fivefeeteleven.urls (
|
||||
id text PRIMARY KEY,
|
||||
redirect_url text,
|
||||
secret text
|
||||
)`)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
29
internal/handler/expandurlhandler.go
Normal file
29
internal/handler/expandurlhandler.go
Normal file
@ -0,0 +1,29 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"5feet11/internal/logic"
|
||||
"5feet11/internal/svc"
|
||||
"5feet11/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func ExpandUrlHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.ExpandReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := logic.NewExpandUrlLogic(r.Context(), svcCtx)
|
||||
resp, err := l.ExpandUrl(&req)
|
||||
if err != nil {
|
||||
httpx.Error(w, err)
|
||||
} else {
|
||||
http.Redirect(w, r, resp.RedirectUrl, http.StatusTemporaryRedirect)
|
||||
}
|
||||
}
|
||||
}
|
27
internal/handler/routes.go
Normal file
27
internal/handler/routes.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Code generated by goctl. DO NOT EDIT.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"5feet11/internal/svc"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest"
|
||||
)
|
||||
|
||||
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:snowflake",
|
||||
Handler: ExpandUrlHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/redirect",
|
||||
Handler: ShortenUrlHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
29
internal/handler/shortenurlhandler.go
Normal file
29
internal/handler/shortenurlhandler.go
Normal file
@ -0,0 +1,29 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"5feet11/internal/logic"
|
||||
"5feet11/internal/svc"
|
||||
"5feet11/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
)
|
||||
|
||||
func ShortenUrlHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.ShortenReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := logic.NewShortenUrlLogic(r.Context(), svcCtx)
|
||||
resp, err := l.ShortenUrl(&req)
|
||||
if err != nil {
|
||||
httpx.Error(w, err)
|
||||
} else {
|
||||
httpx.OkJson(w, resp)
|
||||
}
|
||||
}
|
||||
}
|
46
internal/logic/expandurllogic.go
Normal file
46
internal/logic/expandurllogic.go
Normal file
@ -0,0 +1,46 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"5feet11/internal/db"
|
||||
"5feet11/internal/svc"
|
||||
"5feet11/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ExpandUrlLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewExpandUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ExpandUrlLogic {
|
||||
return &ExpandUrlLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ExpandUrlLogic) ExpandUrl(req *types.ExpandReq) (resp *types.ExpandResp, err error) {
|
||||
queryUrl := db.UrlTable.SelectBuilder("redirect_url").Query(l.svcCtx.DB)
|
||||
queryUrl.BindStruct(db.UrlModel{Id: req.Snowflake})
|
||||
|
||||
var urls []db.UrlModel
|
||||
if err := queryUrl.Select(&urls); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(urls) != 1 {
|
||||
return nil, fmt.Errorf("no URL found")
|
||||
}
|
||||
|
||||
resp = &types.ExpandResp{
|
||||
RedirectUrl: urls[0].RedirectUrl,
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
46
internal/logic/shortenurllogic.go
Normal file
46
internal/logic/shortenurllogic.go
Normal file
@ -0,0 +1,46 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"5feet11/internal/db"
|
||||
"5feet11/internal/svc"
|
||||
"5feet11/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ShortenUrlLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewShortenUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShortenUrlLogic {
|
||||
return &ShortenUrlLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ShortenUrlLogic) ShortenUrl(req *types.ShortenReq) (resp *types.ShortenResp, err error) {
|
||||
id := l.svcCtx.Snowflake.Generate().Base58()
|
||||
|
||||
insertUrl := db.UrlTable.InsertBuilder().TTL(30 * time.Second).Query(l.svcCtx.DB)
|
||||
insertUrl.BindStruct(db.UrlModel{
|
||||
Id: id,
|
||||
RedirectUrl: req.RedirectUrl,
|
||||
Secret: &req.Secret,
|
||||
})
|
||||
|
||||
if err := insertUrl.ExecRelease(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp = &types.ShortenResp{
|
||||
Id: id,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
46
internal/svc/servicecontext.go
Normal file
46
internal/svc/servicecontext.go
Normal file
@ -0,0 +1,46 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"5feet11/internal/config"
|
||||
"5feet11/internal/db"
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/scylladb/gocqlx/v2"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
DB gocqlx.Session
|
||||
Snowflake *snowflake.Node
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
cluster := gocql.NewCluster(c.ScyllaDB.Hosts...)
|
||||
|
||||
session, err := gocqlx.WrapSession(cluster.CreateSession())
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
|
||||
if err := db.Seed(session); err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
logx.Info("Schema's are up to date!")
|
||||
|
||||
node, err := snowflake.NewNode(1)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
DB: session,
|
||||
Snowflake: node,
|
||||
}
|
||||
}
|
||||
|
||||
func (svcCtx *ServiceContext) Close() {
|
||||
svcCtx.DB.Close()
|
||||
}
|
20
internal/types/types.go
Normal file
20
internal/types/types.go
Normal file
@ -0,0 +1,20 @@
|
||||
// Code generated by goctl. DO NOT EDIT.
|
||||
package types
|
||||
|
||||
type ExpandReq struct {
|
||||
Snowflake string `path:"snowflake"`
|
||||
}
|
||||
|
||||
type ExpandResp struct {
|
||||
RedirectUrl string `json:"redirectUrl"`
|
||||
}
|
||||
|
||||
type ShortenReq struct {
|
||||
RedirectUrl string `json:"redirectUrl"`
|
||||
Secret string `json:"secret,optional"`
|
||||
ExpiresIn int64 `json:"expiresIn,optional"`
|
||||
}
|
||||
|
||||
type ShortenResp struct {
|
||||
Id string `json:"id"`
|
||||
}
|
Reference in New Issue
Block a user