Skip to content

Commit

Permalink
Added version flag
Browse files Browse the repository at this point in the history
  • Loading branch information
svera committed Jan 20, 2025
1 parent 55e9c19 commit 03eddfa
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 65 deletions.
40 changes: 21 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ On first run, Coreander will index the documents in your library, creating a dat

Every time is run, the application scans the library folder only for documents not yet indexed and adds them to the index. You can force to index all documents wether they were previously indexed or not by passing the `--force-indexing` flag or setting the environment variable `FORCE_INDEXING` to `true`.

Even if the application is still indexing entries, you can access its web interface right away. Just open a web browser and go to `localhost:3000` (replace `localhost` with the hostname / IP address of the machine where the server is running if you want to access it from another system). It is possible to change the listening port just executing the application with the `--port` flag or the `PORT` environment variable (e. g. `coreandexr --port 4000` or `PORT=4000 coreander`)
Even if the application is still indexing entries, you can access its web interface right away. Just open a web browser and go to `localhost:3000` (replace `localhost` with the hostname / IP address of the machine where the server is running if you want to access it from another system). It is possible to change the listening port just executing the application with the `-p` or `--port` flags, or the `PORT` environment variable (e. g. `coreander -p 4000` or `PORT=4000 coreander`)

### Setting up an Internet-facing server

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

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` flag or the `REQUIRE_AUTH=true` environment variable.
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 `-a` or `--require-auth` flags, or the `REQUIRE_AUTH=true` environment variable.

On first run, Coreander creates an admin user with the following credentials:

Expand All @@ -122,23 +122,25 @@ In case both a flag and its equivalent environment variable are passed, flag tak

|Flag|Environment variable|Description|
|----|--------------------|-----------|
|`--lib-path` |`LIB_PATH` | Absolute path to the folder containing the documents.
|`--port` |`PORT` | Port number in which the webserver listens for requests. Defaults to 3000.
|`--batch-size` |`BATCH_SIZE` | Number of documents persisted by the indexer in one write operation. Defaults to 100.
|`--cover-max-width` |`COVER_MAX_WIDTH` | Maximum horizontal size for documents cover thumbnails in pixels. Defaults to 600.
|`--force-indexing` |`FORCE_INDEXING` | Whether to force indexing already indexed documents or not. Defaults to false.
|`--smtp-server` |`SMTP_SERVER` | Address of the send mail server.
|`--smtp-port` |`SMTP_PORT` | Port number of the send mail server. Defaults to 587.
|`--smtp-user` |`SMTP_USER` | User to authenticate against the SMTP server.
|`--smtp-password` |`SMTP_PASSWORD` | User's password to authenticate against the SMTP server.
|`--jwt-secret` |`JWT_SECRET` | String to use to sign JWTs.
|`--require-auth` |`REQUIRE_AUTH` | Require authentication to access the application if true. Defaults to false.
|`--min-password-length` |`MIN_PASSWORD_LENGTH` | Minimum length acceptable for passwords. Defaults to 5.
|`--words-per-minute` |`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` |`SESSION_TIMEOUT` | Specifies the maximum time a user session may last, in hours. Floating-point values are allowed. Defaults to 24 hours.
|`--recovery-timeout` |`RECOVERY_TIMEOUT` | Specifies the maximum time a user recovery link may last, in hours. Floating-point values are allowed. Defaults to 2 hours.
|`--upload-document-max-size` |`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.
|`--fqdn` |`FQDN` | Domain name of the server. If Coreander is listening to a non-standard HTTP / HTTPS port, include it using a colon (e. g. example.com:3000). Defaults to `localhost`.
|`--lib-path` |`LIB_PATH` | Absolute path to the folder containing the documents.
|`-p` or `--port` |`PORT` | Port number in which the webserver listens for requests. Defaults to 3000.
|`-b` or `--batch-size` |`BATCH_SIZE` | Number of documents persisted by the indexer in one write operation. Defaults to 100.
|`--cover-max-width` |`COVER_MAX_WIDTH` | Maximum horizontal size for documents cover thumbnails in pixels. Defaults to 600.
|`-f` or `--force-indexing` |`FORCE_INDEXING` | Whether to force indexing already indexed documents or not. Defaults to false.
|`--smtp-server` |`SMTP_SERVER` | Address of the send mail server.
|`--smtp-port` |`SMTP_PORT` | Port number of the send mail server. Defaults to 587.
|`--smtp-user` |`SMTP_USER` | User to authenticate against the SMTP server.
|`--smtp-password` |`SMTP_PASSWORD` | User's password to authenticate against the SMTP server.
|`-s` or `--jwt-secret` |`JWT_SECRET` | String to use to sign JWTs.
|`-a` or `--require-auth` |`REQUIRE_AUTH` | Require authentication to access the application if true. Defaults to false.
|`--min-password-length` |`MIN_PASSWORD_LENGTH` | Minimum length acceptable for passwords. Defaults to 5.
|`--words-per-minute` |`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` |`SESSION_TIMEOUT` | Specifies the maximum time a user session may last, in hours. Floating-point values are allowed. Defaults to 24 hours.
|`--recovery-timeout` |`RECOVERY_TIMEOUT` | Specifies the maximum time a user recovery link may last, in hours. Floating-point values are allowed. Defaults to 2 hours.
|`-u` or `--upload-document-max-size` |`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.
|`-d` or `--fqdn` |`FQDN` | Domain name of the server. If Coreander is listening to a non-standard HTTP / HTTPS port, include it using a colon (e. g. example.com:3000). Defaults to `localhost`.
|`-v` or `--version` | | Show version number.


## Screenshots

Expand Down
14 changes: 7 additions & 7 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ type Config struct {
// LibPath holds the absolute path to the folder containing the documents
LibPath string `arg:"" env:"LIB_PATH" help:"Absolute path to the folder containing the documents." type:"path"`
// FQDN stores the domain name of the server. If the server is listening on a non-standard HTTP / HTTPS port, include it using a colon (e. g. example.com:3000)
FQDN string `env:"FQDN" default:"localhost" name:"fqdn" help:"Domain name of the server. If the server is listening on a non-standard HTTP / HTTPS port, include it using a colon (e. g. example:3000)"`
FQDN string `env:"FQDN" short:"d" default:"localhost" name:"fqdn" help:"Domain name of the server. If the server is listening on a non-standard HTTP / HTTPS port, include it using a colon (e. g. example:3000)"`
// Port defines the port number in which the webserver listens for requests
Port int `env:"PORT" default:"3000" help:"Port number in which the webserver listens for requests"`
Port int `env:"PORT" short:"p" default:"3000" help:"Port number in which the webserver listens for requests"`
// BatchSize indicates the number of documents persisted by the indexer in one operation
BatchSize int `env:"BATCH_SIZE" default:"100" name:"batch-size" help:"Number of documents persisted by the indexer in one operation"`
BatchSize int `env:"BATCH_SIZE" short:"b" default:"100" name:"batch-size" help:"Number of documents persisted by the indexer in one operation"`
// CoverMaxWidth sets the maximum horizontal size for documents cover thumbnails in pixels
CoverMaxWidth int `env:"COVER_MAX_WIDTH" default:"600" name:"cover-max-width" help:"Maximum horizontal size for documents cover thumbnails in pixels"`
// ForceIndexing signals whether to force indexing already indexed documents or not
ForceIndexing bool `env:"FORCE_INDEXING" default:"false" name:"force-indexing" help:"Force indexing already indexed documents"`
ForceIndexing bool `env:"FORCE_INDEXING" short:"f" default:"false" name:"force-indexing" help:"Force indexing already indexed documents"`
// SmtpServer points to the address of the send mail server
SmtpServer string `env:"SMTP_SERVER" name:"smtp-server" help:"Address of the send mail server"`
// SmtpPort defines the port in which the mail server listens for requests
Expand All @@ -23,9 +23,9 @@ type Config struct {
// SmtpUser holds the password to authenticate against the SMTP server
SmtpPassword string `env:"SMTP_PASSWORD" name:"smtp-password" help:"Password to authenticate against the SMTP server"`
// JwtSecret stores the string to use to sign JWTs
JwtSecret string `env:"JWT_SECRET" name:"jwt-secret" help:"String to use to sign JWTs"`
JwtSecret string `env:"JWT_SECRET" short:"s" name:"jwt-secret" help:"String to use to sign JWTs"`
// RequireAuth is a switch to enable the application to require authentication to access any route if true
RequireAuth bool `env:"REQUIRE_AUTH" default:"false" name:"require-auth" help:"Require authentication to access any route"`
RequireAuth bool `env:"REQUIRE_AUTH" short:"a" default:"false" name:"require-auth" help:"Require authentication to access any route"`
// MinPasswordLength is the minimum length acceptable for passwords
MinPasswordLength int `env:"MIN_PASSWORD_LENGTH" default:"5" name:"min-password-length" help:"Minimum length acceptable for passwords"`
// WordsPerMinute defines a default words per minute reading speed that will be used for not logged-in users
Expand All @@ -36,5 +36,5 @@ type Config struct {
RecoveryTimeout float64 `env:"RECOVERY_TIMEOUT" default:"2" name:"recovery-timeout" help:"Maximum time a user recovery link may last in hours"`
// 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" default:"20" name:"upload-document-max-size" help:"Maximum document size allowed to be uploaded to the library, in megabytes. Set this to 0 to unlimit upload size."`
UploadDocumentMaxSize int `env:"UPLOAD_DOCUMENT_MAX_SIZE" short:"u" default:"20" name:"upload-document-max-size" help:"Maximum document size allowed to be uploaded to the library, in megabytes. Set this to 0 to unlimit upload size."`
}
6 changes: 3 additions & 3 deletions internal/webserver/embedded/translations/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@
"This action cannot be undone": "Esta acción no se puede deshacer"
"Delete document": "Eliminar documento"
"There was an error deleting the document": "Ocurrió un error al intentar borrar el documento"
"Other documents with similar subjects": "Otros documentos de temática similar"
"Other documents by": "Otros documentos de"
"Other documents in collection \"%s\"": "Otros documentos en la colección \"%s\""
"With similar subjects": "De temática similar"
"Also by": "También de"
"Also in collection \"%s\"": "También en la colección \"%s\""
"See all": "Ver todos"
"Light": "Claro"
"Dark": "Oscuro"
Expand Down
6 changes: 3 additions & 3 deletions internal/webserver/embedded/translations/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@
"This action cannot be undone": "Cette action ne peut pas être annulée"
"Delete document": "supprimer le document"
"There was an error deleting the document": "Une erreur s'est produite lors de la suppression du document"
"Other documents with similar subjects": "Autres documents avec des sujets similaires"
"Other documents by": "Autres documents par"
"Other documents in collection \"%s\"": "Autres documents en collection \"%s\""
"With similar subjects": "Des sujets similaires"
"Also by": "également par"
"Also in collection \"%s\"": "Également dans collection \"%s\""
"See all": "Voir tout"
"Light": "Clair"
"Dark": "Noir"
Expand Down
6 changes: 3 additions & 3 deletions internal/webserver/embedded/views/document.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ <h2>{{t $lang "Unknown author"}}</h2>
{{ $length := len .SameSeries }} {{ if gt $length 0 }}
<section class="row mt-5">
<div class="col-9">
<h4>{{t $lang "Other documents in collection \"%s\"" .Document.Series}}</h4>
<h4>{{t $lang "Also in collection \"%s\"" .Document.Series}}</h4>
</div>
<div class="col-3 text-end">
<a href="/documents?search=SeriesSlug&colon;{{.Document.SeriesSlug}}">
Expand All @@ -84,7 +84,7 @@ <h4>{{t $lang "Other documents in collection \"%s\"" .Document.Series}}</h4>
<section class="row mt-5">
<div class="col-9">
<h4>
{{t $lang "Other documents by"}}
{{t $lang "Also by"}}
{{join $document.Authors ", "}}
</h4>
</div>
Expand All @@ -111,7 +111,7 @@ <h4>
{{ $length := len .SameSubjects }} {{ if gt $length 0 }}
<section class="row mt-5">
<div class="col-9">
<h4>{{t $lang "Other documents with similar subjects"}}</h4>
<h4>{{t $lang "With similar subjects"}}</h4>
</div>
<div class="col-3 text-end">
<a href='/documents?search=SubjectsSlugs&colon;{{join $document.SubjectsSlugs ","}}'>
Expand Down
72 changes: 42 additions & 30 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ const indexPath = "/.coreander/index"
const databasePath = "/.coreander/database.db"

var (
cfg Config
CLIInput struct {
Config
Version kong.VersionFlag `short:"v" name:"version" help:"Get version number."`
}
appFs afero.Fs
idx *index.BleveIndexer
db *gorm.DB
Expand All @@ -36,18 +39,27 @@ var (
)

func init() {
ctx := kong.Parse(&CLIInput, kong.Description(`
Coreander is a document management system which indexes metadata from documents
in a library and allows users to search and read them through a web interface.
`),
kong.Vars{
"version": version,
},
)

if ctx.Error != nil {
log.Fatalf("Error parsing configuration: %s", ctx.Error)
}

log.Printf("Coreander version %s starting\n", version)
homeDir, err = os.UserHomeDir()
if err != nil {
log.Fatal("Error retrieving user home dir")
}

ctx := kong.Parse(&cfg, kong.Name("coreander"), kong.Description("Coreander is a document management system"))
if ctx.Error != nil {
log.Fatalf("Error parsing configuration: %s", ctx.Error)
}
if _, err := os.Stat(cfg.LibPath); os.IsNotExist(err) {
log.Fatalf("Directory '%s' does not exist, exiting", cfg.LibPath)
if _, err := os.Stat(CLIInput.LibPath); os.IsNotExist(err) {
log.Fatalf("Directory '%s' does not exist, exiting", CLIInput.LibPath)
}

metadataReaders = map[string]metadata.Reader{
Expand All @@ -61,62 +73,62 @@ func init() {
appFs = afero.NewOsFs()

indexFile := getIndexFile(appFs)
idx = index.NewBleve(indexFile, appFs, cfg.LibPath, metadataReaders)
db = infrastructure.Connect(homeDir+databasePath, cfg.WordsPerMinute)
idx = index.NewBleve(indexFile, appFs, CLIInput.LibPath, metadataReaders)
db = infrastructure.Connect(homeDir+databasePath, CLIInput.WordsPerMinute)
}

func main() {
defer idx.Close()

go startIndex(idx, cfg.BatchSize, cfg.LibPath)
go startIndex(idx, CLIInput.BatchSize, CLIInput.LibPath)

sender = &infrastructure.NoEmail{}
if cfg.SmtpServer != "" && cfg.SmtpUser != "" && cfg.SmtpPassword != "" {
if CLIInput.SmtpServer != "" && CLIInput.SmtpUser != "" && CLIInput.SmtpPassword != "" {
sender = &infrastructure.SMTP{
Server: cfg.SmtpServer,
Port: cfg.SmtpPort,
User: cfg.SmtpUser,
Password: cfg.SmtpPassword,
Server: CLIInput.SmtpServer,
Port: CLIInput.SmtpPort,
User: CLIInput.SmtpUser,
Password: CLIInput.SmtpPassword,
}
}

webserverConfig := webserver.Config{
Version: version,
MinPasswordLength: cfg.MinPasswordLength,
WordsPerMinute: cfg.WordsPerMinute,
JwtSecret: []byte(cfg.JwtSecret),
FQDN: cfg.FQDN,
Port: cfg.Port,
MinPasswordLength: CLIInput.MinPasswordLength,
WordsPerMinute: CLIInput.WordsPerMinute,
JwtSecret: []byte(CLIInput.JwtSecret),
FQDN: CLIInput.FQDN,
Port: CLIInput.Port,
HomeDir: homeDir,
LibraryPath: cfg.LibPath,
CoverMaxWidth: cfg.CoverMaxWidth,
RequireAuth: cfg.RequireAuth,
UploadDocumentMaxSize: cfg.UploadDocumentMaxSize,
LibraryPath: CLIInput.LibPath,
CoverMaxWidth: CLIInput.CoverMaxWidth,
RequireAuth: CLIInput.RequireAuth,
UploadDocumentMaxSize: CLIInput.UploadDocumentMaxSize,
}

webserverConfig.SessionTimeout, err = time.ParseDuration(fmt.Sprintf("%fh", cfg.SessionTimeout))
webserverConfig.SessionTimeout, err = time.ParseDuration(fmt.Sprintf("%fh", CLIInput.SessionTimeout))
if err != nil {
log.Fatal(fmt.Errorf("wrong value for session timeout"))
}

webserverConfig.RecoveryTimeout, err = time.ParseDuration(fmt.Sprintf("%fh", cfg.RecoveryTimeout))
webserverConfig.RecoveryTimeout, err = time.ParseDuration(fmt.Sprintf("%fh", CLIInput.RecoveryTimeout))
if err != nil {
log.Fatal(fmt.Errorf("wrong value for recovery timeout"))
}

controllers := webserver.SetupControllers(webserverConfig, db, metadataReaders, idx, sender, appFs)
app := webserver.New(webserverConfig, controllers, sender, idx)
if strings.ToLower(cfg.FQDN) == "localhost" {
if strings.ToLower(CLIInput.FQDN) == "localhost" {
fmt.Printf("Warning: using \"localhost\" as FQDN. Links using this FQDN won't be accessible outside this system.\n")
}
log.Printf("Started listening on port %d\n", cfg.Port)
log.Fatal(app.Listen(fmt.Sprintf(":%d", cfg.Port)))
log.Printf("Started listening on port %d\n", CLIInput.Port)
log.Fatal(app.Listen(fmt.Sprintf(":%d", CLIInput.Port)))
}

func startIndex(idx *index.BleveIndexer, batchSize int, libPath string) {
start := time.Now().Unix()
log.Printf("Indexing documents at %s, this can take a while depending on the size of your library.", libPath)
err := idx.AddLibrary(batchSize, cfg.ForceIndexing)
err := idx.AddLibrary(batchSize, CLIInput.ForceIndexing)
if err != nil {
log.Fatal(err)
}
Expand Down

0 comments on commit 03eddfa

Please sign in to comment.