lfss 0.3.2__py3-none-any.whl → 0.4.0__py3-none-any.whl

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.
lfss/src/database.py CHANGED
@@ -69,7 +69,7 @@ class UserRecord:
69
69
  permission: 'FileReadPermission'
70
70
 
71
71
  def __str__(self):
72
- return f"User {self.username} (id={self.id}, admin={self.is_admin}, created at {self.create_time}, last active at {self.last_active}), storage={self.max_storage}, permission={self.permission}"
72
+ return f"User {self.username} (id={self.id}, admin={self.is_admin}, created at {self.create_time}, last active at {self.last_active}, storage={self.max_storage}, permission={self.permission})"
73
73
 
74
74
  DECOY_USER = UserRecord(0, 'decoy', 'decoy', False, '2021-01-01 00:00:00', '2021-01-01 00:00:00', 0, FileReadPermission.PRIVATE)
75
75
  class UserConn(DBConnBase):
@@ -421,7 +421,7 @@ class FileConn(DBConnBase):
421
421
  new_exists = await self.get_file_record(new_url)
422
422
  if new_exists is not None:
423
423
  raise FileExistsError(f"File {new_url} already exists")
424
- async with self.conn.execute("UPDATE fmeta SET url = ? WHERE url = ?", (new_url, old_url)):
424
+ async with self.conn.execute("UPDATE fmeta SET url = ?, create_time = CURRENT_TIMESTAMP WHERE url = ?", (new_url, old_url)):
425
425
  self.logger.info(f"Moved file {old_url} to {new_url}")
426
426
 
427
427
  async def log_access(self, url: str):
@@ -481,8 +481,9 @@ class FileConn(DBConnBase):
481
481
  await self.conn.execute("DELETE FROM fdata WHERE file_id IN ({})".format(','.join(['?'] * len(file_ids))), file_ids)
482
482
 
483
483
  def validate_url(url: str, is_file = True):
484
- ret = not url.startswith('/') and not ('..' in url) and ('/' in url) and not ('//' in url) \
485
- and not ' ' in url and not url.startswith('\\') and not url.startswith('_') and not url.startswith('.')
484
+ prohibited_chars = ['..', ';', "'", '"', '\\', '\0', '\n', '\r', '\t', '\x0b', '\x0c']
485
+ ret = not url.startswith('/') and not url.startswith('_') and not url.startswith('.')
486
+ ret = ret and not any([c in url for c in prohibited_chars])
486
487
 
487
488
  if not ret:
488
489
  raise InvalidPathError(f"Invalid URL: {url}")
lfss/src/server.py CHANGED
@@ -15,7 +15,7 @@ from .error import *
15
15
  from .log import get_logger
16
16
  from .stat import RequestDB
17
17
  from .config import MAX_BUNDLE_BYTES, MAX_FILE_BYTES
18
- from .utils import ensure_uri_compnents
18
+ from .utils import ensure_uri_compnents, format_last_modified, now_stamp
19
19
  from .database import Database, UserRecord, DECOY_USER, FileRecord, check_user_permission, FileReadPermission
20
20
 
21
21
  logger = get_logger("server", term_level="DEBUG")
@@ -81,6 +81,7 @@ app.add_middleware(
81
81
  @app.middleware("http")
82
82
  async def log_requests(request: Request, call_next):
83
83
 
84
+ request_time_stamp = now_stamp()
84
85
  start_time = time.perf_counter()
85
86
  response: Response = await call_next(request)
86
87
  end_time = time.perf_counter()
@@ -91,6 +92,7 @@ async def log_requests(request: Request, call_next):
91
92
  logger_failed_request.error(f"{request.method} {request.url.path} {response.status_code}")
92
93
 
93
94
  await req_conn.log_request(
95
+ request_time_stamp,
94
96
  request.method, request.url.path, response.status_code, response_time,
95
97
  headers = dict(request.headers),
96
98
  query = dict(request.query_params),
@@ -148,7 +150,8 @@ async def get_file(path: str, download = False, user: UserRecord = Depends(get_c
148
150
  return Response(
149
151
  content=fblob, media_type=media_type, headers={
150
152
  "Content-Disposition": f"{disposition}; filename={fname}",
151
- "Content-Length": str(len(fblob))
153
+ "Content-Length": str(len(fblob)),
154
+ "Last-Modified": format_last_modified(file_record.create_time)
152
155
  }
153
156
  )
154
157
 
lfss/src/stat.py CHANGED
@@ -13,7 +13,7 @@ class RequestDB:
13
13
  await self.conn.execute('''
14
14
  CREATE TABLE IF NOT EXISTS requests (
15
15
  id INTEGER PRIMARY KEY AUTOINCREMENT,
16
- time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
16
+ time FLOAT DEFAULT (strftime('%s', 'now')),
17
17
  method TEXT,
18
18
  path TEXT,
19
19
  headers TEXT,
@@ -43,7 +43,8 @@ class RequestDB:
43
43
  await self.commit()
44
44
 
45
45
  async def log_request(
46
- self, method: str, path: str,
46
+ self, time: float,
47
+ method: str, path: str,
47
48
  status: int, duration: float,
48
49
  headers: Optional[Any] = None,
49
50
  query: Optional[Any] = None,
@@ -57,9 +58,9 @@ class RequestDB:
57
58
  client = str(client)
58
59
  async with self.conn.execute('''
59
60
  INSERT INTO requests (
60
- method, path, headers, query, client, duration, request_size, response_size, status
61
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
62
- ''', (method, path, headers, query, client, duration, request_size, response_size, status)) as cursor:
61
+ time, method, path, headers, query, client, duration, request_size, response_size, status
62
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
63
+ ''', (time, method, path, headers, query, client, duration, request_size, response_size, status)) as cursor:
63
64
  assert cursor.lastrowid is not None
64
65
  return cursor.lastrowid
65
66
 
lfss/src/utils.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Callable
1
+ import datetime
2
2
  import urllib.parse
3
3
  import asyncio
4
4
  import functools
@@ -49,4 +49,20 @@ def debounce_async(delay: float = 0):
49
49
  except asyncio.CancelledError:
50
50
  pass
51
51
  return wrapper
52
- return debounce_wrap
52
+ return debounce_wrap
53
+
54
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified
55
+ def format_last_modified(last_modified_gmt: str):
56
+ """
57
+ Format the last modified time to the HTTP standard format
58
+ - last_modified_gmt: The last modified time in SQLite ISO 8601 GMT format: e.g. '2021-09-01 12:00:00'
59
+ """
60
+ assert len(last_modified_gmt) == 19
61
+ dt = datetime.datetime.strptime(last_modified_gmt, '%Y-%m-%d %H:%M:%S')
62
+ return dt.strftime('%a, %d %b %Y %H:%M:%S GMT')
63
+
64
+ def now_stamp() -> float:
65
+ return datetime.datetime.now().timestamp()
66
+
67
+ def stamp_to_str(stamp: float) -> str:
68
+ return datetime.datetime.fromtimestamp(stamp).strftime('%Y-%m-%d %H:%M:%S')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lfss
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Lightweight file storage service
5
5
  Home-page: https://github.com/MenxLi/lfss
6
6
  Author: li, mengxun
@@ -15,13 +15,13 @@ lfss/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  lfss/client/api.py,sha256=VBiU-JxGaN6S-fXYD_KvzG7FsGBIUzZ5IisJJSMooDk,3443
16
16
  lfss/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  lfss/src/config.py,sha256=mc8QoiWoPgiZ5JnSvKBH8jWbp5uLSyG3l5boDiQPXS0,325
18
- lfss/src/database.py,sha256=jsDcDrc5r4mnuFiY10YEMwIRMvx6c4PY4LN9C9BinDA,27854
18
+ lfss/src/database.py,sha256=wNDsvjvr7Jq1gDeUd_26tETcfeiplAmywIwXLK1TD80,27923
19
19
  lfss/src/error.py,sha256=S5ui3tJ0uKX4EZnt2Db-KbDmJXlECI_OY95cNMkuegc,218
20
20
  lfss/src/log.py,sha256=qNE04sHoZ2rMbQ5dR2zT7Xaz1KVAfYp5hKpWJX42S9g,5244
21
- lfss/src/server.py,sha256=6sXOXGPF3s1h38zNS6mP4N9qEXZ8biEAx-wSdpCNlwg,13010
22
- lfss/src/stat.py,sha256=Y28h-TNhQJzsAlCP4E7mnJmgISXGIECvCCRPq_73ZN0,2043
23
- lfss/src/utils.py,sha256=qCXFTIcfnVsdg6zvoEDhZniLTU-WiG23SILXzUG1XBw,1613
24
- lfss-0.3.2.dist-info/METADATA,sha256=qHYqmRzikIGgohOabShmfj1T9lQHa6iIPAF2BV2wSVo,1787
25
- lfss-0.3.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
- lfss-0.3.2.dist-info/entry_points.txt,sha256=nUIJhenyZbcymvPVhrzV7SAtRhd7O52DflbRrpQUC04,110
27
- lfss-0.3.2.dist-info/RECORD,,
21
+ lfss/src/server.py,sha256=P4GpsIJ869fi6lzHk2IVMyJooGWIvqh0cykkDerJ74k,13190
22
+ lfss/src/stat.py,sha256=_4OaSvBm7D6mPgifwxnhGIEk1_q3SxfJr3lizaEoV_w,2081
23
+ lfss/src/utils.py,sha256=8VkrtpSmurbMiX7GyK-n7Grvzy3uwSJXHdONEsuLCDI,2272
24
+ lfss-0.4.0.dist-info/METADATA,sha256=X0qXQ4gkl_sJ5VNEGLJ0XG0JSqmNnOPixHVn6ZQ7pj0,1787
25
+ lfss-0.4.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
+ lfss-0.4.0.dist-info/entry_points.txt,sha256=nUIJhenyZbcymvPVhrzV7SAtRhd7O52DflbRrpQUC04,110
27
+ lfss-0.4.0.dist-info/RECORD,,
File without changes