Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make shorten api #5

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions css/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,19 @@ input[type="checkbox"] {

textarea {
min-height: 140px;
}


.api-key-container {
align-items: center;
display: flex;
}

.api-key {
width: 400px;
height: 30px;
line-height: 30px;
background: #fff;
text-align: center;
margin-right: 8px;
}
8 changes: 8 additions & 0 deletions js/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,12 @@ $(function(){
}
})
}

$('.api-key-show').click(function(){
$('.api-key-show').hide();
$('.api-key-container').toggle();
})
$('#publish-api-key-form').submit(function(){
return confirm('再発行するとこれまで使用していたAPI Keyは使用できなくなります。\nAPI Keyを再発行しますか?');
})
})
24 changes: 24 additions & 0 deletions src/adapters/web/page_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ func (web *Web) pagesHandler(w http.ResponseWriter, r *http.Request) {
renderTemplate(w, r, "/pages.tpl", resp)
}

func (web *Web) shortenByAPIHandler(w http.ResponseWriter, r *http.Request) {
var v engine.ShortenByAPIRequest
if err := json.NewDecoder(r.Body).Decode(&v); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
page, err := web.engine.ShortenByAPI(&v)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
bytes, err := json.Marshal(map[string]string{
"URL": page.URL,
"Slug": page.Slug,
})
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
w.Write(bytes)
}

func (web *Web) shortenHandler(w http.ResponseWriter, r *http.Request) {
user, ok := context.Get(r, "user").(*domain.User)
if !ok {
Expand Down
22 changes: 22 additions & 0 deletions src/adapters/web/user_handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package web

import (
"net/http"

"github.com/gorilla/context"
"github.com/scoville/scvl/src/domain"
)

func (web *Web) publishAPIKeyHandler(w http.ResponseWriter, r *http.Request) {
user, ok := context.Get(r, "user").(*domain.User)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
err := web.engine.UpdateUserAPIKey(user.ID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/pages", http.StatusSeeOther)
}
2 changes: 2 additions & 0 deletions src/adapters/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func (web *Web) Start(port string) error {
r.Handle("/", web.authenticate(web.rootHandler)).Methods(http.MethodGet)
r.Handle("/logout", web.authenticate(web.logoutHandler)).Methods(http.MethodPost)
r.Handle("/shorten", web.authenticate(web.shortenHandler)).Methods(http.MethodPost)
r.HandleFunc("/api/shorten", web.shortenByAPIHandler).Methods(http.MethodPost)
r.Handle("/api/key", web.authenticate(web.publishAPIKeyHandler)).Methods(http.MethodPost)
r.Handle("/pages", web.authenticate(web.pagesHandler)).Methods(http.MethodGet)
r.Handle("/files", web.authenticate(web.filesHandler)).Methods(http.MethodGet)
r.Handle("/files", web.authenticate(web.fileUploadHandler)).Methods(http.MethodPost)
Expand Down
3 changes: 3 additions & 0 deletions src/domain/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ type Page struct {

OGP *OGP `json:"ogp"`
}

// PageSlugLength is the length for shorten url path.
const PageSlugLength = 5
1 change: 1 addition & 0 deletions src/domain/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type User struct {
DeletedAt *time.Time `sql:"index"`
Name string `json:"name"`
Email string `json:"email" gorm:"type:varchar(100);unique_index"`
APIKey string `json:"api_key" gorm:"unique_index"`

Files []*File `json:"files"`
Images []*Image `json:"images"`
Expand Down
36 changes: 33 additions & 3 deletions src/engine/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (e *Engine) Shorten(req *ShortenRequest) (page *domain.Page, err error) {

page = &domain.Page{
UserID: req.UserID,
Slug: domain.GenerateSlug(5),
Slug: domain.GenerateSlug(domain.PageSlugLength),
Title: title,
URL: req.URL,
}
Expand All @@ -81,15 +81,45 @@ func (e *Engine) Shorten(req *ShortenRequest) (page *domain.Page, err error) {
Image: req.Image,
Title: req.Title,
}
err = e.sqlClient.CreateOGP(page.OGP)
if err != nil {
if err = e.sqlClient.CreateOGP(page.OGP); err != nil {
return
}

e.redisClient.SetOGPID(page.Slug, int(page.OGP.ID))
return
}

// ShortenByAPIRequest is the request struct for ShortenByAPI function
type ShortenByAPIRequest struct {
URL string
APIKey string
}

// ShortenByAPI shorten url
func (e *Engine) ShortenByAPI(req *ShortenByAPIRequest) (page *domain.Page, err error) {
if req.URL == "" {
err = errors.New("url cannot be empty")
return
}
if req.APIKey == "" {
err = errors.New("api_key cannot be empty")
return
}
if _, err = e.sqlClient.FindUserByAPIKey(req.APIKey); err != nil {
return
}

page = &domain.Page{
Slug: domain.GenerateSlug(domain.PageSlugLength),
URL: req.URL,
}
if err = e.sqlClient.CreatePage(page); err != nil {
return
}
e.redisClient.SetURL(page.Slug, page.URL)
return
}

// AccessRequest is the request struct for Access function
type AccessRequest struct {
Slug string
Expand Down
2 changes: 2 additions & 0 deletions src/engine/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ type SQLClient interface {
Close() error

FindUser(uint) (*domain.User, error)
FindUserByAPIKey(string) (*domain.User, error)
FindOrCreateUser(domain.User) (*domain.User, error)
UpdateUser(*domain.User, *domain.User) error

FindPages(params *FindPagesRequest) (pages []*domain.Page, count int, err error)
FindPageBySlug(string) (*domain.Page, error)
Expand Down
10 changes: 10 additions & 0 deletions src/engine/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,13 @@ func (e *Engine) FindOrCreateUserByGoogleCode(code string) (*domain.User, error)
}
return e.sqlClient.FindOrCreateUser(u)
}

// UpdateUserAPIKey updates the user
func (e *Engine) UpdateUserAPIKey(userID uint) error {
u, err := e.sqlClient.FindUser(userID)
if err != nil {
return err
}
key := domain.GenerateSlug(40)
return e.sqlClient.UpdateUser(u, &domain.User{APIKey: key})
}
16 changes: 16 additions & 0 deletions src/providers/sql/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ func (c *client) FindUser(id uint) (user *domain.User, err error) {
return
}

func (c *client) FindUserByAPIKey(apiKey string) (user *domain.User, err error) {
user = &domain.User{}

err = c.db.Table(tblUsers).
Where("api_key = ?", apiKey).
First(user).Error

return
}

func (c *client) FindOrCreateUser(params domain.User) (user *domain.User, err error) {
user = &domain.User{}
err = c.db.
Expand All @@ -36,3 +46,9 @@ func (c *client) FindOrCreateUser(params domain.User) (user *domain.User, err er
FirstOrCreate(user).Error
return
}

func (c *client) UpdateUser(user, params *domain.User) error {
return c.db.Table(tblUsers).
Model(user).
Update(params).Error
}
9 changes: 9 additions & 0 deletions templates/pages.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
{{if .LoginURL}}
<a href="{{.LoginURL}}" class="login btn btn-primary btn-lg">ログイン</a>
{{else}}
<button class="btn btn-outline-info btn-xs api-key-show">API Keyを表示</button>
<div class="api-key-container" style="display:none">
<div class="api-key">
{{.User.APIKey}}
</div>
<form action="/api/key" method="post" class="form-inline mt-2 mt-md-0" id="publish-api-key-form">
<button class="btn btn-outline-info my-2 my-sm-0 api-key-publish" type="submit">API Keyを再発行</button>
</form>
</div>
<p>URLの短縮ができます。</p>
<form action="/shorten" method="post">
<div class="form-inline">
Expand Down