激光材料数据平台 1.3.0
公开数据看板、四位 Key 录入面板、管理后台与三级权限、动态字段配置、 PostgreSQL/SQLite 双支持、数据库备份,以及本版新增的成品图上传与预览。 图片相关: - 录入表单支持相册选图与手机端直接拍照,每条记录最多 6 张 - 浏览器内压缩到最长边 1600 并剥离 EXIF,入库统一为 JPG/PNG - 二进制直接入库,现有备份自动覆盖图片,部署无需新增卷 - 详情页缩略图宫格与全屏 lightbox,公开看板同样可见 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
"""四位 Key 录入面板的图片链路:这是手机端最主要的入口,必须整条走通。"""
|
||||
import hashlib
|
||||
import hmac
|
||||
|
||||
import pytest
|
||||
|
||||
from conftest import png_bytes, record_payload
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def entry(client, admin):
|
||||
"""用管理员账号配好四位 Key,再换成录入会话。"""
|
||||
from app.config import settings
|
||||
from app.main import ENTRY_COOKIE
|
||||
|
||||
admin_client, admin_headers = admin
|
||||
response = admin_client.put("/api/me/entry-key", headers=admin_headers, json={"key": "1379", "password": settings.admin_password})
|
||||
assert response.status_code == 200, response.text
|
||||
client.cookies.clear()
|
||||
assert client.post("/api/entry/auth", json={"key": "1379"}).status_code == 200
|
||||
token = client.cookies.get(ENTRY_COOKIE)
|
||||
csrf = hmac.new(settings.secret.encode(), f"entry:{token}".encode(), hashlib.sha256).hexdigest()
|
||||
return client, {"X-CSRF-Token": csrf}
|
||||
|
||||
|
||||
def entry_upload(client, headers, **kwargs):
|
||||
files = {"file": ("field.png", png_bytes(48, 36), "image/png"), "thumbnail": ("t.png", png_bytes(12, 9), "image/png")}
|
||||
return client.post("/api/entry/uploads/images", headers=headers, files=files, **kwargs)
|
||||
|
||||
|
||||
def test_entry_upload_requires_the_entry_session(client):
|
||||
client.cookies.clear()
|
||||
assert client.post("/api/entry/uploads/images", files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 401
|
||||
|
||||
|
||||
def test_entry_upload_requires_the_entry_csrf_token(entry):
|
||||
client, _ = entry
|
||||
assert client.post("/api/entry/uploads/images", files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 403
|
||||
|
||||
|
||||
def test_admin_csrf_token_is_not_accepted_on_the_entry_endpoint(entry, admin):
|
||||
"""两套会话的 CSRF 派生方式不同,管理端令牌不能拿来打录入接口。"""
|
||||
client, _ = entry
|
||||
_, admin_headers = admin
|
||||
assert client.post("/api/entry/uploads/images", headers=admin_headers, files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 403
|
||||
|
||||
|
||||
def test_entry_record_is_saved_with_its_images(entry):
|
||||
from app.config import settings
|
||||
|
||||
client, headers = entry
|
||||
token = entry_upload(client, headers).json()["data"]["token"]
|
||||
payload = record_payload("LAS-ENTRY-001", image_tokens=[token], confirm_username=settings.admin_username)
|
||||
created = client.post("/api/entry/records", headers=headers, json=payload)
|
||||
assert created.status_code == 200, created.text
|
||||
|
||||
images = client.get(f"/api/public/records/{created.json()['data']['id']}").json()["data"]["images"]
|
||||
assert len(images) == 1 and images[0]["width"] == 48
|
||||
|
||||
|
||||
def test_entry_record_rejects_a_mismatched_username(entry):
|
||||
client, headers = entry
|
||||
token = entry_upload(client, headers).json()["data"]["token"]
|
||||
payload = record_payload("LAS-ENTRY-002", image_tokens=[token], confirm_username="someone-else")
|
||||
assert client.post("/api/entry/records", headers=headers, json=payload).status_code == 403
|
||||
|
||||
|
||||
def test_entry_pending_image_can_be_discarded(entry):
|
||||
client, headers = entry
|
||||
token = entry_upload(client, headers).json()["data"]["token"]
|
||||
assert client.delete(f"/api/entry/uploads/images/{token}", headers=headers).status_code == 200
|
||||
assert client.delete(f"/api/entry/uploads/images/{token}", headers=headers).status_code == 404
|
||||
|
||||
|
||||
def test_pending_image_of_another_account_cannot_be_claimed(client, admin):
|
||||
"""令牌必须同时校验归属,否则拿到别人的令牌就能把图挂到自己的记录上。"""
|
||||
from app.config import settings
|
||||
from app.main import SESSION_COOKIE
|
||||
|
||||
admin_client, admin_headers = admin
|
||||
created = admin_client.post("/api/users", headers=admin_headers, json={"username": "uploader9", "display_name": "录入九", "password": "UploaderPass123", "role": "uploader"})
|
||||
assert created.status_code in (200, 409)
|
||||
|
||||
# 换成另一个账号上传,拿到属于它的令牌
|
||||
client.cookies.clear()
|
||||
client.post("/api/auth/login", data={"username": "uploader9", "password": "UploaderPass123"})
|
||||
other_csrf = hmac.new(settings.secret.encode(), client.cookies.get(SESSION_COOKIE).encode(), hashlib.sha256).hexdigest()
|
||||
stolen = client.post("/api/uploads/images", headers={"X-CSRF-Token": other_csrf}, files={"file": ("mine.png", png_bytes(20, 20), "image/png")}).json()["data"]["token"]
|
||||
|
||||
# 换回管理员,用别人的令牌建记录必须失败
|
||||
client.cookies.clear()
|
||||
client.post("/api/auth/login", data={"username": settings.admin_username, "password": settings.admin_password})
|
||||
admin_csrf = hmac.new(settings.secret.encode(), client.cookies.get(SESSION_COOKIE).encode(), hashlib.sha256).hexdigest()
|
||||
response = client.post("/api/records", headers={"X-CSRF-Token": admin_csrf}, json=record_payload("LAS-STEAL-001", image_tokens=[stolen]))
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_thumbnail_variant_reports_its_own_media_type(entry):
|
||||
from app.config import settings
|
||||
|
||||
client, headers = entry
|
||||
body = client.post("/api/entry/uploads/images", headers=headers, files={
|
||||
"file": ("photo.jpg", open("/System/Library/CoreServices/DefaultBackground.jpg", "rb").read() if False else png_bytes(64, 64), "image/png"),
|
||||
"thumbnail": ("t.png", png_bytes(16, 16), "image/png"),
|
||||
}).json()
|
||||
token = body["data"]["token"]
|
||||
payload = record_payload("LAS-ENTRY-004", image_tokens=[token], confirm_username=settings.admin_username)
|
||||
record_id = client.post("/api/entry/records", headers=headers, json=payload).json()["data"]["id"]
|
||||
image = client.get(f"/api/public/records/{record_id}").json()["data"]["images"][0]
|
||||
assert "variant=thumb" in image["thumb_url"]
|
||||
response = client.get(image["thumb_url"])
|
||||
assert response.status_code == 200 and response.headers["content-type"] == "image/png"
|
||||
assert response.headers["etag"].endswith('-thumb"')
|
||||
Reference in New Issue
Block a user