v2 #43
|
@ -0,0 +1,76 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"owl-blogs/app/repository"
|
||||
"owl-blogs/config"
|
||||
"owl-blogs/domain/model"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type AuthorService struct {
|
||||
repo repository.AuthorRepository
|
||||
config config.Config
|
||||
}
|
||||
|
||||
func NewAuthorService(repo repository.AuthorRepository, config config.Config) *AuthorService {
|
||||
return &AuthorService{repo: repo, config: config}
|
||||
}
|
||||
|
||||
func hashPassword(password string) (string, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func (s *AuthorService) Create(name string, password string) (*model.Author, error) {
|
||||
hash, err := hashPassword(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.Create(name, hash)
|
||||
}
|
||||
|
||||
func (s *AuthorService) FindByName(name string) (*model.Author, error) {
|
||||
return s.repo.FindByName(name)
|
||||
}
|
||||
|
||||
func (s *AuthorService) Authenticate(name string, password string) bool {
|
||||
author, err := s.repo.FindByName(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
err = bcrypt.CompareHashAndPassword([]byte(author.PasswordHash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (s *AuthorService) getSecretKey() string {
|
||||
return s.config.SECRET_KEY()
|
||||
}
|
||||
|
||||
func (s *AuthorService) CreateToken(name string) (string, error) {
|
||||
hash := sha256.New()
|
||||
_, err := hash.Write([]byte(name + s.getSecretKey()))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s.%x", name, hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func (s *AuthorService) ValidateToken(token string) bool {
|
||||
parts := strings.Split(token, ".")
|
||||
witness := parts[len(parts)-1]
|
||||
name := strings.Join(parts[:len(parts)-1], ".")
|
||||
|
||||
hash := sha256.New()
|
||||
_, err := hash.Write([]byte(name + s.getSecretKey()))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fmt.Sprintf("%x", hash.Sum(nil)) == witness
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package app_test
|
||||
|
||||
import (
|
||||
"owl-blogs/app"
|
||||
"owl-blogs/infra"
|
||||
"owl-blogs/test"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testConfig struct {
|
||||
}
|
||||
|
||||
func (c *testConfig) SECRET_KEY() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
func getAutherService() *app.AuthorService {
|
||||
db := test.NewMockDb()
|
||||
authorRepo := infra.NewDefaultAuthorRepo(db)
|
||||
authorService := app.NewAuthorService(authorRepo, &testConfig{})
|
||||
return authorService
|
||||
|
||||
}
|
||||
|
||||
func TestAuthorCreate(t *testing.T) {
|
||||
authorService := getAutherService()
|
||||
author, err := authorService.Create("test", "test")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test", author.Name)
|
||||
require.NotEmpty(t, author.PasswordHash)
|
||||
require.NotEqual(t, "test", author.PasswordHash)
|
||||
}
|
||||
|
||||
func TestAuthorFindByName(t *testing.T) {
|
||||
authorService := getAutherService()
|
||||
_, err := authorService.Create("test", "test")
|
||||
require.NoError(t, err)
|
||||
author, err := authorService.FindByName("test")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test", author.Name)
|
||||
require.NotEmpty(t, author.PasswordHash)
|
||||
require.NotEqual(t, "test", author.PasswordHash)
|
||||
}
|
||||
|
||||
func TestAuthorAuthenticate(t *testing.T) {
|
||||
authorService := getAutherService()
|
||||
_, err := authorService.Create("test", "test")
|
||||
require.NoError(t, err)
|
||||
require.True(t, authorService.Authenticate("test", "test"))
|
||||
require.False(t, authorService.Authenticate("test", "test1"))
|
||||
require.False(t, authorService.Authenticate("test1", "test"))
|
||||
}
|
||||
|
||||
func TestAuthorCreateToken(t *testing.T) {
|
||||
authorService := getAutherService()
|
||||
_, err := authorService.Create("test", "test")
|
||||
require.NoError(t, err)
|
||||
token, err := authorService.CreateToken("test")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, token)
|
||||
require.NotEqual(t, "test", token)
|
||||
}
|
||||
|
||||
func TestAuthorValidateToken(t *testing.T) {
|
||||
authorService := getAutherService()
|
||||
_, err := authorService.Create("test", "test")
|
||||
require.NoError(t, err)
|
||||
token, err := authorService.CreateToken("test")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, authorService.ValidateToken(token))
|
||||
require.False(t, authorService.ValidateToken(token[:len(token)-2]))
|
||||
require.False(t, authorService.ValidateToken("test"))
|
||||
require.False(t, authorService.ValidateToken("test.test"))
|
||||
require.False(t, authorService.ValidateToken(strings.Replace(token, "test", "test1", 1)))
|
||||
}
|
|
@ -17,3 +17,8 @@ type BinaryRepository interface {
|
|||
Create(name string, data []byte) (*model.BinaryFile, error)
|
||||
FindById(id string) (*model.BinaryFile, error)
|
||||
}
|
||||
|
||||
type AuthorRepository interface {
|
||||
Create(name string, passwordHash string) (*model.Author, error)
|
||||
FindByName(name string) (*model.Author, error)
|
||||
}
|
||||
|
|
|
@ -4,46 +4,64 @@ import (
|
|||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"owl-blogs/app"
|
||||
"owl-blogs/domain/model"
|
||||
"owl-blogs/infra"
|
||||
"owl-blogs/test"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testDbName() string {
|
||||
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
b := make([]rune, 6)
|
||||
for i := range b {
|
||||
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
||||
func getUserToken(service *app.AuthorService) string {
|
||||
_, err := service.Create("test", "test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return "/tmp/" + string(b) + ".db"
|
||||
token, err := service.CreateToken("test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
func TestEditorFormGet(t *testing.T) {
|
||||
db := infra.NewSqliteDB(testDbName())
|
||||
app := App(db).FiberApp
|
||||
db := test.NewMockDb()
|
||||
owlApp := App(db)
|
||||
app := owlApp.FiberApp
|
||||
token := getUserToken(owlApp.AuthorService)
|
||||
|
||||
req := httptest.NewRequest("GET", "/editor/ImageEntry", nil)
|
||||
req.AddCookie(&http.Cookie{Name: "token", Value: token})
|
||||
resp, err := app.Test(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestEditorFormPost(t *testing.T) {
|
||||
dbName := testDbName()
|
||||
db := infra.NewSqliteDB(dbName)
|
||||
func TestEditorFormGetNoAuth(t *testing.T) {
|
||||
db := test.NewMockDb()
|
||||
owlApp := App(db)
|
||||
app := owlApp.FiberApp
|
||||
|
||||
req := httptest.NewRequest("GET", "/editor/ImageEntry", nil)
|
||||
req.AddCookie(&http.Cookie{Name: "token", Value: "invalid"})
|
||||
resp, err := app.Test(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 302, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestEditorFormPost(t *testing.T) {
|
||||
db := test.NewMockDb()
|
||||
owlApp := App(db)
|
||||
app := owlApp.FiberApp
|
||||
token := getUserToken(owlApp.AuthorService)
|
||||
repo := infra.NewEntryRepository(db, owlApp.Registry)
|
||||
binRepo := infra.NewBinaryFileRepo(db)
|
||||
|
||||
|
@ -67,6 +85,7 @@ func TestEditorFormPost(t *testing.T) {
|
|||
|
||||
req := httptest.NewRequest("POST", "/editor/ImageEntry", body)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.AddCookie(&http.Cookie{Name: "token", Value: token})
|
||||
resp, err := app.Test(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 302, resp.StatusCode)
|
||||
|
@ -84,3 +103,34 @@ func TestEditorFormPost(t *testing.T) {
|
|||
require.Equal(t, fileBytes, bin.Data)
|
||||
|
||||
}
|
||||
|
||||
func TestEditorFormPostNoAuth(t *testing.T) {
|
||||
db := test.NewMockDb()
|
||||
owlApp := App(db)
|
||||
app := owlApp.FiberApp
|
||||
|
||||
fileDir, _ := os.Getwd()
|
||||
fileName := "../../test/fixtures/test.png"
|
||||
filePath := path.Join(fileDir, fileName)
|
||||
|
||||
file, err := os.Open(filePath)
|
||||
require.NoError(t, err)
|
||||
defer file.Close()
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
part, _ := writer.CreateFormFile("ImageId", filepath.Base(file.Name()))
|
||||
io.Copy(part, file)
|
||||
part, _ = writer.CreateFormField("Content")
|
||||
io.WriteString(part, "test content")
|
||||
writer.Close()
|
||||
|
||||
req := httptest.NewRequest("POST", "/editor/ImageEntry", body)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.AddCookie(&http.Cookie{Name: "token", Value: "invalid"})
|
||||
resp, err := app.Test(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 302, resp.StatusCode)
|
||||
require.Contains(t, resp.Header.Get("Location"), "/auth/login")
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"owl-blogs/app"
|
||||
"owl-blogs/config"
|
||||
"owl-blogs/domain/model"
|
||||
"owl-blogs/infra"
|
||||
"owl-blogs/web"
|
||||
|
@ -10,16 +11,20 @@ import (
|
|||
const DbPath = "owlblogs.db"
|
||||
|
||||
func App(db infra.Database) *web.WebApp {
|
||||
registry := app.NewEntryTypeRegistry()
|
||||
config := config.NewConfig()
|
||||
|
||||
registry := app.NewEntryTypeRegistry()
|
||||
registry.Register(&model.ImageEntry{})
|
||||
|
||||
entryRepo := infra.NewEntryRepository(db, registry)
|
||||
binRepo := infra.NewBinaryFileRepo(db)
|
||||
authorRepo := infra.NewDefaultAuthorRepo(db)
|
||||
|
||||
entryService := app.NewEntryService(entryRepo)
|
||||
binaryService := app.NewBinaryFileService(binRepo)
|
||||
return web.NewWebApp(entryService, registry, binaryService)
|
||||
authorService := app.NewAuthorService(authorRepo, config)
|
||||
|
||||
return web.NewWebApp(entryService, registry, binaryService, authorService)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package config
|
||||
|
||||
import "os"
|
||||
|
||||
type Config interface {
|
||||
SECRET_KEY() string
|
||||
}
|
||||
|
||||
type EnvConfig struct {
|
||||
secretKey string
|
||||
}
|
||||
|
||||
func getEnvOrPanic(key string) string {
|
||||
value, set := os.LookupEnv(key)
|
||||
if !set {
|
||||
panic("Environment variable " + key + " is not set")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func NewConfig() Config {
|
||||
return &EnvConfig{
|
||||
secretKey: getEnvOrPanic("OWL_SECRET_KEY"),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *EnvConfig) SECRET_KEY() string {
|
||||
return c.secretKey
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package model
|
||||
|
||||
type Author struct {
|
||||
Name string
|
||||
PasswordHash string
|
||||
}
|
3
go.mod
3
go.mod
|
@ -24,6 +24,7 @@ require (
|
|||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.47.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -58,6 +58,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
|
@ -83,6 +85,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package infra
|
||||
|
||||
import (
|
||||
"owl-blogs/domain/model"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type sqlAuthor struct {
|
||||
Name string `db:"name"`
|
||||
PasswordHash string `db:"password_hash"`
|
||||
}
|
||||
|
||||
type DefaultAuthorRepo struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
func NewDefaultAuthorRepo(db Database) *DefaultAuthorRepo {
|
||||
sqlxdb := db.Get()
|
||||
|
||||
// Create table if not exists
|
||||
sqlxdb.MustExec(`
|
||||
CREATE TABLE IF NOT EXISTS authors (
|
||||
name TEXT PRIMARY KEY,
|
||||
password_hash TEXT NOT NULL
|
||||
);
|
||||
`)
|
||||
|
||||
return &DefaultAuthorRepo{
|
||||
db: sqlxdb,
|
||||
}
|
||||
}
|
||||
|
||||
// FindByName implements repository.AuthorRepository.
|
||||
func (r *DefaultAuthorRepo) FindByName(name string) (*model.Author, error) {
|
||||
var author sqlAuthor
|
||||
err := r.db.Get(&author, "SELECT * FROM authors WHERE name = ?", name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.Author{
|
||||
Name: author.Name,
|
||||
PasswordHash: author.PasswordHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create implements repository.AuthorRepository.
|
||||
func (r *DefaultAuthorRepo) Create(name string, passwordHash string) (*model.Author, error) {
|
||||
author := sqlAuthor{
|
||||
Name: name,
|
||||
PasswordHash: passwordHash,
|
||||
}
|
||||
_, err := r.db.NamedExec("INSERT INTO authors (name, password_hash) VALUES (:name, :password_hash)", author)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.Author{
|
||||
Name: author.Name,
|
||||
PasswordHash: author.PasswordHash,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package infra_test
|
||||
|
||||
import (
|
||||
"owl-blogs/app/repository"
|
||||
"owl-blogs/infra"
|
||||
"owl-blogs/test"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupAutherRepo() repository.AuthorRepository {
|
||||
db := test.NewMockDb()
|
||||
repo := infra.NewDefaultAuthorRepo(db)
|
||||
return repo
|
||||
}
|
||||
|
||||
func TestAuthorRepoCreate(t *testing.T) {
|
||||
repo := setupAutherRepo()
|
||||
|
||||
author, err := repo.Create("name", "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
author, err = repo.FindByName(author.Name)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, author.Name, "name")
|
||||
require.Equal(t, author.PasswordHash, "password")
|
||||
}
|
||||
|
||||
func TestAuthorRepoNoSideEffect(t *testing.T) {
|
||||
repo := setupAutherRepo()
|
||||
|
||||
author, err := repo.Create("name1", "password1")
|
||||
require.NoError(t, err)
|
||||
|
||||
author2, err := repo.Create("name2", "password2")
|
||||
require.NoError(t, err)
|
||||
|
||||
author, err = repo.FindByName(author.Name)
|
||||
require.NoError(t, err)
|
||||
author2, err = repo.FindByName(author2.Name)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, author.Name, "name1")
|
||||
require.Equal(t, author.PasswordHash, "password1")
|
||||
require.Equal(t, author2.Name, "name2")
|
||||
require.Equal(t, author2.PasswordHash, "password2")
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
OWL_SECRET_KEY=test-secret-key \
|
||||
go test -v -coverprofile=coverage.out ./...
|
27
web/app.go
27
web/app.go
|
@ -2,6 +2,7 @@ package web
|
|||
|
||||
import (
|
||||
"owl-blogs/app"
|
||||
"owl-blogs/web/middleware"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
@ -11,9 +12,15 @@ type WebApp struct {
|
|||
EntryService *app.EntryService
|
||||
BinaryService *app.BinaryService
|
||||
Registry *app.EntryTypeRegistry
|
||||
AuthorService *app.AuthorService
|
||||
}
|
||||
|
||||
func NewWebApp(entryService *app.EntryService, typeRegistry *app.EntryTypeRegistry, binService *app.BinaryService) *WebApp {
|
||||
func NewWebApp(
|
||||
entryService *app.EntryService,
|
||||
typeRegistry *app.EntryTypeRegistry,
|
||||
binService *app.BinaryService,
|
||||
authorService *app.AuthorService,
|
||||
) *WebApp {
|
||||
app := fiber.New()
|
||||
|
||||
indexHandler := NewIndexHandler(entryService)
|
||||
|
@ -25,15 +32,20 @@ func NewWebApp(entryService *app.EntryService, typeRegistry *app.EntryTypeRegist
|
|||
editorListHandler := NewEditorListHandler(typeRegistry)
|
||||
editorHandler := NewEditorHandler(entryService, typeRegistry, binService)
|
||||
|
||||
// Login
|
||||
app.Get("/auth/login", loginHandler.HandleGet)
|
||||
app.Post("/auth/login", loginHandler.HandlePost)
|
||||
|
||||
// Editor
|
||||
editor := app.Group("/editor")
|
||||
editor.Use(middleware.NewAuthMiddleware(authorService).Handle)
|
||||
editor.Get("/", editorListHandler.Handle)
|
||||
editor.Get("/:editor/", editorHandler.HandleGet)
|
||||
editor.Post("/:editor/", editorHandler.HandlePost)
|
||||
|
||||
// app.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
||||
app.Get("/", indexHandler.Handle)
|
||||
app.Get("/lists/:list/", listHandler.Handle)
|
||||
// Editor
|
||||
app.Get("/editor/auth/", loginHandler.HandleGet)
|
||||
app.Post("/editor/auth/", loginHandler.HandlePost)
|
||||
app.Get("/editor/", editorListHandler.Handle)
|
||||
app.Get("/editor/:editor/", editorHandler.HandleGet)
|
||||
app.Post("/editor/:editor/", editorHandler.HandlePost)
|
||||
// Media
|
||||
app.Get("/media/*filepath", mediaHandler.Handle)
|
||||
// RSS
|
||||
|
@ -57,6 +69,7 @@ func NewWebApp(entryService *app.EntryService, typeRegistry *app.EntryTypeRegist
|
|||
EntryService: entryService,
|
||||
Registry: typeRegistry,
|
||||
BinaryService: binService,
|
||||
AuthorService: authorService,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,16 @@ type EditorHandler struct {
|
|||
registry *app.EntryTypeRegistry
|
||||
}
|
||||
|
||||
func NewEditorHandler(entryService *app.EntryService, registry *app.EntryTypeRegistry, binService *app.BinaryService) *EditorHandler {
|
||||
return &EditorHandler{entrySvc: entryService, registry: registry, binSvc: binService}
|
||||
func NewEditorHandler(
|
||||
entryService *app.EntryService,
|
||||
registry *app.EntryTypeRegistry,
|
||||
binService *app.BinaryService,
|
||||
) *EditorHandler {
|
||||
return &EditorHandler{
|
||||
entrySvc: entryService,
|
||||
registry: registry,
|
||||
binSvc: binService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *EditorHandler) paramToEntry(c *fiber.Ctx) (model.Entry, error) {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"owl-blogs/app"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type AuthMiddleware struct {
|
||||
authorService *app.AuthorService
|
||||
}
|
||||
|
||||
func NewAuthMiddleware(authorService *app.AuthorService) *AuthMiddleware {
|
||||
return &AuthMiddleware{authorService: authorService}
|
||||
}
|
||||
|
||||
func (m *AuthMiddleware) Handle(c *fiber.Ctx) error {
|
||||
// get token from cookie
|
||||
token := c.Cookies("token")
|
||||
if token == "" {
|
||||
return c.Redirect("/auth/login")
|
||||
}
|
||||
|
||||
// check token
|
||||
valid := m.authorService.ValidateToken(token)
|
||||
if !valid {
|
||||
return c.Redirect("/auth/login")
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
Loading…
Reference in New Issue