Skip to content

Commit 3a350c6

Browse files
committed
first commit
0 parents  commit 3a350c6

File tree

10 files changed

+423
-0
lines changed

10 files changed

+423
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pmcompose
2+
docker-compose.yaml
3+
docker-compose.yml
4+
vendor/
5+
dist/
6+
.vscode/
7+
*.tar.gz

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) Eugen Ciur <eugen@papermerge.com> and other contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Generate Docker Compose for Papermerge DMS
2+
3+

ask/ask.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package ask
2+
3+
import (
4+
"bufio"
5+
"crypto/subtle"
6+
"fmt"
7+
"os"
8+
"os/user"
9+
"strconv"
10+
"strings"
11+
12+
"golang.org/x/term"
13+
)
14+
15+
func ReadInput(reader *bufio.Reader) string {
16+
input, _ := reader.ReadString('\n')
17+
return strings.TrimSpace(input)
18+
}
19+
20+
func WebAppPort(defaultValue int) (int, error) {
21+
reader := bufio.NewReader(os.Stdin)
22+
23+
fmt.Println(
24+
`The port on which the Papermerge DMS webserver
25+
will listen for incoming connections.`)
26+
27+
fmt.Printf("Port [%d]:", defaultValue)
28+
appPort := ReadInput(reader)
29+
if appPort != "" {
30+
result, err := strconv.Atoi(appPort)
31+
32+
if err != nil {
33+
return -1, err
34+
}
35+
36+
return result, nil
37+
}
38+
39+
return defaultValue, nil
40+
}
41+
42+
func AppVersion(defaultValue string) (string, error) {
43+
reader := bufio.NewReader(os.Stdin)
44+
45+
fmt.Println("Papermerge DMS version")
46+
47+
fmt.Printf("Version [%s]:", defaultValue)
48+
version := ReadInput(reader)
49+
if version == "" {
50+
return defaultValue, nil
51+
}
52+
53+
return version, nil
54+
}
55+
56+
func LoginCredentials() (*Credentials, error) {
57+
reader := bufio.NewReader(os.Stdin)
58+
59+
fmt.Println("Specify initial login credentials.")
60+
61+
creds := Credentials{}
62+
var username string
63+
64+
currentUser, err := user.Current()
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
fmt.Printf("Username [%s]:", currentUser.Username)
70+
username = ReadInput(reader)
71+
if username == "" {
72+
creds.Username = currentUser.Username
73+
} else {
74+
creds.Username = username
75+
}
76+
77+
passwordMatch := false
78+
79+
for passwordMatch == false {
80+
fmt.Print("Password:")
81+
password1, err := term.ReadPassword(int(os.Stdin.Fd()))
82+
if err != nil {
83+
return nil, err
84+
}
85+
if len(password1) == 0 {
86+
fmt.Println("Cannot be empty")
87+
continue
88+
}
89+
fmt.Println()
90+
fmt.Print("Password (again):")
91+
password2, err := term.ReadPassword(int(os.Stdin.Fd()))
92+
fmt.Println()
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
passwordMatch = comparePasswords(password1, password2)
98+
99+
if passwordMatch {
100+
creds.Password = string(password1)
101+
break
102+
} else {
103+
fmt.Println("Password did not match")
104+
}
105+
}
106+
107+
return &creds, nil
108+
}
109+
110+
func StorageBackend() (*S3StorageBackend, error) {
111+
fmt.Println(`
112+
S3 Storage
113+
===========
114+
115+
When using S3 storage, Papermerge will upload/download documents
116+
to/from your private S3 location. This may be useful for example
117+
when you plan run multiple webapp containers or you want
118+
to use CDN (currently works only with AWS CloudFront) for serving
119+
document files.
120+
`)
121+
122+
reader := bufio.NewReader(os.Stdin)
123+
fmt.Print("Would you use S3 storage? (yes no) [no]:")
124+
125+
yesno := ""
126+
127+
for yesno != "yes" || yesno != "no" {
128+
yesno := ReadInput(reader)
129+
130+
if yesno == "" || yesno == "no" {
131+
return nil, nil
132+
}
133+
134+
if yesno != "yes" {
135+
fmt.Print("Answer can be either yes or no")
136+
}
137+
138+
if yesno == "yes" {
139+
break
140+
}
141+
}
142+
143+
backend := S3StorageBackend{}
144+
fmt.Print("AWS negion name? [eu-central-1]:")
145+
region := ReadInput(reader)
146+
if region == "" {
147+
region = "eu-central-1"
148+
}
149+
backend.AWSRegionName = region
150+
151+
fmt.Print("S3 bucket name:")
152+
bucketName := ReadInput(reader)
153+
backend.S3BucketName = bucketName
154+
155+
fmt.Print("AWS_ACCESS_KEY_ID:")
156+
accessKeyID := ReadInput(reader)
157+
backend.AWSAccessKeyID = accessKeyID
158+
159+
fmt.Print("AWS_SECRET_ACCESS_KEY:")
160+
secretAccessKey := ReadInput(reader)
161+
backend.AWSSecretAccessKey = secretAccessKey
162+
163+
return &backend, nil
164+
165+
}
166+
167+
func comparePasswords(password1, password2 []byte) bool {
168+
// Use subtle.ConstantTimeCompare for secure comparison
169+
return subtle.ConstantTimeCompare(password1, password2) == 1
170+
}

ask/types.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package ask
2+
3+
type Credentials struct {
4+
Username string
5+
Password string
6+
Email string
7+
}
8+
9+
type S3StorageBackend struct {
10+
S3BucketName string
11+
AWSRegionName string
12+
AWSAccessKeyID string
13+
AWSSecretAccessKey string
14+
}

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module github.com/papermerge/pmcompose
2+
3+
go 1.24.2
4+
5+
require (
6+
golang.org/x/sys v0.32.0 // indirect
7+
golang.org/x/term v0.31.0 // indirect
8+
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
2+
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
3+
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
4+
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=

pmcompose.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"text/template"
7+
8+
"github.com/papermerge/pmcompose/ask"
9+
"github.com/papermerge/pmcompose/utils"
10+
)
11+
12+
type ComposeConfig struct {
13+
SecretKey string
14+
AppVersion string
15+
UserLoginCredentials ask.Credentials
16+
WebAppPort int
17+
S3StorageBackend ask.S3StorageBackend
18+
}
19+
20+
func main() {
21+
22+
fmt.Println(`
23+
###################################################
24+
### Papermerge DMS docker compose generator ###
25+
###################################################
26+
`)
27+
28+
cfg := ComposeConfig{}
29+
30+
version, err := ask.AppVersion("3.5")
31+
32+
if err != nil {
33+
fmt.Printf("Error: %v\n", err)
34+
return
35+
}
36+
37+
cfg.AppVersion = version
38+
39+
appPort, err := ask.WebAppPort(12000)
40+
41+
if err != nil {
42+
fmt.Printf("Error: %v\n", err)
43+
return
44+
}
45+
46+
cfg.WebAppPort = appPort
47+
48+
creds, err := ask.LoginCredentials()
49+
50+
if err != nil {
51+
fmt.Printf("Error: %v\n", err)
52+
return
53+
}
54+
55+
cfg.UserLoginCredentials = *creds
56+
57+
s3StorageBackend, err := ask.StorageBackend()
58+
59+
if err != nil {
60+
fmt.Printf("Error: %v\n", err)
61+
return
62+
}
63+
64+
if s3StorageBackend != nil {
65+
cfg.S3StorageBackend = *s3StorageBackend
66+
}
67+
68+
secretKey, err := utils.GenerateSecretString(32)
69+
if err != nil {
70+
fmt.Printf("Error: %v\n", err)
71+
return
72+
}
73+
cfg.SecretKey = secretKey
74+
75+
tmpl, err := template.ParseFiles("pmcompose_templates/docker-compose.yaml.tmpl")
76+
if err != nil {
77+
fmt.Println("Error loading template:", err)
78+
return
79+
}
80+
81+
outputFile, err := os.Create("docker-compose.yml")
82+
if err != nil {
83+
fmt.Println("Error creating docker-compose.yml:", err)
84+
return
85+
}
86+
defer outputFile.Close()
87+
88+
err = tmpl.Execute(outputFile, cfg)
89+
if err != nil {
90+
fmt.Println("Error executing template:", err)
91+
return
92+
}
93+
94+
fmt.Println("✅ docker-compose.yml generated successfully.")
95+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
services:
2+
webapp:
3+
image: papermerge/papermerge:{{ .AppVersion }}
4+
environment:
5+
PAPERMERGE__SECURITY__SECRET_KEY: "{{ .SecretKey }}"
6+
PAPERMERGE__AUTH__USERNAME: {{.UserLoginCredentials.Username }}
7+
PAPERMERGE__AUTH__PASSWORD: {{.UserLoginCredentials.Password }}
8+
PAPERMERGE__DATABASE__URL: postgresql://coco:jumbo@db:5432/pmgdb
9+
PAPERMERGE__MAIN__MEDIA_ROOT: /var/media/pmg
10+
ports:
11+
- "{{ .WebAppPort }}:80"
12+
depends_on:
13+
- db
14+
- redis
15+
volumes:
16+
- media_root:/var/media/pmg
17+
path_template_worker:
18+
image: papermerge/path-tmpl-worker:0.4
19+
command: worker
20+
environment:
21+
PAPERMERGE__DATABASE__URL: postgresql://coco:jumbo@db:5432/pmgdb
22+
PAPERMERGE__REDIS__URL: redis://redis:6379/0
23+
PATH_TMPL_WORKER_ARGS: "-Q path_tmpl -c 2"
24+
depends_on:
25+
- db
26+
- redis
27+
{{- if ne .S3StorageBackend.S3BucketName "" }}
28+
s3worker:
29+
image: papermerge/s3worker:0.4.3
30+
command: worker
31+
environment:
32+
PAPERMERGE__DATABASE__URL: postgresql://coco:jumbo@db:5432/pmgdb
33+
PAPERMERGE__REDIS__URL: redis://redis:6379/0
34+
PAPERMERGE__MAIN__MEDIA_ROOT: /var/media/pmg
35+
PAPERMERGE__S3__BUCKET_NAME: {{ .S3StorageBackend.S3BucketName }}
36+
S3_WORKER_ARGS: "-Q s3 -c 2"
37+
AWS_REGION_NAME: {{ .S3StorageBackend.AWSRegionName }}
38+
AWS_ACCESS_KEY_ID: {{ .S3StorageBackend.AWSAccessKeyID }}
39+
AWS_SECRET_ACCESS_KEY: {{ .S3StorageBackend.AWSSecretAccessKey }}
40+
depends_on:
41+
- db
42+
- redis
43+
volumes:
44+
- media_root:/var/media/pmg
45+
{{- end }}
46+
db:
47+
image: postgres:16.1
48+
volumes:
49+
- pgdata:/var/lib/postgresql/data/
50+
environment:
51+
POSTGRES_PASSWORD: jumbo
52+
POSTGRES_DB: pmgdb
53+
POSTGRES_USER: coco
54+
healthcheck:
55+
test: pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB
56+
interval: 5s
57+
timeout: 10s
58+
retries: 5
59+
start_period: 10s
60+
redis:
61+
image: bitnami/redis:7.2
62+
ports:
63+
- "6379:6379"
64+
environment:
65+
ALLOW_EMPTY_PASSWORD: "yes"
66+
volumes:
67+
pgdata:
68+
media_root:

0 commit comments

Comments
 (0)