Skip to content

Commit 182ff20

Browse files
committed
调整分离数据库中的文件操作到储存引擎
1 parent 7ecb17f commit 182ff20

File tree

4 files changed

+119
-68
lines changed

4 files changed

+119
-68
lines changed

fbox/database.py

Lines changed: 31 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,26 @@
55
from fbox.utils import get_now
66
from fbox.files.models import Box, File, IPUser
77
from fbox.files.choices import StatusChoice
8+
from fbox.files.storage import storage
89
from fbox.cards.models import Card
910

1011

1112
class BoxDatabaseMixin:
1213
boxes: dict[str, Box] = {}
1314
expired_boxes: list[Box] = []
1415

15-
def init_boxes(self) -> None:
16-
box_data = settings.DATA_ROOT / "box"
16+
async def init_boxes(self) -> None:
1717
logger.info(f"Initialize boxes")
18+
box_codes = await storage.get_dir_filenames("box")
1819

19-
for box_path in box_data.iterdir():
20-
box_code = box_path.name
21-
box_dir = box_data / box_code
22-
box_json = box_dir / "box.json"
23-
24-
if not box_json.exists():
25-
shutil.rmtree(box_dir)
20+
for box_code in box_codes:
21+
box = await storage.get_box(box_code)
22+
if box is None:
23+
await storage.remove_box(box_code)
2624
continue
2725

28-
box = Box.parse_file(box_json)
29-
3026
if self.check_box_expire(box):
3127
logger.debug(f"Box {box.code} expire")
32-
3328
self.expire_box(box)
3429
continue
3530

@@ -38,27 +33,19 @@ def init_boxes(self) -> None:
3833

3934
logger.info(f"Initialize boxes finishied")
4035

41-
def archive_box(self, box: Box) -> None:
42-
logger.debug(f"Archive box {box.code}")
43-
44-
now = get_now().date().isoformat()
45-
current = settings.DATA_ROOT / "box" / box.code
46-
target = settings.LOGS_ROOT / "box" / box.code / now
47-
target.mkdir(parents=True)
48-
49-
for f in current.iterdir():
50-
shutil.move(f, target)
51-
current.rmdir()
52-
5336
async def clean_expired_boxes(self) -> None:
54-
logger.info(f"Box count {len(self.boxes.keys())}")
55-
logger.info(f"Clean {len(self.expired_boxes)} box")
37+
logger.info(f"Check {len(self.boxes)} box")
38+
box_codes = self.boxes.keys()
39+
for code in box_codes:
40+
box = self.boxes[code]
41+
if self.check_box_expire(box):
42+
self.expire_box(box)
5643

44+
logger.info(f"Box count {len(self.boxes)}, expired {len(self.expired_boxes)}")
5745
for box in self.expired_boxes:
58-
await asyncio.to_thread(self.archive_box, box)
46+
await storage.archive_box(box)
5947

6048
self.expired_boxes.clear()
61-
6249
logger.info(f"Clean box finished")
6350

6451
def check_box_expire(self, box: Box) -> bool:
@@ -93,14 +80,9 @@ def get_boxes(self, expired: bool) -> list[Box]:
9380
return self.expired_boxes
9481
return list(self.boxes.values())
9582

96-
def save_box_file(self, box: Box) -> None:
97-
box_file = settings.DATA_ROOT / "box" / box.code / "box.json"
98-
with open(box_file, "w") as f:
99-
f.write(box.json())
100-
10183
async def save_box(self, box: Box) -> None:
10284
self.boxes[box.code] = box
103-
await asyncio.to_thread(self.save_box_file, box)
85+
await storage.save_box(box)
10486

10587
def expire_box(self, box: Box) -> None:
10688
self.expired_boxes.append(box)
@@ -135,17 +117,19 @@ class CardDatabaseMixin:
135117
cards: dict[str, Card] = {}
136118
expired_cards: list[str] = []
137119

138-
def init_cards(self) -> None:
139-
card_data = settings.DATA_ROOT / "card"
120+
async def init_cards(self) -> None:
140121
logger.info(f"Initialize cards")
141122

142-
for card_path in card_data.iterdir():
143-
card_json = card_data / card_path.name
144-
card = Card.parse_file(card_json)
123+
card_json_names = await storage.get_dir_filenames("card")
124+
125+
for card_json_name in card_json_names:
126+
card_code = card_json_name.split(".")[0]
127+
card = await storage.get_card(card_code)
128+
if card is None:
129+
continue
145130

146131
if self.check_card_expire(card):
147132
logger.debug(f"Card {card.code} expire")
148-
149133
self.expire_card(card)
150134
continue
151135

@@ -158,7 +142,7 @@ def check_card_expire(self, card: Card) -> bool:
158142
now = int(get_now().timestamp())
159143

160144
logger.debug(
161-
f"card {card.code} with count {card.count} passed {now - card.created} seconds"
145+
f"Card {card.code} count {card.count} passed {now - card.created} seconds"
162146
)
163147

164148
if card.created > 0 and (now - card.created) >= (365 * 24 * 3600):
@@ -179,14 +163,8 @@ def get_card(self, code: str) -> Card | None:
179163
return None
180164
return card
181165

182-
def save_card_file(self, card: Card) -> None:
183-
card_file = settings.DATA_ROOT / "card" / f"{card.code}.json"
184-
with open(card_file, "w") as f:
185-
f.write(card.json())
186-
187166
async def save_card(self, card: Card) -> None:
188-
self.cards[card.code] = card
189-
await asyncio.to_thread(self.save_card_file, card)
167+
await storage.save_card(card)
190168

191169
def expire_card(self, card: Card) -> None:
192170
self.expired_cards.append(card.code)
@@ -213,7 +191,6 @@ def clean_expire_ip_user(self) -> None:
213191
f"{ip_user.ip} error {error_expire}, box {box_expire}, file {file_expire}"
214192
)
215193

216-
217194
if error_expire and box_expire and file_expire:
218195
expired.append(ip_user.ip)
219196
else:
@@ -244,21 +221,11 @@ def save_ip_user(self, ip_user: IPUser) -> None:
244221

245222

246223
class Database(BoxDatabaseMixin, CardDatabaseMixin, IPUserDatabaseMixin):
247-
def __init__(self) -> None:
248-
if not settings.DATA_ROOT.exists():
249-
settings.DATA_ROOT.mkdir(parents=True)
250-
251-
box_data = settings.DATA_ROOT / "box"
252-
if not box_data.exists():
253-
box_data.mkdir(parents=True)
254-
else:
255-
self.init_boxes()
256-
257-
card_data = settings.DATA_ROOT / "card"
258-
if not card_data.exists():
259-
card_data.mkdir(parents=True)
260-
else:
261-
self.init_cards()
224+
async def init_db(self) -> None:
225+
await storage.init_root()
226+
227+
await self.init_boxes()
228+
await self.init_cards()
262229

263230

264231
db = Database()

fbox/files/storage.py

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
1-
import asyncio, os, hashlib
1+
import asyncio, os, hashlib, shutil
22
from typing import BinaryIO
33
from pathlib import Path
44
from shutil import disk_usage
55

66
from fastapi import UploadFile
77

88
from fbox import settings
9+
from fbox.utils import get_now
10+
from fbox.files.models import Box
11+
from fbox.cards.models import Card
912

1013

1114
class FileSystemStorage:
1215
CHUNK_SIZE = 256 * 1024
1316

17+
async def init_root(self):
18+
data_root = settings.DATA_ROOT
19+
box_data = data_root / "box"
20+
card_data = data_root / "card"
21+
22+
box_data.mkdir(parents=True, exist_ok=True)
23+
card_data.mkdir(parents=True, exist_ok=True)
24+
25+
logs_root = settings.LOGS_ROOT
26+
box_logs = logs_root / "box"
27+
box_logs.mkdir(parents=True, exist_ok=True)
28+
1429
async def get_filepath(self, code: str, filename: str):
1530
filepath = settings.DATA_ROOT / "box" / code / "files" / filename
1631
exist = True
@@ -75,3 +90,70 @@ async def delete_file(self, filepath: Path):
7590
async def delete_files(self, filepaths: list[Path]):
7691
tasks = [self.delete_file(filepath) for filepath in filepaths]
7792
await asyncio.gather(*tasks)
93+
94+
async def get_dir_filenames(self, dirname: str) -> list[str]:
95+
dirpath = settings.DATA_ROOT / dirname
96+
filenames = []
97+
for path in dirpath.iterdir():
98+
filename = path.name
99+
filenames.append(filename)
100+
return filenames
101+
102+
def _get_box(self, code: str) -> Box | None:
103+
box_json = settings.DATA_ROOT / "box" / code / "box.json"
104+
if box_json.exists():
105+
box = Box.parse_file(box_json)
106+
return box
107+
return None
108+
109+
async def get_box(self, code: str) -> Box | None:
110+
return await asyncio.to_thread(self._get_box, code)
111+
112+
def _save_box(self, box: Box) -> None:
113+
box_file = settings.DATA_ROOT / "box" / box.code / "box.json"
114+
with open(box_file, "w") as f:
115+
f.write(box.json())
116+
117+
async def save_box(self, box: Box) -> None:
118+
await asyncio.to_thread(self._save_box, box)
119+
120+
def _remove_box(self, code: str) -> None:
121+
box_dir = settings.DATA_ROOT / "box" / code
122+
shutil.rmtree(box_dir)
123+
124+
async def remove_box(self, code: str) -> None:
125+
await asyncio.to_thread(self._remove_box, code)
126+
127+
def _archive_box(self, box: Box) -> None:
128+
now = get_now().date().isoformat()
129+
current = settings.DATA_ROOT / "box" / box.code
130+
target = settings.LOGS_ROOT / "box" / box.code / now
131+
target.mkdir(parents=True)
132+
133+
for f in current.iterdir():
134+
shutil.move(f, target)
135+
current.rmdir()
136+
137+
async def archive_box(self, box: Box) -> None:
138+
await asyncio.to_thread(self._archive_box, box)
139+
140+
def _get_card(self, code: str) -> Card | None:
141+
card_json = settings.DATA_ROOT / "card" / f"{code}.json"
142+
if card_json.exists():
143+
card = Card.parse_file(card_json)
144+
return card
145+
return None
146+
147+
async def get_card(self, code: str) -> Card | None:
148+
return await asyncio.to_thread(self._get_card, code)
149+
150+
def _save_card(self, card: Card) -> None:
151+
card_json = settings.DATA_ROOT / "card" / f"{card.code}.json"
152+
with open(card_json, "w") as f:
153+
f.write(card.json())
154+
155+
async def save_card(self, card: Card) -> None:
156+
await asyncio.to_thread(self._save_card, card)
157+
158+
159+
storage = FileSystemStorage()

fbox/files/views.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from fbox.cards.depends import get_card
1818
from fbox.files.models import Box, FileCreate, IPUser, File
1919
from fbox.files.choices import UploadFailChoice, StatusChoice
20-
from fbox.files.storage import FileSystemStorage
20+
from fbox.files.storage import storage
2121
from fbox.files.utils import (
2222
generate_code,
2323
get_ip,
@@ -31,7 +31,6 @@
3131

3232

3333
router = APIRouter(tags=["Files"])
34-
storage = FileSystemStorage()
3534

3635

3736
@router.get("/files/capacity")

fbox/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
async def clean_data():
1313
while True:
14-
logger.info("Runing clean data")
14+
logger.info("Running clean data")
1515

1616
await db.clean_expired_boxes()
1717
db.clean_expire_ip_user()
@@ -23,6 +23,9 @@ async def clean_data():
2323
async def startup():
2424
logger.info("Running startup task")
2525

26+
await db.init_db()
27+
logger.info("Init database complete")
28+
2629
asyncio.create_task(clean_data())
2730

2831
logger.info("Startup task finished")

0 commit comments

Comments
 (0)