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 3 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を再発行しますか?');
})
})
2 changes: 2 additions & 0 deletions src/adapters/web/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ func (web *Web) authenticate(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, _ := web.store.Get(r, "scvl")
userID, ok := session.Values["user_id"].(uint)
userID = 1
ok = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete

if ok {
user, err := web.engine.FindUser(userID)
if err != nil || user.GoogleToken == "" {
Expand Down
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/publish", web.authenticate(web.publishAPIKeyHandler)).Methods(http.MethodPost)
Copy link

@ttexan1 ttexan1 Dec 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path名がこれだと(keyではなく)apiをpublishすることになりそう。
あと、RestでURLを考える場合は基本的に何をするかはAction(get post put delete)で定義されているから、Path中に動詞を入れると冗長になると言われている。
例えば /api/key とかの方がいいかも

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
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
34 changes: 34 additions & 0 deletions src/engine/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,40 @@ func (e *Engine) Shorten(req *ShortenRequest) (page *domain.Page, err error) {
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
}
_, err = e.sqlClient.FindUserByAPIKey(req.APIKey)
if err != nil {
return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if _, err = e.sqlClient.FindUserByAPIKey(req.APIKey); err != nil {
return
}


page = &domain.Page{
Slug: domain.GenerateSlug(5),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2箇所以上で使うんだったら定数を定義するのもあり。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

どの部分のことですか?

URL: req.URL,
}
err = e.sqlClient.CreatePage(page)
if err != nil {
return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/publish" 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