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.
Files changed (25) hide show
  1. {simple_image_hosting-0.1.2/simple_image_hosting.egg-info → simple_image_hosting-0.1.4}/PKG-INFO +1 -5
  2. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/README.md +0 -4
  3. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/pyproject.toml +1 -1
  4. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/cli.py +1 -2
  5. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/main.py +51 -23
  6. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/index.html +6 -6
  7. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4/simple_image_hosting.egg-info}/PKG-INFO +1 -5
  8. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/LICENSE +0 -0
  9. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/setup.cfg +0 -0
  10. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/__init__.py +0 -0
  11. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/models.py +0 -0
  12. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/utils.py +0 -0
  13. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/app.js +0 -0
  14. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/styles.css +0 -0
  15. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/axios.min.js +0 -0
  16. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/element-plus.css +0 -0
  17. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/element-plus.full.min.js +0 -0
  18. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/heic2any.min.js +0 -0
  19. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/load-image.all.min.js +0 -0
  20. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image/web/static/vendor/vue.global.prod.js +0 -0
  21. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/SOURCES.txt +0 -0
  22. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/dependency_links.txt +0 -0
  23. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/entry_points.txt +0 -0
  24. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/requires.txt +0 -0
  25. {simple_image_hosting-0.1.2 → simple_image_hosting-0.1.4}/simple_image_hosting.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple-image-hosting
3
- Version: 0.1.2
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
  ```
@@ -129,10 +129,6 @@ server {
129
129
  client_max_body_size 20m;
130
130
  proxy_read_timeout 300s;
131
131
  proxy_send_timeout 300s;
132
-
133
- # HTML 与 API 响应不建议长期缓存
134
- expires -1;
135
- add_header Cache-Control "no-store";
136
132
  }
137
133
  }
138
134
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simple-image-hosting"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "Simple image hosting"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
 
7
7
  import uvicorn
8
8
 
9
- from .main import _bootstrap_admin, create_app, hash_password
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
- from fastapi.staticfiles import StaticFiles
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
- @app.on_event("startup")
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, db: Session = Depends(get_db)):
550
- db_image = db.query(Image).filter(Image.id == image_uuid).first()
551
- if not db_image:
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
- image_path = image_disk_path(app.state.images_dir, db_image.id, db_image.file_extension)
555
- if not image_path.exists():
556
- raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Image file not found")
557
- media_type = IMAGE_MEDIA_TYPES.get(normalize_image_extension(db_image.file_extension), "application/octet-stream")
558
- return FileResponse(path=image_path, media_type=media_type)
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
- db_image = db.query(Image).filter(Image.id == image_uuid).first()
589
- if not db_image:
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=86400"},
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
- app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
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="https://cdn.bootcdn.net/ajax/libs/element-plus/2.13.6/index.min.css" />
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="https://cdn.bootcdn.net/ajax/libs/vue/3.5.31/vue.global.prod.min.js"></script>
407
- <script src="https://cdn.bootcdn.net/ajax/libs/element-plus/2.13.6/index.full.min.js"></script>
408
- <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.14.0/axios.min.js"></script>
409
- <script src="https://cdn.jsdelivr.net/npm/heic2any@0.0.4/dist/heic2any.min.js"></script>
410
- <script src="https://cdn.bootcdn.net/ajax/libs/blueimp-load-image/2.31.0/load-image.all.min.js"></script>
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple-image-hosting
3
- Version: 0.1.2
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
  ```