Initial commit

This commit is contained in:
2022-08-26 21:10:50 +02:00
parent f5b7072065
commit 2bf97a0194
21 changed files with 1339 additions and 2 deletions

11
internal/config/config.go Normal file
View 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
View 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
View 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
}

View 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)
}
}
}

View 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),
},
},
)
}

View 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)
}
}
}

View 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
}

View 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
}

View 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
View 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"`
}