Skip to content

Commit

Permalink
Upload
Browse files Browse the repository at this point in the history
* Upload documents from web interface.
* Fixed word count from epub files using `htm` extension.
  • Loading branch information
svera authored Mar 3, 2024
1 parent 36aaa1f commit db4d450
Show file tree
Hide file tree
Showing 46 changed files with 679 additions and 195 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ A personal documents server, Coreander indexes the documents (EPUBs and PDFs wit
* [Send to email supported](#send-to-email).
* Read indexed epubs and PDFs from Coreander's interface thanks to [epub.js](http://futurepress.org/) and [pdf.js](https://mozilla.github.io/pdf.js/).
* Restrictable access only to registered users.
* Upload documents through the web interface.

## Installation

Expand Down Expand Up @@ -79,7 +80,7 @@ Coreander can send documents through email. This way, you can take advantage of

### User management and access restriction

Coreander distinguish between two kinds of users: regular users and administrator users, with the latter being the only ones with the ability to create new users and delete documents.
Coreander distinguish between two kinds of users: regular users and administrator users, with the latter being the only ones with the ability to create new users and upload and delete documents.

By default, Coreander allow unrestricted access to its contents, except management areas which require an administrator user. To allow access only to registered users in the whole application, pass the `REQUIRE_AUTH=true` environment variable.

Expand All @@ -106,3 +107,5 @@ On first run, Coreander creates an admin user with the following credentials:
* `MIN_PASSWORD_LENGTH`: Minimum length acceptable for passwords. Defaults to 5.
* `WORDS_PER_MINUTE`: Defines a default words per minute reading speed that will be used for not logged-in users. Defaults to 250.
* `SESSION_TIMEOUT`: Specifies the maximum time a user session may last, in hours. Floating-point values are allowed. Defaults to 24 hours.
* `UPLOAD_DOCUMENT_MAX_SIZE`: Maximum document size allowed to be uploaded to the library, in megabytes. Set this to 0 to unlimit upload size. Defaults to 20 megabytes.

3 changes: 3 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ type Config struct {
WordsPerMinute float64 `env:"WORDS_PER_MINUTE" env-default:"250"`
// SessionTimeout specifies the maximum time a user session may last in hours
SessionTimeout float64 `env:"SESSION_TIMEOUT" env-default:"24"`
// UploadDocumentMaxSize is the maximum document size allowed to be uploaded to the library, in megabytes.
// Set this to 0 to unlimit upload size. Defaults to 20 megabytes.
UploadDocumentMaxSize int `env:"UPLOAD_DOCUMENT_MAX_SIZE" env-default:"20"`
}
2 changes: 1 addition & 1 deletion internal/index/bleve_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
func (b *BleveIndexer) AddFile(file string) error {
ext := strings.ToLower(filepath.Ext(file))
if _, ok := b.reader[ext]; !ok {
return nil
return fmt.Errorf("file extension %s not supported", ext)
}
meta, err := b.reader[ext].Metadata(file)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/metadata/epub.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func words(documentFullPath string) (int, error) {
defer r.Close()
count := 0
for _, f := range r.File {
isContent, err := doublestar.PathMatch("O*PS/**/*.*html", f.Name)
isContent, err := doublestar.PathMatch("O*PS/**/*.*htm*", f.Name)
if err != nil {
return 0, err
}
Expand Down
20 changes: 15 additions & 5 deletions internal/webserver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ func SetupControllers(cfg Config, db *gorm.DB, metadataReaders map[string]metada
}

documentsCfg := document.Config{
WordsPerMinute: cfg.WordsPerMinute,
LibraryPath: cfg.LibraryPath,
HomeDir: cfg.HomeDir,
CoverMaxWidth: cfg.CoverMaxWidth,
WordsPerMinute: cfg.WordsPerMinute,
LibraryPath: cfg.LibraryPath,
HomeDir: cfg.HomeDir,
CoverMaxWidth: cfg.CoverMaxWidth,
Hostname: cfg.Hostname,
Port: cfg.Port,
UploadDocumentMaxSize: cfg.UploadDocumentMaxSize,
}

authController := auth.NewController(usersRepository, sender, authCfg, printers)
Expand Down Expand Up @@ -97,6 +100,10 @@ func SetupControllers(cfg Config, db *gorm.DB, metadataReaders map[string]metada
SigningKey: cfg.JwtSecret,
SigningMethod: "HS256",
TokenLookup: "cookie:coreander",
SuccessHandler: func(c *fiber.Ctx) error {
c.Locals("Session", jwtclaimsreader.SessionData(c))
return c.Next()
},
ErrorHandler: func(c *fiber.Ctx, err error) error {
return forbidden(c)
},
Expand All @@ -105,6 +112,10 @@ func SetupControllers(cfg Config, db *gorm.DB, metadataReaders map[string]metada
SigningKey: cfg.JwtSecret,
SigningMethod: "HS256",
TokenLookup: "cookie:coreander",
SuccessHandler: func(c *fiber.Ctx) error {
c.Locals("Session", jwtclaimsreader.SessionData(c))
return c.Next()
},
ErrorHandler: func(c *fiber.Ctx, err error) error {
err = c.Next()
if cfg.RequireAuth {
Expand All @@ -116,7 +127,6 @@ func SetupControllers(cfg Config, db *gorm.DB, metadataReaders map[string]metada
ErrorHandler: func(c *fiber.Ctx, err error) error {
// Status code defaults to 500
code := fiber.StatusInternalServerError

// Retrieve the custom status code if it's a *fiber.Error
var e *fiber.Error
if errors.As(err, &e) {
Expand Down
16 changes: 0 additions & 16 deletions internal/webserver/controller/auth/controller.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package auth

import (
"fmt"
"time"

"github.com/gofiber/fiber/v2"
"github.com/svera/coreander/v3/internal/webserver/model"
"golang.org/x/text/message"
)
Expand Down Expand Up @@ -34,11 +32,6 @@ type Config struct {
SessionTimeout time.Duration
}

const (
defaultHttpPort = 80
defaultHttpsPort = 443
)

func NewController(repository authRepository, sender recoveryEmail, cfg Config, printers map[string]*message.Printer) *Controller {
return &Controller{
repository: repository,
Expand All @@ -47,12 +40,3 @@ func NewController(repository authRepository, sender recoveryEmail, cfg Config,
config: cfg,
}
}

func (a *Controller) urlPort(c *fiber.Ctx) string {
port := fmt.Sprintf(":%d", a.config.Port)
if (a.config.Port == defaultHttpPort && c.Protocol() == "http") ||
(a.config.Port == defaultHttpsPort && c.Protocol() == "https") {
port = ""
}
return port
}
3 changes: 2 additions & 1 deletion internal/webserver/controller/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

"github.com/gofiber/fiber/v2"
"github.com/svera/coreander/v3/internal/webserver/controller"
"github.com/svera/coreander/v3/internal/webserver/infrastructure"
)

Expand All @@ -13,7 +14,7 @@ func (a *Controller) Login(c *fiber.Ctx) error {
"%s://%s%s/%s/reset-password",
c.Protocol(),
a.config.Hostname,
a.urlPort(c),
controller.UrlPort(c.Protocol(), a.config.Port),
c.Params("lang"),
)

Expand Down
3 changes: 2 additions & 1 deletion internal/webserver/controller/auth/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/svera/coreander/v3/internal/webserver/controller"
"github.com/svera/coreander/v3/internal/webserver/infrastructure"
)

Expand All @@ -33,7 +34,7 @@ func (a *Controller) Request(c *fiber.Ctx) error {
"%s://%s%s/%s/reset-password?id=%s",
c.Protocol(),
a.config.Hostname,
a.urlPort(c),
controller.UrlPort(c.Protocol(), a.config.Port),
c.Params("lang"),
user.RecoveryUUID,
)
Expand Down
17 changes: 17 additions & 0 deletions internal/webserver/controller/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package controller

import "fmt"

const (
defaultHttpPort = 80
defaultHttpsPort = 443
)

func UrlPort(protocol string, port int) string {
urlPort := fmt.Sprintf(":%d", port)
if (port == defaultHttpPort && protocol == "http") ||
(port == defaultHttpsPort && protocol == "https") {
urlPort = ""
}
return urlPort
}
12 changes: 8 additions & 4 deletions internal/webserver/controller/document/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type IdxReaderWriter interface {
SameSubjects(slug string, quantity int) ([]index.Document, error)
SameAuthors(slug string, quantity int) ([]index.Document, error)
SameSeries(slug string, quantity int) ([]index.Document, error)
AddFile(file string) error
RemoveFile(file string) error
Documents(IDs []string) (map[string]index.Document, error)
}
Expand All @@ -34,10 +35,13 @@ type highlightsRepository interface {
}

type Config struct {
WordsPerMinute float64
LibraryPath string
HomeDir string
CoverMaxWidth int
WordsPerMinute float64
LibraryPath string
HomeDir string
CoverMaxWidth int
Hostname string
Port int
UploadDocumentMaxSize int
}

type Controller struct {
Expand Down
9 changes: 6 additions & 3 deletions internal/webserver/controller/document/detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/gofiber/fiber/v2"
"github.com/svera/coreander/v3/internal/webserver/infrastructure"
"github.com/svera/coreander/v3/internal/webserver/jwtclaimsreader"
"github.com/svera/coreander/v3/internal/webserver/model"
)

func (d *Controller) Detail(c *fiber.Ctx) error {
Expand All @@ -17,7 +17,11 @@ func (d *Controller) Detail(c *fiber.Ctx) error {
emailSendingConfigured = false
}

session := jwtclaimsreader.SessionData(c)
var session model.User
if val, ok := c.Locals("Session").(model.User); ok {
session = val
}

if session.WordsPerMinute > 0 {
d.config.WordsPerMinute = session.WordsPerMinute
}
Expand Down Expand Up @@ -61,7 +65,6 @@ func (d *Controller) Detail(c *fiber.Ctx) error {
"Document": document,
"EmailSendingConfigured": emailSendingConfigured,
"EmailFrom": d.sender.From(),
"Session": session,
"SameSeries": sameSeries,
"SameAuthors": sameAuthors,
"SameSubjects": sameSubjects,
Expand Down
9 changes: 5 additions & 4 deletions internal/webserver/controller/document/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/svera/coreander/v3/internal/index"
"github.com/svera/coreander/v3/internal/result"
"github.com/svera/coreander/v3/internal/webserver/infrastructure"
"github.com/svera/coreander/v3/internal/webserver/jwtclaimsreader"
"github.com/svera/coreander/v3/internal/webserver/model"
"github.com/svera/coreander/v3/internal/webserver/view"
)
Expand All @@ -23,7 +22,11 @@ func (d *Controller) Search(c *fiber.Ctx) error {
page = 1
}

session := jwtclaimsreader.SessionData(c)
var session model.User
if val, ok := c.Locals("Session").(model.User); ok {
session = val
}

if session.WordsPerMinute > 0 {
d.config.WordsPerMinute = session.WordsPerMinute
}
Expand All @@ -46,7 +49,6 @@ func (d *Controller) Search(c *fiber.Ctx) error {
"Title": "Search results",
"EmailSendingConfigured": emailSendingConfigured,
"EmailFrom": d.sender.From(),
"Session": session,
"WordsPerMinute": d.config.WordsPerMinute,
}, "layout")
}
Expand Down Expand Up @@ -77,7 +79,6 @@ func (d *Controller) Search(c *fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Count": count,
"Title": "Coreander",
"Session": session,
"Highlights": docsSortedByHighlightedDate,
"EmailSendingConfigured": emailSendingConfigured,
"EmailFrom": d.sender.From(),
Expand Down
Loading

0 comments on commit db4d450

Please sign in to comment.