simple-image-hosting 0.1.2__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {simple_image_hosting-0.1.2/simple_image_hosting.egg-info → simple_image_hosting-0.1.4}/PKG-INFO +1 -5
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/README.md +0 -4
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/pyproject.toml +1 -1
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/cli.py +1 -2
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/main.py +51 -23
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/index.html +6 -6
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4/simple_image_hosting.egg-info}/PKG-INFO +1 -5
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/LICENSE +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/setup.cfg +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/__init__.py +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/models.py +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/utils.py +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/app.js +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/styles.css +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/axios.min.js +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/element-plus.css +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/element-plus.full.min.js +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/heic2any.min.js +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/load-image.all.min.js +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/vue.global.prod.js +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/SOURCES.txt +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/dependency_links.txt +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/entry_points.txt +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/requires.txt +0 -0
- {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/top_level.txt +0 -0
{simple_image_hosting-0.1.2/simple_image_hosting.egg-info → simple_image_hosting-0.1.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simple-image-hosting
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Simple image hosting
|
|
5
5
|
Author: pkufool
|
|
6
6
|
License: Apache 2.0
|
|
@@ -148,10 +148,6 @@ server {
|
|
|
148
148
|
client_max_body_size 20m;
|
|
149
149
|
proxy_read_timeout 300s;
|
|
150
150
|
proxy_send_timeout 300s;
|
|
151
|
-
|
|
152
|
-
# HTML 与 API 响应不建议长期缓存
|
|
153
|
-
expires -1;
|
|
154
|
-
add_header Cache-Control "no-store";
|
|
155
151
|
}
|
|
156
152
|
}
|
|
157
153
|
```
|
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
import uvicorn
|
|
8
8
|
|
|
9
|
-
from .main import
|
|
9
|
+
from .main import create_app, hash_password
|
|
10
10
|
from .models import DEFAULT_COMPRESS_QUALITY, User, create_session_factory
|
|
11
11
|
|
|
12
12
|
|
|
@@ -100,7 +100,6 @@ def run_serve(args: argparse.Namespace) -> None:
|
|
|
100
100
|
database_url=args.database_url,
|
|
101
101
|
base_path=args.base_path,
|
|
102
102
|
)
|
|
103
|
-
_bootstrap_admin(app)
|
|
104
103
|
uvicorn.run(app, host=args.host, port=args.port, reload=args.reload)
|
|
105
104
|
|
|
106
105
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import json
|
|
3
|
+
import mimetypes
|
|
3
4
|
import os
|
|
4
5
|
import secrets
|
|
5
6
|
from datetime import datetime
|
|
@@ -9,7 +10,7 @@ from typing import List, Optional
|
|
|
9
10
|
from dotenv import load_dotenv
|
|
10
11
|
from fastapi import Cookie, Depends, FastAPI, File, Form, Header, HTTPException, Query, Request, Response, UploadFile, status
|
|
11
12
|
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
|
12
|
-
|
|
13
|
+
|
|
13
14
|
from pydantic import BaseModel, Field
|
|
14
15
|
from sqlalchemy import and_
|
|
15
16
|
from sqlalchemy.orm import Session
|
|
@@ -131,6 +132,19 @@ def image_disk_path(images_dir: Path, image_id: str, extension: str) -> Path:
|
|
|
131
132
|
return images_dir / f"{generate_uuid_filename(image_id)}.{extension}"
|
|
132
133
|
|
|
133
134
|
|
|
135
|
+
def find_image_file(images_dir: Path, image_id: str) -> Optional[Path]:
|
|
136
|
+
"""Locate an image on disk by scanning the directory, skipping the database."""
|
|
137
|
+
base = images_dir / generate_uuid_filename(image_id)
|
|
138
|
+
parent = base.parent
|
|
139
|
+
if not parent.is_dir():
|
|
140
|
+
return None
|
|
141
|
+
stem = base.name
|
|
142
|
+
for entry in parent.iterdir():
|
|
143
|
+
if entry.is_file() and entry.stem == stem and entry.suffix.lstrip(".") in ALLOWED_EXTENSIONS:
|
|
144
|
+
return entry
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
|
|
134
148
|
def normalize_path_prefix(value: Optional[str]) -> str:
|
|
135
149
|
if not value:
|
|
136
150
|
return ""
|
|
@@ -286,10 +300,7 @@ def create_app(
|
|
|
286
300
|
db_path=None if app.state.database_url else app.state.db_path,
|
|
287
301
|
)
|
|
288
302
|
|
|
289
|
-
|
|
290
|
-
def _startup_bootstrap_admin() -> None:
|
|
291
|
-
_bootstrap_admin(app)
|
|
292
|
-
|
|
303
|
+
_bootstrap_admin(app)
|
|
293
304
|
_register_routes(app)
|
|
294
305
|
if not resolved_base_path:
|
|
295
306
|
return app
|
|
@@ -546,16 +557,21 @@ def _register_routes(app: FastAPI) -> None:
|
|
|
546
557
|
|
|
547
558
|
|
|
548
559
|
@app.get("/image/{image_uuid}", response_class=FileResponse)
|
|
549
|
-
def get_image(image_uuid: str
|
|
550
|
-
|
|
551
|
-
if not
|
|
560
|
+
def get_image(image_uuid: str):
|
|
561
|
+
image_path = find_image_file(app.state.images_dir, image_uuid)
|
|
562
|
+
if not image_path:
|
|
552
563
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Image not found")
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
564
|
+
ext = normalize_image_extension(image_path.suffix.lstrip("."))
|
|
565
|
+
media_type = IMAGE_MEDIA_TYPES.get(ext, "application/octet-stream")
|
|
566
|
+
return FileResponse(path=image_path, media_type=media_type, headers={"Cache-Control": "public, max-age=2592000"})
|
|
567
|
+
# db_image = db.query(Image).filter(Image.id == image_uuid).first()
|
|
568
|
+
# if not db_image:
|
|
569
|
+
# raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Image not found")
|
|
570
|
+
# image_path = image_disk_path(app.state.images_dir, db_image.id, db_image.file_extension)
|
|
571
|
+
# if not image_path.exists():
|
|
572
|
+
# raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Image file not found")
|
|
573
|
+
# media_type = IMAGE_MEDIA_TYPES.get(normalize_image_extension(db_image.file_extension), "application/octet-stream")
|
|
574
|
+
# return FileResponse(path=image_path, media_type=media_type)
|
|
559
575
|
|
|
560
576
|
|
|
561
577
|
@app.get("/download/{image_uuid}")
|
|
@@ -583,16 +599,11 @@ def _register_routes(app: FastAPI) -> None:
|
|
|
583
599
|
image_uuid: str,
|
|
584
600
|
size: int = Query(default=160, ge=48, le=512),
|
|
585
601
|
quality: int = Query(default=75, ge=40, le=95),
|
|
586
|
-
db: Session = Depends(get_db),
|
|
587
602
|
):
|
|
588
|
-
|
|
589
|
-
if not
|
|
603
|
+
image_path = find_image_file(app.state.images_dir, image_uuid)
|
|
604
|
+
if not image_path:
|
|
590
605
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Image not found")
|
|
591
606
|
|
|
592
|
-
image_path = image_disk_path(app.state.images_dir, db_image.id, db_image.file_extension)
|
|
593
|
-
if not image_path.exists():
|
|
594
|
-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Image file not found")
|
|
595
|
-
|
|
596
607
|
try:
|
|
597
608
|
with open(image_path, "rb") as source_file:
|
|
598
609
|
image_data = source_file.read()
|
|
@@ -603,8 +614,14 @@ def _register_routes(app: FastAPI) -> None:
|
|
|
603
614
|
return Response(
|
|
604
615
|
content=thumbnail_data,
|
|
605
616
|
media_type="image/jpeg",
|
|
606
|
-
headers={"Cache-Control": "public, max-age=
|
|
617
|
+
headers={"Cache-Control": "public, max-age=2592000"},
|
|
607
618
|
)
|
|
619
|
+
# db_image = db.query(Image).filter(Image.id == image_uuid).first()
|
|
620
|
+
# if not db_image:
|
|
621
|
+
# raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Image not found")
|
|
622
|
+
# image_path = image_disk_path(app.state.images_dir, db_image.id, db_image.file_extension)
|
|
623
|
+
# if not image_path.exists():
|
|
624
|
+
# raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Image file not found")
|
|
608
625
|
|
|
609
626
|
|
|
610
627
|
def list_user_images(
|
|
@@ -828,7 +845,18 @@ def _register_routes(app: FastAPI) -> None:
|
|
|
828
845
|
if WEB_DIR.exists():
|
|
829
846
|
static_dir = WEB_DIR / "static"
|
|
830
847
|
if static_dir.exists():
|
|
831
|
-
|
|
848
|
+
|
|
849
|
+
@app.get("/static/{file_path:path}", include_in_schema=False)
|
|
850
|
+
def static_file(file_path: str):
|
|
851
|
+
full_path = static_dir / file_path
|
|
852
|
+
if not full_path.exists() or not full_path.is_file():
|
|
853
|
+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
|
854
|
+
media_type = mimetypes.guess_type(str(full_path))[0]
|
|
855
|
+
return FileResponse(
|
|
856
|
+
path=full_path,
|
|
857
|
+
media_type=media_type,
|
|
858
|
+
headers={"Cache-Control": "public, max-age=604800"},
|
|
859
|
+
)
|
|
832
860
|
|
|
833
861
|
|
|
834
862
|
@app.get("/", include_in_schema=False)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<meta name="simple-image-base-path" content="__SIMPLE_IMAGE_BASE_PATH__" />
|
|
8
8
|
<title>Simple Image</title>
|
|
9
|
-
<link rel="stylesheet" href="
|
|
9
|
+
<link rel="stylesheet" href="static/vendor/element-plus.css" />
|
|
10
10
|
<link rel="stylesheet" href="static/styles.css" />
|
|
11
11
|
</head>
|
|
12
12
|
|
|
@@ -403,11 +403,11 @@
|
|
|
403
403
|
</el-dialog>
|
|
404
404
|
</div>
|
|
405
405
|
|
|
406
|
-
<script src="
|
|
407
|
-
<script src="
|
|
408
|
-
<script src="
|
|
409
|
-
<script src="
|
|
410
|
-
<script src="
|
|
406
|
+
<script src="static/vendor/vue.global.prod.js"></script>
|
|
407
|
+
<script src="static/vendor/element-plus.full.min.js"></script>
|
|
408
|
+
<script src="static/vendor/axios.min.js"></script>
|
|
409
|
+
<script src="static/vendor/heic2any.min.js"></script>
|
|
410
|
+
<script src="static/vendor/load-image.all.min.js"></script>
|
|
411
411
|
<script src="static/app.js"></script>
|
|
412
412
|
</body>
|
|
413
413
|
|
{simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4/simple_image_hosting.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simple-image-hosting
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Simple image hosting
|
|
5
5
|
Author: pkufool
|
|
6
6
|
License: Apache 2.0
|
|
@@ -148,10 +148,6 @@ server {
|
|
|
148
148
|
client_max_body_size 20m;
|
|
149
149
|
proxy_read_timeout 300s;
|
|
150
150
|
proxy_send_timeout 300s;
|
|
151
|
-
|
|
152
|
-
# HTML 与 API 响应不建议长期缓存
|
|
153
|
-
expires -1;
|
|
154
|
-
add_header Cache-Control "no-store";
|
|
155
151
|
}
|
|
156
152
|
}
|
|
157
153
|
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/styles.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|