From 54f54fedd7d30794f808f10d340bfea5ecd138a8 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Sat, 11 Jan 2025 20:25:54 +0100 Subject: [PATCH 1/6] Replaced cleanenv with kong --- config.go | 34 +++++++++++++++++----------------- go.mod | 6 +----- go.sum | 16 +++++++--------- main.go | 8 +++++--- 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/config.go b/config.go index b27a385..26e2311 100644 --- a/config.go +++ b/config.go @@ -3,38 +3,38 @@ package main // Config stores the application configuration type Config struct { // LibPath holds the absolute path to the folder containing the documents - LibPath string `env:"LIB_PATH" env-required:"true"` + 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. :3000) - FQDN string `env:"FQDN" env-default:"localhost"` + 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. :3000)"` // Port defines the port number in which the webserver listens for requests - Port int `env:"PORT" env-default:"3000"` + Port int `env:"PORT" 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" env-default:"100"` + BatchSize int `env:"BATCH_SIZE" 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" env-default:"600"` + 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" env-default:"false"` + ForceIndexing bool `env:"FORCE_INDEXING" 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"` + 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 - SmtpPort int `env:"SMTP_PORT" env-default:"587"` + SmtpPort int `env:"SMTP_PORT" default:"587" name:"smtp-port" help:"Port in which the mail server listens for requests"` // SmtpUser holds the user to authenticate against the SMTP server - SmtpUser string `env:"SMTP_USER"` + SmtpUser string `env:"SMTP_USER" name:"smtp-user" help:"User to authenticate against the SMTP server"` // SmtpUser holds the password to authenticate against the SMTP server - SmtpPassword string `env:"SMTP_PASSWORD"` + 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 []byte `env:"JWT_SECRET"` + JwtSecret []byte `env:"JWT_SECRET" 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" env-default:"false"` + RequireAuth bool `env:"REQUIRE_AUTH" 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" env-default:"5"` + 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 - WordsPerMinute float64 `env:"WORDS_PER_MINUTE" env-default:"250"` + WordsPerMinute float64 `env:"WORDS_PER_MINUTE" default:"250" name:"words-per-minute" help:"Default words per minute reading speed that will be used for not logged-in users"` // SessionTimeout specifies the maximum time a user session may last in hours - SessionTimeout float64 `env:"SESSION_TIMEOUT" env-default:"24"` + SessionTimeout float64 `env:"SESSION_TIMEOUT" default:"24" name:"session-timeout" help:"Maximum time a user session may last in hours"` // RecoveryTimeout specifies the maximum time a user recovery link may last in hours - RecoveryTimeout float64 `env:"RECOVERY_TIMEOUT" env-default:"2"` + 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" env-default:"20"` + 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."` } diff --git a/go.mod b/go.mod index 399a18f..2feeab3 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23 toolchain go1.23.0 require ( + github.com/alecthomas/kong v1.6.1 github.com/blevesearch/bleve/v2 v2.4.4 github.com/bmatcuk/doublestar/v4 v4.7.1 github.com/glebarez/sqlite v1.11.0 @@ -14,7 +15,6 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.1 github.com/google/uuid v1.6.0 github.com/hhrutter/tiff v1.0.1 - github.com/ilyakaznacheev/cleanenv v1.5.0 github.com/kovidgoyal/imaging v1.6.3 github.com/magefile/mage v1.15.0 github.com/microcosm-cc/bluemonday v1.0.27 @@ -60,7 +60,6 @@ require ( ) require ( - github.com/BurntSushi/toml v1.4.0 // indirect github.com/PuerkitoBio/goquery v1.10.1 github.com/RoaringBitmap/roaring v1.9.4 // indirect github.com/andybalholm/brotli v1.1.1 // indirect @@ -87,7 +86,6 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/gosimple/slug v1.15.0 - github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -108,6 +106,4 @@ require ( golang.org/x/sys v0.29.0 // indirect google.golang.org/protobuf v1.36.2 // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df - gopkg.in/yaml.v3 v3.0.1 // indirect - olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect ) diff --git a/go.sum b/go.sum index d4f634b..3628695 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,13 @@ -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU= github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY= github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/kong v1.6.1 h1:/7bVimARU3uxPD0hbryPE8qWrS3Oz3kPQoxA/H2NKG8= +github.com/alecthomas/kong v1.6.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= @@ -106,19 +109,16 @@ github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo= github.com/gosimple/slug v1.15.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0= github.com/hhrutter/lzw v1.0.0/go.mod h1:2HC6DJSn/n6iAZfgM3Pg+cP1KxeWc3ezG8bBqW5+WEo= github.com/hhrutter/tiff v1.0.1 h1:MIus8caHU5U6823gx7C6jrfoEvfSTGtEFRiM8/LOzC0= github.com/hhrutter/tiff v1.0.1/go.mod h1:zU/dNgDm0cMIa8y8YwcYBeuEEveI4B0owqHyiPpJPHc= -github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4= -github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= @@ -369,5 +369,3 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ= -olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= diff --git a/main.go b/main.go index 6d70d67..b7feda4 100644 --- a/main.go +++ b/main.go @@ -8,10 +8,10 @@ import ( "time" "github.com/blevesearch/bleve/v2" - "github.com/ilyakaznacheev/cleanenv" "github.com/pirmd/epub" "gorm.io/gorm" + "github.com/alecthomas/kong" "github.com/spf13/afero" "github.com/svera/coreander/v4/internal/index" "github.com/svera/coreander/v4/internal/metadata" @@ -41,8 +41,10 @@ func init() { if err != nil { log.Fatal("Error retrieving user home dir") } - if err = cleanenv.ReadEnv(&cfg); err != nil { - log.Fatalf("Error parsing configuration from environment variables: %s", err) + + ctx := kong.Parse(&cfg, kong.Name("coreander"), kong.Description("Coreander is a document management system")) + if ctx.Error != nil { + log.Fatalf("Error parsing configuration from environment variables: %s", ctx.Error) } if _, err := os.Stat(cfg.LibPath); os.IsNotExist(err) { log.Fatalf("Directory '%s' does not exist, exiting", cfg.LibPath) From 0aeb63d3a705379c9372767b511aa6742469d1d2 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Sun, 12 Jan 2025 12:59:47 +0100 Subject: [PATCH 2/6] Updated readme, use string instead of byte slice for secret --- README.md | 60 +++++++++++++++++++++++++++++++------------------------ config.go | 6 +++--- main.go | 2 +- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index c3bfc0f..97b306b 100644 --- a/README.md +++ b/README.md @@ -58,13 +58,13 @@ Environment="LIB_PATH=" then, start the service with `service coreander start`. You can manage it with the usual commands `start`, `stop` and `status`. Refer to your service manager documentation for more information. -Coreander requires a `LIB_PATH` environment variable to be set, which tells the application where your documents are located. +Coreander requires the absolute path where your documents are located as an argument. You can also pass it through the `LIB_PATH` environment variable. On first run, Coreander will index the documents in your library, creating a database with those entries located at `$home/coreander/index`. Depending on your system's performance and the size of your library this may take a while. Also, the database can grow fairly big, so make sure you have enough free space on disk. -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 setting the environment variable `FORCE_INDEXING` to `true`. +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` environment variable (e. g. `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 `--port` flag or the `PORT` environment variable (e. g. `coreandexr --port 4000` or `PORT=4000 coreander`) ### Setting up an Internet-facing server @@ -87,12 +87,14 @@ Some features rely on having an SMTP email service set up, and won't be availabl * Send document to email. * Recover user password. -You can use any email service that allow sending emails using the SMTP protocol, like [GMX](https://gmx.com/mail). The following environment variables need to be defined: +You can use any email service that allow sending emails using the SMTP protocol, like [GMX](https://gmx.com/mail). The following flags or environment variables need to be defined: -* `SMTP_SERVER`: The URL of the SMTP server to be used, for example `mail.gmx.com`. -* `SMTP_PORT`: The port number used by the email service, defaults to `587`. -* `SMTP_USER`: The user name. -* `SMTP_PASSWORD`: User's password. +|Flag|Environment variable|Description| +|----|--------------------|-----------| +|`--smtp-server` | `SMTP_SERVER` | The URL of the SMTP server to be used, for example `mail.gmx.com`.| +|`--smtp-port` | `SMTP_PORT` | The port number used by the email service, defaults to `587`. | +|`--smtp-user` | `SMTP_USER` | The user name. | +|`--smtp-password`| `SMTP_PASSWORD`| User's password. | #### Send to email @@ -102,7 +104,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=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 `--require-auth` flag or the `REQUIRE_AUTH=true` environment variable. On first run, Coreander creates an admin user with the following credentials: @@ -113,20 +115,26 @@ On first run, Coreander creates an admin user with the following credentials: ### Settings -* `LIB_PATH`: Absolute path to the folder containing the documents. -* `PORT`: Port number in which the webserver listens for requests. Defaults to 3000. -* `BATCH_SIZE`: Number of documents persisted by the indexer in one write operation. Defaults to 100. -* `COVER_MAX_WIDTH`: Maximum horizontal size for documents cover thumbnails in pixels. Defaults to 600. -* `FORCE_INDEXING`: Whether to force indexing already indexed documents or not. Defaults to false. -* `SMTP_SERVER`: Address of the send mail server. -* `SMTP_PORT`: Port number of the send mail server. Defaults to 587. -* `SMTP_USER`: User to authenticate against the SMTP server. -* `SMTP_PASSWORD`: User's password to authenticate against the SMTP server. -* `JWT_SECRET`: String to use to sign JWTs. -* `REQUIRE_AUTH`: Require authentication to access the application if true. Defaults to false. -* `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. -* `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`: 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`: 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`. +Run `coreander -h` or `coreander --help` to see help. + +In case both a flag and its equivalent environment variable are passed, flag takes precendence. + +|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`. diff --git a/config.go b/config.go index 26e2311..955aca3 100644 --- a/config.go +++ b/config.go @@ -4,8 +4,8 @@ package main 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. :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. :3000)"` + // 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)"` // 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"` // BatchSize indicates the number of documents persisted by the indexer in one operation @@ -23,7 +23,7 @@ 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 []byte `env:"JWT_SECRET" name:"jwt-secret" help:"String to use to sign JWTs"` + JwtSecret string `env:"JWT_SECRET" 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"` // MinPasswordLength is the minimum length acceptable for passwords diff --git a/main.go b/main.go index b7feda4..54f9a39 100644 --- a/main.go +++ b/main.go @@ -84,7 +84,7 @@ func main() { Version: version, MinPasswordLength: cfg.MinPasswordLength, WordsPerMinute: cfg.WordsPerMinute, - JwtSecret: cfg.JwtSecret, + JwtSecret: []byte(cfg.JwtSecret), FQDN: cfg.FQDN, Port: cfg.Port, HomeDir: homeDir, From 71b18191b359366797d5c928d605dc5e84fd8323 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Sun, 12 Jan 2025 20:30:17 +0100 Subject: [PATCH 3/6] Update message --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 54f9a39..f476c99 100644 --- a/main.go +++ b/main.go @@ -44,7 +44,7 @@ func init() { ctx := kong.Parse(&cfg, kong.Name("coreander"), kong.Description("Coreander is a document management system")) if ctx.Error != nil { - log.Fatalf("Error parsing configuration from environment variables: %s", ctx.Error) + 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) From 03eddfa7fa44f194417cc539b31c089463992f18 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Mon, 20 Jan 2025 18:44:30 +0100 Subject: [PATCH 4/6] Added version flag --- README.md | 40 ++++++----- config.go | 14 ++-- .../webserver/embedded/translations/es.yml | 6 +- .../webserver/embedded/translations/fr.yml | 6 +- .../webserver/embedded/views/document.html | 6 +- main.go | 72 +++++++++++-------- 6 files changed, 79 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index e76ddd4..04af41a 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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: @@ -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 diff --git a/config.go b/config.go index 955aca3..8325cb2 100644 --- a/config.go +++ b/config.go @@ -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 @@ -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 @@ -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."` } diff --git a/internal/webserver/embedded/translations/es.yml b/internal/webserver/embedded/translations/es.yml index 812b313..ff94d99 100644 --- a/internal/webserver/embedded/translations/es.yml +++ b/internal/webserver/embedded/translations/es.yml @@ -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" diff --git a/internal/webserver/embedded/translations/fr.yml b/internal/webserver/embedded/translations/fr.yml index 2bd9fdf..02ea623 100644 --- a/internal/webserver/embedded/translations/fr.yml +++ b/internal/webserver/embedded/translations/fr.yml @@ -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" diff --git a/internal/webserver/embedded/views/document.html b/internal/webserver/embedded/views/document.html index 84ced8b..c7c864a 100644 --- a/internal/webserver/embedded/views/document.html +++ b/internal/webserver/embedded/views/document.html @@ -63,7 +63,7 @@

{{t $lang "Unknown author"}}

{{ $length := len .SameSeries }} {{ if gt $length 0 }}
-

{{t $lang "Other documents in collection \"%s\"" .Document.Series}}

+

{{t $lang "Also in collection \"%s\"" .Document.Series}}

@@ -84,7 +84,7 @@

{{t $lang "Other documents in collection \"%s\"" .Document.Series}}

- {{t $lang "Other documents by"}} + {{t $lang "Also by"}} {{join $document.Authors ", "}}

@@ -111,7 +111,7 @@

{{ $length := len .SameSubjects }} {{ if gt $length 0 }}
-

{{t $lang "Other documents with similar subjects"}}

+

{{t $lang "With similar subjects"}}

diff --git a/main.go b/main.go index f476c99..e962ac8 100644 --- a/main.go +++ b/main.go @@ -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 @@ -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{ @@ -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) } From 7d32750bb7d76cc26d49fc36be12cc6b5e3a4cd1 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Tue, 21 Jan 2025 12:43:25 +0100 Subject: [PATCH 5/6] SImplify --- config.go => cli_input.go | 7 ++- .../webserver/embedded/translations/fr.yml | 2 +- main.go | 60 +++++++++---------- 3 files changed, 34 insertions(+), 35 deletions(-) rename config.go => cli_input.go (95%) diff --git a/config.go b/cli_input.go similarity index 95% rename from config.go rename to cli_input.go index 8325cb2..8c3e3f0 100644 --- a/config.go +++ b/cli_input.go @@ -1,7 +1,10 @@ package main -// Config stores the application configuration -type Config struct { +import "github.com/alecthomas/kong" + +// CLIInput stores the application configuration +type CLIInput struct { + Version kong.VersionFlag `short:"v" name:"version" help:"Get version number."` // 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) diff --git a/internal/webserver/embedded/translations/fr.yml b/internal/webserver/embedded/translations/fr.yml index 02ea623..cd74c9c 100644 --- a/internal/webserver/embedded/translations/fr.yml +++ b/internal/webserver/embedded/translations/fr.yml @@ -97,7 +97,7 @@ "Delete document": "supprimer le document" "There was an error deleting the document": "Une erreur s'est produite lors de la suppression du document" "With similar subjects": "Des sujets similaires" -"Also by": "également par" +"Also by": "Également par" "Also in collection \"%s\"": "Également dans collection \"%s\"" "See all": "Voir tout" "Light": "Clair" diff --git a/main.go b/main.go index e962ac8..e4434a4 100644 --- a/main.go +++ b/main.go @@ -25,10 +25,7 @@ const indexPath = "/.coreander/index" const databasePath = "/.coreander/database.db" var ( - CLIInput struct { - Config - Version kong.VersionFlag `short:"v" name:"version" help:"Get version number."` - } + input CLIInput appFs afero.Fs idx *index.BleveIndexer db *gorm.DB @@ -39,9 +36,8 @@ 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. + ctx := kong.Parse(&input, 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, @@ -58,8 +54,8 @@ func init() { log.Fatal("Error retrieving user home dir") } - if _, err := os.Stat(CLIInput.LibPath); os.IsNotExist(err) { - log.Fatalf("Directory '%s' does not exist, exiting", CLIInput.LibPath) + if _, err := os.Stat(input.LibPath); os.IsNotExist(err) { + log.Fatalf("Directory '%s' does not exist, exiting", input.LibPath) } metadataReaders = map[string]metadata.Reader{ @@ -73,62 +69,62 @@ func init() { appFs = afero.NewOsFs() indexFile := getIndexFile(appFs) - idx = index.NewBleve(indexFile, appFs, CLIInput.LibPath, metadataReaders) - db = infrastructure.Connect(homeDir+databasePath, CLIInput.WordsPerMinute) + idx = index.NewBleve(indexFile, appFs, input.LibPath, metadataReaders) + db = infrastructure.Connect(homeDir+databasePath, input.WordsPerMinute) } func main() { defer idx.Close() - go startIndex(idx, CLIInput.BatchSize, CLIInput.LibPath) + go startIndex(idx, input.BatchSize, input.LibPath) sender = &infrastructure.NoEmail{} - if CLIInput.SmtpServer != "" && CLIInput.SmtpUser != "" && CLIInput.SmtpPassword != "" { + if input.SmtpServer != "" && input.SmtpUser != "" && input.SmtpPassword != "" { sender = &infrastructure.SMTP{ - Server: CLIInput.SmtpServer, - Port: CLIInput.SmtpPort, - User: CLIInput.SmtpUser, - Password: CLIInput.SmtpPassword, + Server: input.SmtpServer, + Port: input.SmtpPort, + User: input.SmtpUser, + Password: input.SmtpPassword, } } webserverConfig := webserver.Config{ Version: version, - MinPasswordLength: CLIInput.MinPasswordLength, - WordsPerMinute: CLIInput.WordsPerMinute, - JwtSecret: []byte(CLIInput.JwtSecret), - FQDN: CLIInput.FQDN, - Port: CLIInput.Port, + MinPasswordLength: input.MinPasswordLength, + WordsPerMinute: input.WordsPerMinute, + JwtSecret: []byte(input.JwtSecret), + FQDN: input.FQDN, + Port: input.Port, HomeDir: homeDir, - LibraryPath: CLIInput.LibPath, - CoverMaxWidth: CLIInput.CoverMaxWidth, - RequireAuth: CLIInput.RequireAuth, - UploadDocumentMaxSize: CLIInput.UploadDocumentMaxSize, + LibraryPath: input.LibPath, + CoverMaxWidth: input.CoverMaxWidth, + RequireAuth: input.RequireAuth, + UploadDocumentMaxSize: input.UploadDocumentMaxSize, } - webserverConfig.SessionTimeout, err = time.ParseDuration(fmt.Sprintf("%fh", CLIInput.SessionTimeout)) + webserverConfig.SessionTimeout, err = time.ParseDuration(fmt.Sprintf("%fh", input.SessionTimeout)) if err != nil { log.Fatal(fmt.Errorf("wrong value for session timeout")) } - webserverConfig.RecoveryTimeout, err = time.ParseDuration(fmt.Sprintf("%fh", CLIInput.RecoveryTimeout)) + webserverConfig.RecoveryTimeout, err = time.ParseDuration(fmt.Sprintf("%fh", input.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(CLIInput.FQDN) == "localhost" { + if strings.ToLower(input.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", CLIInput.Port) - log.Fatal(app.Listen(fmt.Sprintf(":%d", CLIInput.Port))) + log.Printf("Started listening on port %d\n", input.Port) + log.Fatal(app.Listen(fmt.Sprintf(":%d", input.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, CLIInput.ForceIndexing) + err := idx.AddLibrary(batchSize, input.ForceIndexing) if err != nil { log.Fatal(err) } From b395796ba390f9f37ba4fc446012b37443abc35e Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Tue, 21 Jan 2025 12:51:05 +0100 Subject: [PATCH 6/6] Changed comment --- cli_input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli_input.go b/cli_input.go index 8c3e3f0..89803aa 100644 --- a/cli_input.go +++ b/cli_input.go @@ -2,7 +2,7 @@ package main import "github.com/alecthomas/kong" -// CLIInput stores the application configuration +// CLIInput stores all configuration flags and arguments thant can be passed to the application type CLIInput struct { Version kong.VersionFlag `short:"v" name:"version" help:"Get version number."` // LibPath holds the absolute path to the folder containing the documents