Skip to content

Commit 282338e

Browse files
committed
Update Readme and add password protection (#13)
* add setup guide for clients * add projekt link to header and load proxy password from env * Implent password checking * fix docker compose --------- Signed-off-by: lowikian <38097264+bugmaschine@users.noreply.github.com> Co-authored-by: Lowikian <lowikian@einfachzocken.eu>
1 parent 9651077 commit 282338e

File tree

6 files changed

+100
-39
lines changed

6 files changed

+100
-39
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ Because:
1919
* Stores it in a local PostgreSQL database
2020
* Saves media files in your own S3-compatible bucket
2121

22+
## Features
23+
24+
* **Transparent Proxy**: Redirects all requests through e6-cache, then to your chosen instance.
25+
* **Passive Caching**: Automatically caches every post you view.
26+
* **Local Storage**: Stores metadata in a local PostgreSQL database and media files in your own S3-compatible bucket.
27+
* **Customizable**: Easily configurable to work with any e621-compatible instance.
28+
* **Self-Hosted**: Runs on your own server, giving you full control over your data.
29+
* **Authentication**: Supports authentication for secure access, even when exposed to the world.
30+
2231
## Dev Setup
2332

2433
### Start DB and S3 Storage
@@ -67,6 +76,7 @@ For The Wolf's Stash, it just reports the host not being supported.
6776
### Other Clients
6877
Feel free to open a PR to add documentation for other clients.
6978

79+
7080
## Speed Comparison (Speed depends on your internet, and database speed)
7181

7282
### Image 1:

dev/docker-compose.yml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ services:
2929
image: minio/mc
3030
depends_on:
3131
- minio
32-
entrypoint: >
33-
/bin/sh -c "
34-
sleep 5; # Waits for startup
35-
/usr/bin/mc alias set minio http://minio:9000 minioadmin minioadmin;
36-
/usr/bin/mc mb --ignore-existing minio/e6cache-media;
37-
exit 0;
38-
"
32+
entrypoint:
33+
- /bin/sh
34+
- -c
35+
- |
36+
echo "Waiting for MinIO to be ready..."
37+
until /usr/bin/mc alias set minio http://minio:9000 minioadmin minioadmin; do
38+
echo "MinIO not ready yet, retrying in 2s..."
39+
sleep 2
40+
done
41+
/usr/bin/mc alias set minio http://minio:9000 minioadmin minioadmin;
42+
/usr/bin/mc mb minio/e6cache-media --ignore-existing;
43+
exit 0;
3944
4045
volumes:
4146
dev_db_data:

docker-compose.yml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ services:
4141
# Proxy settings
4242
PROXY_URL: http://localhost:8080 # Set this to the Server IP / URL, as otherwise the proxy will not work.
4343
E6_BASE: https://e621.net
44+
PROXY_AUTH: "" # Leave empty to disable proxy auth append like this to your username "Username:YourPassword"
4445
ports:
4546
- "8080:8080" # Point this to an Reverse Proxy and set the Proxy Url acordingly.
4647

@@ -49,13 +50,18 @@ services:
4950
image: minio/mc
5051
depends_on:
5152
- minio
52-
entrypoint: >
53-
/bin/sh -c "
54-
sleep 5; # Waits for startup
55-
/usr/bin/mc alias set minio http://minio:9000 minioadmin minioadmin;
56-
/usr/bin/mc mb --ignore-existing minio/e6cache-media;
57-
exit 0;
58-
"
53+
entrypoint:
54+
- /bin/sh
55+
- -c
56+
- |
57+
echo "Waiting for MinIO to be ready..."
58+
until /usr/bin/mc alias set minio http://minio:9000 minioadmin minioadmin; do
59+
echo "MinIO not ready yet, retrying in 2s..."
60+
sleep 2
61+
done
62+
/usr/bin/mc alias set minio http://minio:9000 minioadmin minioadmin;
63+
/usr/bin/mc mb minio/e6cache-media --ignore-existing;
64+
exit 0;
5965
volumes:
6066
db_data:
6167
minio_data:

src/.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ S3_REGION=us-east-1
1616

1717
# Proxy settings
1818
PROXY_URL=http://localhost:8080
19-
E6_BASE=https://e621.net
19+
E6_BASE=https://e621.net
20+
PROXY_AUTH="" # Leave empty to disable proxy auth append like this to your username "Username:YourPassword"

src/http_calls.go

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,51 @@ func makeProxyLink(original string) string {
5353
}
5454

5555
func proxyAndTransform(c *gin.Context) {
56+
57+
logging.Debug("Headers: ", c.Request.Header)
58+
59+
auth := c.Request.Header.Get("Authorization")
60+
61+
requestUsername := ""
62+
// if proxy auth is enabled, check for auth header
63+
if PROXY_AUTH != "" {
64+
if auth == "" {
65+
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
66+
return
67+
}
68+
69+
// an auth header should normaly look like this: base64 hashed username:password
70+
// ours should look like this username:proxy_auth:password
71+
// the idea is that the user enters their username and then a colon and then the proxy auth. Because most clients dont support colons in the api key, but will accept it in user names
72+
auth = strings.TrimPrefix(auth, "Basic ")
73+
decodedAuth, err := base64.StdEncoding.DecodeString(auth)
74+
if err != nil {
75+
logging.Error("Error decoding auth header: ", err)
76+
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
77+
return
78+
}
79+
// parse it into username, password and proxy auth
80+
authParts := strings.Split(string(decodedAuth), ":")
81+
suppliedProxyAuth := authParts[1] // if you want to check the password, set it to 2
82+
if len(authParts) != 3 || suppliedProxyAuth != PROXY_AUTH {
83+
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
84+
return
85+
}
86+
87+
c.Request.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(authParts[0]+":"+authParts[2])))
88+
} else {
89+
90+
if auth == "" {
91+
requestUsername = "anonymous"
92+
}
93+
94+
// parse the Authorization header
95+
auth = strings.ReplaceAll(auth, "Basic ", "")
96+
decodedAuth, _ := base64.URLEncoding.DecodeString(auth)
97+
splitAuth := strings.Split(string(decodedAuth), ":")
98+
requestUsername = splitAuth[0] // 0 is username, 1 is api key
99+
}
100+
56101
// Construct full target URL
57102
originalURL := baseURL + c.Request.URL.Path
58103
if c.Request.URL.RawQuery != "" {
@@ -77,10 +122,9 @@ func proxyAndTransform(c *gin.Context) {
77122
copyHeaders(c.Request.Header, req.Header)
78123

79124
// useragent stuff
80-
setUseragent(c, req)
125+
setUseragent(requestUsername, req)
81126

82127
logging.Debug("Host: ", c.Request.Host)
83-
logging.Debug("Headers: ", c.Request.Header)
84128
logging.Debug("Proxied Headers: ", req.Header)
85129

86130
// Perform request
@@ -322,7 +366,9 @@ func proxyFile(c *gin.Context) {
322366

323367
// download the image from the api
324368
req, _ := http.NewRequest("GET", string(url), nil)
325-
setUseragent(c, req)
369+
370+
// i dont think the username is required for downloading files
371+
setUseragent("", req)
326372
client := &http.Client{}
327373
resp, err := client.Do(req)
328374
if err != nil {
@@ -380,30 +426,15 @@ func ProcessPost(c *gin.Context, post *Post) {
380426
post.Sample.URL = makeProxyLink(post.Sample.URL)
381427
}
382428

383-
func setUseragent(c *gin.Context, req *http.Request) {
384-
auth := c.Request.Header.Get("Authorization")
385-
429+
func setUseragent(username string, req *http.Request) {
386430
var useragent string
387-
388-
if auth == "" {
389-
useragent = useragentBase
390-
req.Header.Set("User-Agent", useragent)
391-
return
392-
}
393-
394-
// parse the Authorization header
395-
auth = strings.ReplaceAll(auth, "Basic ", "")
396-
decodedAuth, _ := base64.URLEncoding.DecodeString(auth)
397-
splitAuth := strings.Split(string(decodedAuth), ":") // 0 is username, 1 is api key
398-
399-
if splitAuth[0] == "" {
431+
if len(username) < 1 { // if it's too small, then forget about it
400432
useragent = useragentBase
401433
req.Header.Set("User-Agent", useragent)
402434
return
403435
}
404436

405-
//finally assemble the useragent
406-
useragent = useragentBase + " (Request made on behalf of " + splitAuth[0] + ")"
437+
useragent = useragentBase + " (Request made on behalf of " + username + ")"
407438

408439
req.Header.Set("User-Agent", useragent)
409440
}

src/main.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func isDebug() bool {
2121
var (
2222
debugMode string = "false"
2323
Database DB
24-
useragentBase = "e6-cache"
24+
useragentBase = "e6-cache (https://github.com/bugmaschine/e6-cache)"
2525
port = ":8080"
2626
Key []byte // gets randomly generated every launch, and used for signing the urls.
2727
maxCacheAge = 1 * time.Hour // idk what's a good value, but 1 hours seems enough
@@ -45,8 +45,9 @@ var (
4545
DB_PASS string
4646

4747
// Proxy settings
48-
PROXY_URL string
49-
baseURL string
48+
PROXY_URL string
49+
baseURL string
50+
PROXY_AUTH string
5051
)
5152

5253
func loadEnv() {
@@ -74,6 +75,13 @@ func loadEnv() {
7475
// Proxy Settings
7576
PROXY_URL = os.Getenv("PROXY_URL")
7677
baseURL = os.Getenv("E6_BASE")
78+
PROXY_AUTH = os.Getenv("PROXY_AUTH")
79+
80+
if PROXY_AUTH != "" {
81+
logging.Debug("Proxy auth is enabled with key: ", PROXY_AUTH)
82+
} else {
83+
logging.Debug("Proxy auth is disabled")
84+
}
7785

7886
}
7987

0 commit comments

Comments
 (0)