lfss 0.7.1__py3-none-any.whl → 0.7.2__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.
Readme.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Lightweight File Storage Service (LFSS)
2
2
  [![PyPI](https://img.shields.io/pypi/v/lfss)](https://pypi.org/project/lfss/)
3
3
 
4
- A lightweight file/object storage service!
4
+ My experiment on a lightweight file/object storage service.
5
+ It stores small files and metadata in sqlite, large files in the filesystem, and serves them through a simple REST API.
6
+ Tested on 2 million files, and it works fine... thanks to the sqlite database!
5
7
 
6
8
  Usage:
7
9
  ```sh
@@ -21,7 +23,7 @@ Or, you can start a web server at `/frontend` and open `index.html` in your brow
21
23
 
22
24
  The API usage is simple, just `GET`, `PUT`, `DELETE` to the `/<username>/file/url` path.
23
25
  Authentication is done via `Authorization` header with the value `Bearer <token>`, or through the `token` query parameter.
24
- You can refer to `frontend` as an application example, and `frontend/api.js` or `lfss.client.api.py` for the API usage.
26
+ You can refer to `frontend` as an application example, and `frontend/api.js` or `lfss/client/api.py` for the API usage.
25
27
 
26
28
  By default, the service exposes all files to the public for `GET` requests,
27
29
  but file-listing is restricted to the user's own files.
lfss/cli/balance.py CHANGED
@@ -24,28 +24,19 @@ def barriered(func):
24
24
 
25
25
  @barriered
26
26
  async def move_to_external(f_id: str, flag: str = ''):
27
- # async with aiosqlite.connect(db_file, timeout = 60) as c:
28
27
  async with transaction() as c:
29
- async with c.execute( "SELECT data FROM blobs.fdata WHERE file_id = ?", (f_id,)) as cursor:
30
- blob_row = await cursor.fetchone()
31
- if blob_row is None:
32
- print(f"{flag}File {f_id} not found in blobs.fdata")
33
- return
28
+ cursor = await c.execute( "SELECT data FROM blobs.fdata WHERE file_id = ?", (f_id,))
29
+ blob_row = await cursor.fetchone()
30
+ if blob_row is None:
31
+ print(f"{flag}File {f_id} not found in blobs.fdata")
32
+ return
34
33
  await c.execute("BEGIN")
35
34
  blob: bytes = blob_row[0]
36
- try:
37
- async with aiofiles.open(LARGE_BLOB_DIR / f_id, 'wb') as f:
38
- await f.write(blob)
39
- await c.execute( "UPDATE fmeta SET external = 1 WHERE file_id = ?", (f_id,))
40
- await c.execute( "DELETE FROM blobs.fdata WHERE file_id = ?", (f_id,))
41
- await c.commit()
42
- print(f"{flag}Moved {f_id} to external storage")
43
- except Exception as e:
44
- await c.rollback()
45
- print(f"{flag}Error moving {f_id}: {e}")
46
-
47
- if isinstance(e, KeyboardInterrupt):
48
- raise e
35
+ async with aiofiles.open(LARGE_BLOB_DIR / f_id, 'wb') as f:
36
+ await f.write(blob)
37
+ await c.execute( "UPDATE fmeta SET external = 1 WHERE file_id = ?", (f_id,))
38
+ await c.execute( "DELETE FROM blobs.fdata WHERE file_id = ?", (f_id,))
39
+ print(f"{flag}Moved {f_id} to external storage")
49
40
 
50
41
  @barriered
51
42
  async def move_to_internal(f_id: str, flag: str = ''):
@@ -56,19 +47,10 @@ async def move_to_internal(f_id: str, flag: str = ''):
56
47
  async with aiofiles.open(LARGE_BLOB_DIR / f_id, 'rb') as f:
57
48
  blob = await f.read()
58
49
 
59
- await c.execute("BEGIN")
60
- try:
61
- await c.execute("INSERT INTO blobs.fdata (file_id, data) VALUES (?, ?)", (f_id, blob))
62
- await c.execute("UPDATE fmeta SET external = 0 WHERE file_id = ?", (f_id,))
63
- await c.commit()
64
- (LARGE_BLOB_DIR / f_id).unlink(missing_ok=True)
65
- print(f"{flag}Moved {f_id} to internal storage")
66
- except Exception as e:
67
- await c.rollback()
68
- print(f"{flag}Error moving {f_id}: {e}")
69
- if isinstance(e, KeyboardInterrupt):
70
- raise e
71
-
50
+ await c.execute("INSERT INTO blobs.fdata (file_id, data) VALUES (?, ?)", (f_id, blob))
51
+ await c.execute("UPDATE fmeta SET external = 0 WHERE file_id = ?", (f_id,))
52
+ (LARGE_BLOB_DIR / f_id).unlink(missing_ok=True)
53
+ print(f"{flag}Moved {f_id} to internal storage")
72
54
 
73
55
  @global_entrance()
74
56
  async def _main(batch_size: int = 10000):
lfss/src/config.py CHANGED
@@ -13,5 +13,5 @@ LARGE_BLOB_DIR.mkdir(exist_ok=True)
13
13
 
14
14
  # https://sqlite.org/fasterthanfs.html
15
15
  LARGE_FILE_BYTES = 8 * 1024 * 1024 # 8MB
16
- MAX_FILE_BYTES = 1024 * 1024 * 1024 # 1GB
17
- MAX_BUNDLE_BYTES = 1024 * 1024 * 1024 # 1GB
16
+ MAX_FILE_BYTES = 512 * 1024 * 1024 # 512MB
17
+ MAX_BUNDLE_BYTES = 512 * 1024 * 1024 # 512MB
@@ -66,26 +66,27 @@ class SqlConnectionPool:
66
66
  if len(self._connections) == 0:
67
67
  raise Exception("No available connections, please init the pool first")
68
68
 
69
- if w:
70
- assert self._w_connection
71
- if self._w_connection.is_available:
72
- self._w_connection.is_available = False
73
- return self._w_connection
74
- raise Exception("Write connection is not available")
75
-
76
69
  async with self._lock:
77
- for c in self._connections:
78
- if c.is_available:
79
- c.is_available = False
80
- return c
70
+ if w:
71
+ assert self._w_connection
72
+ if self._w_connection.is_available:
73
+ self._w_connection.is_available = False
74
+ return self._w_connection
75
+ raise Exception("Write connection is not available")
76
+
77
+ else:
78
+ for c in self._connections:
79
+ if c.is_available:
80
+ c.is_available = False
81
+ return c
81
82
  raise Exception("No available connections, impossible?")
82
83
 
83
84
  async def release(self, conn: SqlConnection):
84
- if conn == self._w_connection:
85
- conn.is_available = True
86
- return
87
-
88
85
  async with self._lock:
86
+ if conn == self._w_connection:
87
+ conn.is_available = True
88
+ return
89
+
89
90
  if not conn in self._connections:
90
91
  raise Exception("Connection not in pool")
91
92
  conn.is_available = True
lfss/src/database.py CHANGED
@@ -519,7 +519,13 @@ class Database:
519
519
  raise FileNotFoundError(f"File {url} not found")
520
520
  if not r.external:
521
521
  raise ValueError(f"File {url} is not stored externally, should use read_file instead")
522
- return fconn.get_file_blob_external(r.file_id)
522
+ ret = fconn.get_file_blob_external(r.file_id)
523
+
524
+ async with transaction() as w_cur:
525
+ await FileConn(w_cur).log_access(url)
526
+
527
+ return ret
528
+
523
529
 
524
530
  async def read_file(self, url: str) -> bytes:
525
531
  validate_url(url)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lfss
3
- Version: 0.7.1
3
+ Version: 0.7.2
4
4
  Summary: Lightweight file storage service
5
5
  Home-page: https://github.com/MenxLi/lfss
6
6
  Author: li, mengxun
@@ -21,7 +21,9 @@ Description-Content-Type: text/markdown
21
21
  # Lightweight File Storage Service (LFSS)
22
22
  [![PyPI](https://img.shields.io/pypi/v/lfss)](https://pypi.org/project/lfss/)
23
23
 
24
- A lightweight file/object storage service!
24
+ My experiment on a lightweight file/object storage service.
25
+ It stores small files and metadata in sqlite, large files in the filesystem, and serves them through a simple REST API.
26
+ Tested on 2 million files, and it works fine... thanks to the sqlite database!
25
27
 
26
28
  Usage:
27
29
  ```sh
@@ -41,7 +43,7 @@ Or, you can start a web server at `/frontend` and open `index.html` in your brow
41
43
 
42
44
  The API usage is simple, just `GET`, `PUT`, `DELETE` to the `/<username>/file/url` path.
43
45
  Authentication is done via `Authorization` header with the value `Bearer <token>`, or through the `token` query parameter.
44
- You can refer to `frontend` as an application example, and `frontend/api.js` or `lfss.client.api.py` for the API usage.
46
+ You can refer to `frontend` as an application example, and `frontend/api.js` or `lfss/client/api.py` for the API usage.
45
47
 
46
48
  By default, the service exposes all files to the public for `GET` requests,
47
49
  but file-listing is restricted to the user's own files.
@@ -1,4 +1,4 @@
1
- Readme.md,sha256=8ZAADV1K20gEDhwiL-Qq_rBAePlwWQn8c5uJ_X7OCIg,1128
1
+ Readme.md,sha256=t5dninN6gFzve_NySxqGrEkmAYhF1h1RhaMdCOeCILA,1348
2
2
  docs/Known_issues.md,sha256=rfdG3j1OJF-59S9E06VPyn0nZKbW-ybPxkoZ7MEZWp8,81
3
3
  docs/Permission.md,sha256=EY1Y4tT5PDdHDb2pXsVgMAJXxkUihTZfPT0_z7Q53FA,1485
4
4
  frontend/api.js,sha256=-ouhsmucEunAK3m1H__MqffQkXAjoeVEfM15BvqfIZs,7677
@@ -8,7 +8,7 @@ frontend/popup.js,sha256=3PgaGZmxSdV1E-D_MWgcR7aHWkcsHA1BNKSOkmP66tA,5191
8
8
  frontend/scripts.js,sha256=hQ8m3L7P-LplLqrPUWD6pBo4C_tCUl2XZKRNtkWBy8I,21155
9
9
  frontend/styles.css,sha256=wly8O-zF4EUgV12Tv1bATSfmJsLITv2u3_SiyXVaxv4,4096
10
10
  frontend/utils.js,sha256=Ts4nlef8pkrEgpwX-uQwAhWvwxlIzex8ijDLNCa22ps,2372
11
- lfss/cli/balance.py,sha256=vhTlZoBF97dHBf5IittHpybKswaA-xXOfiPAdvIHD5Q,4794
11
+ lfss/cli/balance.py,sha256=heOgwH6oNnfYsKJfA4VxWKdEXPstdVbbRXWxcDqLIS0,4176
12
12
  lfss/cli/cli.py,sha256=bJOeEyri_XVWUvnjohsw_oPYKp-bELxLrg5sVWOpKQA,2259
13
13
  lfss/cli/panel.py,sha256=iGdVmdWYjA_7a78ZzWEB_3ggIOBeUKTzg6F5zLaB25c,1401
14
14
  lfss/cli/serve.py,sha256=bO3GT0kuylMGN-7bZWP4e71MlugGZ_lEMkYaYld_Ntg,985
@@ -18,16 +18,16 @@ lfss/client/api.py,sha256=ICqpcyvSf-9QmYNv9EQ5fA_MViSuLxSNn-CIBNqWkW8,5414
18
18
  lfss/sql/init.sql,sha256=C-JtQAlaOjESI8uoF1Y_9dKukEVSw5Ll-7yA3gG-XHU,1210
19
19
  lfss/sql/pragma.sql,sha256=uENx7xXjARmro-A3XAK8OM8v5AxDMdCCRj47f86UuXg,206
20
20
  lfss/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- lfss/src/config.py,sha256=0TjCCrqkoiLw45vOT0PbDciWbzpD7ztWtubwIwXCsPk,527
22
- lfss/src/connection_pool.py,sha256=Y47BJ1gsyusmmVA1PHoFBhytup8s3HbU9D_Aw0y4-TM,4848
23
- lfss/src/database.py,sha256=l0Z-ixfwAkYHbSgk9cVceOJ2vzkYUJeVw25Ny7HQxAA,31104
21
+ lfss/src/config.py,sha256=G-tOdk6RrE62_d-QoD6WXD4l70YriBazUFdumTz3P20,529
22
+ lfss/src/connection_pool.py,sha256=qBtXr2j_O7vGI2Mv0HVsza35T8nljIx85jLlyEv1SgE,4918
23
+ lfss/src/database.py,sha256=tGcHfxThmRTmtLIlEWdJtAe1tzP64wHSC1JcbQdRU-8,31226
24
24
  lfss/src/datatype.py,sha256=BLS7vuuKnFZQg0nrKeP9SymqUhcN6HwPgejU0yBd_Ak,1622
25
25
  lfss/src/error.py,sha256=imbhwnbhnI3HLhkbfICROe3F0gleKrOk4XnqHJDOtuI,285
26
26
  lfss/src/log.py,sha256=qNE04sHoZ2rMbQ5dR2zT7Xaz1KVAfYp5hKpWJX42S9g,5244
27
27
  lfss/src/server.py,sha256=9mnBlcHJruwOV-aoHa1P146F4khpVDmif7rq58yZX3U,15306
28
28
  lfss/src/stat.py,sha256=hTMtQyM_Ukmhc33Bb9FGCfBMIX02KrGHQg8nL7sC8sU,2082
29
29
  lfss/src/utils.py,sha256=8VkrtpSmurbMiX7GyK-n7Grvzy3uwSJXHdONEsuLCDI,2272
30
- lfss-0.7.1.dist-info/METADATA,sha256=nNjDG5BqDBRpeYP3NNPEdM02wWeIIJNO1VDdKNrgqQ0,1820
31
- lfss-0.7.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
32
- lfss-0.7.1.dist-info/entry_points.txt,sha256=d_Ri3GXxUW-S0E6q953A8od0YMmUAnZGlJSKS46OiW8,172
33
- lfss-0.7.1.dist-info/RECORD,,
30
+ lfss-0.7.2.dist-info/METADATA,sha256=pJcPBmMpKwNujl4CWZKRchT3uXyCPaEvi1mek7SUJUE,2040
31
+ lfss-0.7.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
32
+ lfss-0.7.2.dist-info/entry_points.txt,sha256=d_Ri3GXxUW-S0E6q953A8od0YMmUAnZGlJSKS46OiW8,172
33
+ lfss-0.7.2.dist-info/RECORD,,
File without changes