lfss 0.7.5__py3-none-any.whl → 0.7.7__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.
- frontend/api.js +1 -0
- frontend/index.html +1 -2
- frontend/info.css +36 -0
- frontend/info.js +137 -0
- frontend/popup.css +1 -0
- frontend/scripts.js +15 -32
- frontend/styles.css +3 -2
- lfss/cli/cli.py +13 -2
- lfss/cli/user.py +4 -3
- lfss/src/database.py +2 -1
- lfss/src/utils.py +34 -1
- {lfss-0.7.5.dist-info → lfss-0.7.7.dist-info}/METADATA +1 -1
- {lfss-0.7.5.dist-info → lfss-0.7.7.dist-info}/RECORD +15 -13
- {lfss-0.7.5.dist-info → lfss-0.7.7.dist-info}/WHEEL +0 -0
- {lfss-0.7.5.dist-info → lfss-0.7.7.dist-info}/entry_points.txt +0 -0
frontend/api.js
CHANGED
@@ -20,6 +20,7 @@
|
|
20
20
|
* @property {number} file_size - the size of the file, in bytes
|
21
21
|
* @property {string} create_time - the time the file was created
|
22
22
|
* @property {string} access_time - the time the file was last accessed
|
23
|
+
* @property {string} mime_type - the mime type of the file
|
23
24
|
*
|
24
25
|
* Partially complete...
|
25
26
|
* @typedef {Object} DirectoryRecord
|
frontend/index.html
CHANGED
frontend/info.css
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
div.info-container {
|
3
|
+
display: flex;
|
4
|
+
flex-direction: column;
|
5
|
+
margin: 1rem;
|
6
|
+
gap: 1rem;
|
7
|
+
}
|
8
|
+
div.info-container-left {
|
9
|
+
margin: 0 auto;
|
10
|
+
padding: 10px;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-radius: 5px;
|
13
|
+
background-color: #f9f9f9;
|
14
|
+
}
|
15
|
+
|
16
|
+
td {
|
17
|
+
text-align: left;
|
18
|
+
text-wrap: nowrap;
|
19
|
+
width: fit-content;
|
20
|
+
}
|
21
|
+
td.info-table-key {
|
22
|
+
font-weight: bold;
|
23
|
+
padding-right: 1rem;
|
24
|
+
}
|
25
|
+
|
26
|
+
div.info-container-right {
|
27
|
+
display: flex;
|
28
|
+
flex-direction: column;
|
29
|
+
gap: 0.5rem;
|
30
|
+
width: 100%;
|
31
|
+
}
|
32
|
+
div.info-path-copy {
|
33
|
+
display: flex;
|
34
|
+
gap: 0.5rem;
|
35
|
+
align-items: center;
|
36
|
+
}
|
frontend/info.js
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
/**
|
2
|
+
* @import { UserRecord, FileRecord, DirectoryRecord } from "./api.js";
|
3
|
+
*/
|
4
|
+
import Connector from "./api.js";
|
5
|
+
import { createFloatingWindow, showPopup } from "./popup.js";
|
6
|
+
import { cvtGMT2Local, formatSize, decodePathURI, copyToClipboard } from "./utils.js";
|
7
|
+
|
8
|
+
const ensureSlashEnd = (path) => {
|
9
|
+
return path.endsWith('/') ? path : path + '/';
|
10
|
+
}
|
11
|
+
|
12
|
+
/**
|
13
|
+
* @param {FileRecord} r
|
14
|
+
* @param {UserRecord} u
|
15
|
+
*/
|
16
|
+
export function showInfoPanel(r, u){
|
17
|
+
const innerHTML = `
|
18
|
+
<div class="info-container">
|
19
|
+
<div class="info-container-left">
|
20
|
+
<table class="info-table">
|
21
|
+
<tr>
|
22
|
+
<td class="info-table-key">Filename</td>
|
23
|
+
<td class="info-table-value">${decodePathURI(r.url).split('/').pop()}</td>
|
24
|
+
</tr>
|
25
|
+
<tr>
|
26
|
+
<td class="info-table-key">File-Type</td>
|
27
|
+
<td class="info-table-value">${r.mime_type}</td>
|
28
|
+
</tr>
|
29
|
+
<tr>
|
30
|
+
<td class="info-table-key">Size</td>
|
31
|
+
<td class="info-table-value">${formatSize(r.file_size)}</td>
|
32
|
+
</tr>
|
33
|
+
<tr>
|
34
|
+
<td class="info-table-key">Owner</td>
|
35
|
+
<td class="info-table-value">${r.owner_id}</td>
|
36
|
+
</tr>
|
37
|
+
<tr>
|
38
|
+
<td class="info-table-key">Create-Time</td>
|
39
|
+
<td class="info-table-value">${cvtGMT2Local(r.create_time)}</td>
|
40
|
+
</td>
|
41
|
+
<tr>
|
42
|
+
<td class="info-table-key">Access-Time</td>
|
43
|
+
<td class="info-table-value">${cvtGMT2Local(r.access_time)}</td>
|
44
|
+
</tr>
|
45
|
+
</table>
|
46
|
+
</div>
|
47
|
+
<div class="info-container-right">
|
48
|
+
<div class="info-path-copy">
|
49
|
+
<input type="text" value="${window.location.origin}/${r.url}" readonly>
|
50
|
+
<button class="copy-button" id='copy-btn-full-path'>📋</button>
|
51
|
+
</div>
|
52
|
+
<div class="info-path-copy">
|
53
|
+
<input type="text" value="${r.url}" readonly>
|
54
|
+
<button class="copy-button" id='copy-btn-rel-path'>📋</button>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
`
|
59
|
+
const [win, closeWin] = createFloatingWindow(innerHTML, {title: 'File Info'});
|
60
|
+
document.getElementById('copy-btn-full-path').onclick = () => {
|
61
|
+
copyToClipboard(window.location.origin + '/' + r.url);
|
62
|
+
showPopup('Path copied to clipboard', {timeout: 2000, level: 'success'});
|
63
|
+
}
|
64
|
+
document.getElementById('copy-btn-rel-path').onclick = () => {
|
65
|
+
copyToClipboard(r.url);
|
66
|
+
showPopup('Path copied to clipboard', {timeout: 2000, level: 'success'});
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
/**
|
71
|
+
* @param {DirectoryRecord} r
|
72
|
+
* @param {UserRecord} u
|
73
|
+
* @param {Connector} c
|
74
|
+
*/
|
75
|
+
export function showDirInfoPanel(r, u, c){
|
76
|
+
let fmtPath = decodePathURI(r.url);
|
77
|
+
if (fmtPath.endsWith('/')) {
|
78
|
+
fmtPath = fmtPath.slice(0, -1);
|
79
|
+
}
|
80
|
+
const innerHTML = `
|
81
|
+
<div class="info-container">
|
82
|
+
<div class="info-container-left">
|
83
|
+
<table class="info-table">
|
84
|
+
<tr>
|
85
|
+
<td class="info-table-key">Pathname</td>
|
86
|
+
<td class="info-table-value" id="info-table-pathname">${fmtPath.split('/').pop()}</td>
|
87
|
+
</tr>
|
88
|
+
<tr>
|
89
|
+
<td class="info-table-key">Size</td>
|
90
|
+
<td class="info-table-value" id="info-table-pathsize">N/A</td>
|
91
|
+
</tr>
|
92
|
+
<tr>
|
93
|
+
<td class="info-table-key">Access-Time</td>
|
94
|
+
<td class="info-table-value" id="info-table-accesstime">1970-01-01 00:00:00</td>
|
95
|
+
</tr>
|
96
|
+
<tr>
|
97
|
+
<td class="info-table-key">Create-Time</td>
|
98
|
+
<td class="info-table-value" id="info-table-createtime">1970-01-01 00:00:00</td>
|
99
|
+
</td>
|
100
|
+
</table>
|
101
|
+
</div>
|
102
|
+
<div class="info-container-right">
|
103
|
+
<div class="info-path-copy">
|
104
|
+
<input type="text" value="${window.location.origin}/${ensureSlashEnd(r.url)}" readonly>
|
105
|
+
<button class="copy-button" id='copy-btn-full-path'>📋</button>
|
106
|
+
</div>
|
107
|
+
<div class="info-path-copy">
|
108
|
+
<input type="text" value="${ensureSlashEnd(r.url)}" readonly>
|
109
|
+
<button class="copy-button" id='copy-btn-rel-path'>📋</button>
|
110
|
+
</div>
|
111
|
+
</div>
|
112
|
+
</div>
|
113
|
+
`
|
114
|
+
const [win, closeWin] = createFloatingWindow(innerHTML, {title: 'File Info'});
|
115
|
+
document.getElementById('copy-btn-full-path').onclick = () => {
|
116
|
+
copyToClipboard(window.location.origin + '/' + ensureSlashEnd(r.url));
|
117
|
+
showPopup('Path copied to clipboard', {timeout: 2000, level: 'success'});
|
118
|
+
}
|
119
|
+
document.getElementById('copy-btn-rel-path').onclick = () => {
|
120
|
+
copyToClipboard(ensureSlashEnd(r.url));
|
121
|
+
showPopup('Path copied to clipboard', {timeout: 2000, level: 'success'});
|
122
|
+
}
|
123
|
+
|
124
|
+
const sizeValTd = document.querySelector('.info-table-value#info-table-pathsize');
|
125
|
+
const createTimeValTd = document.querySelector('.info-table-value#info-table-createtime');
|
126
|
+
const accessTimeValTd = document.querySelector('.info-table-value#info-table-accesstime');
|
127
|
+
// console.log(sizeValTd, createTimeValTd, accessTimeValTd)
|
128
|
+
c.getMetadata(ensureSlashEnd(r.url)).then((meta) => {
|
129
|
+
if (!meta) {
|
130
|
+
console.error('Failed to fetch metadata for: ' + r.url);
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
sizeValTd.textContent = formatSize(meta.size);
|
134
|
+
createTimeValTd.textContent = cvtGMT2Local(meta.create_time);
|
135
|
+
accessTimeValTd.textContent = cvtGMT2Local(meta.access_time);
|
136
|
+
});
|
137
|
+
}
|
frontend/popup.css
CHANGED
frontend/scripts.js
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
import Connector from './api.js';
|
2
2
|
import { permMap } from './api.js';
|
3
3
|
import { showFloatingWindowLineInput, showPopup } from './popup.js';
|
4
|
-
import { formatSize, decodePathURI, ensurePathURI,
|
4
|
+
import { formatSize, decodePathURI, ensurePathURI, getRandomString, cvtGMT2Local, debounce, encodePathURI } from './utils.js';
|
5
|
+
import { showInfoPanel, showDirInfoPanel } from './info.js';
|
5
6
|
|
6
7
|
const conn = new Connector();
|
7
8
|
let userRecord = null;
|
@@ -235,7 +236,6 @@ function refreshFileList(){
|
|
235
236
|
const tr = document.createElement('tr');
|
236
237
|
const sizeTd = document.createElement('td');
|
237
238
|
const accessTimeTd = document.createElement('td');
|
238
|
-
const createTimeTd = document.createElement('td');
|
239
239
|
{
|
240
240
|
const nameTd = document.createElement('td');
|
241
241
|
if (dir.url.endsWith('/')){
|
@@ -262,8 +262,6 @@ function refreshFileList(){
|
|
262
262
|
tr.appendChild(sizeTd);
|
263
263
|
accessTimeTd.textContent = cvtGMT2Local(dir.access_time);
|
264
264
|
tr.appendChild(accessTimeTd);
|
265
|
-
createTimeTd.textContent = cvtGMT2Local(dir.create_time);
|
266
|
-
tr.appendChild(createTimeTd);
|
267
265
|
}
|
268
266
|
{
|
269
267
|
const accessTd = document.createElement('td');
|
@@ -275,21 +273,14 @@ function refreshFileList(){
|
|
275
273
|
const actContainer = document.createElement('div');
|
276
274
|
actContainer.classList.add('action-container');
|
277
275
|
|
278
|
-
const
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
conn
|
284
|
-
(meta) => {
|
285
|
-
sizeTd.textContent = formatSize(meta.size);
|
286
|
-
accessTimeTd.textContent = cvtGMT2Local(meta.access_time);
|
287
|
-
createTimeTd.textContent = cvtGMT2Local(meta.create_time);
|
288
|
-
}
|
289
|
-
);
|
290
|
-
showPopup('Fetching metadata...', {level: 'info', timeout: 3000});
|
276
|
+
const infoButton = document.createElement('a');
|
277
|
+
infoButton.style.cursor = 'pointer';
|
278
|
+
infoButton.textContent = 'Details';
|
279
|
+
infoButton.style.width = '100%';
|
280
|
+
infoButton.addEventListener('click', () => {
|
281
|
+
showDirInfoPanel(dir, userRecord, conn);
|
291
282
|
});
|
292
|
-
actContainer.appendChild(
|
283
|
+
actContainer.appendChild(infoButton);
|
293
284
|
|
294
285
|
const moveButton = document.createElement('a');
|
295
286
|
moveButton.textContent = 'Move';
|
@@ -369,13 +360,6 @@ function refreshFileList(){
|
|
369
360
|
tr.appendChild(dateTd);
|
370
361
|
}
|
371
362
|
|
372
|
-
{
|
373
|
-
const dateTd = document.createElement('td');
|
374
|
-
const createTime = file.create_time;
|
375
|
-
dateTd.textContent = cvtGMT2Local(createTime);
|
376
|
-
tr.appendChild(dateTd);
|
377
|
-
}
|
378
|
-
|
379
363
|
{
|
380
364
|
const accessTd = document.createElement('td');
|
381
365
|
if (file.owner_id === userRecord.id || userRecord.is_admin){
|
@@ -416,14 +400,13 @@ function refreshFileList(){
|
|
416
400
|
const actContainer = document.createElement('div');
|
417
401
|
actContainer.classList.add('action-container');
|
418
402
|
|
419
|
-
const
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
showPopup('Link copied to clipboard', {level: "success"});
|
403
|
+
const infoButton = document.createElement('a');
|
404
|
+
infoButton.style.cursor = 'pointer';
|
405
|
+
infoButton.textContent = 'Details';
|
406
|
+
infoButton.addEventListener('click', () => {
|
407
|
+
showInfoPanel(file, userRecord);
|
425
408
|
});
|
426
|
-
actContainer.appendChild(
|
409
|
+
actContainer.appendChild(infoButton);
|
427
410
|
|
428
411
|
const viewButton = document.createElement('a');
|
429
412
|
viewButton.textContent = 'View';
|
frontend/styles.css
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
@import "./popup.css";
|
2
|
+
@import "./info.css";
|
2
3
|
|
3
4
|
body{
|
4
5
|
font-family: Arial, sans-serif;
|
@@ -173,10 +174,10 @@ table#files tr:hover {
|
|
173
174
|
background-color: #eaeaea;
|
174
175
|
transition: all 0.2s;
|
175
176
|
}
|
176
|
-
table#files tr td:nth-child(2), table#files tr td:nth-child(
|
177
|
+
table#files tr td:nth-child(2), table#files tr td:nth-child(4){
|
177
178
|
width: 1%;
|
178
179
|
}
|
179
|
-
table#files tr td:nth-child(3), table#files tr td:nth-child(
|
180
|
+
table#files tr td:nth-child(3), table#files tr td:nth-child(5){
|
180
181
|
width: 12%;
|
181
182
|
}
|
182
183
|
|
lfss/cli/cli.py
CHANGED
@@ -1,7 +1,18 @@
|
|
1
1
|
from lfss.client import Connector, upload_directory, upload_file, download_file, download_directory
|
2
|
-
from lfss.src.datatype import FileReadPermission
|
3
2
|
from pathlib import Path
|
4
3
|
import argparse
|
4
|
+
from lfss.src.datatype import FileReadPermission
|
5
|
+
|
6
|
+
def parse_permission(s: str) -> FileReadPermission:
|
7
|
+
if s.lower() == "public":
|
8
|
+
return FileReadPermission.PUBLIC
|
9
|
+
if s.lower() == "protected":
|
10
|
+
return FileReadPermission.PROTECTED
|
11
|
+
if s.lower() == "private":
|
12
|
+
return FileReadPermission.PRIVATE
|
13
|
+
if s.lower() == "unset":
|
14
|
+
return FileReadPermission.UNSET
|
15
|
+
raise ValueError(f"Invalid permission {s}")
|
5
16
|
|
6
17
|
def parse_arguments():
|
7
18
|
parser = argparse.ArgumentParser(description="Command line interface, please set LFSS_ENDPOINT and LFSS_TOKEN environment variables.")
|
@@ -16,7 +27,7 @@ def parse_arguments():
|
|
16
27
|
sp_upload.add_argument("-j", "--jobs", type=int, default=1, help="Number of concurrent uploads")
|
17
28
|
sp_upload.add_argument("--interval", type=float, default=0, help="Interval between files, only works with directory upload")
|
18
29
|
sp_upload.add_argument("--conflict", choices=["overwrite", "abort", "skip", "skip-ahead"], default="abort", help="Conflict resolution")
|
19
|
-
sp_upload.add_argument("--permission", type=
|
30
|
+
sp_upload.add_argument("--permission", type=parse_permission, default=FileReadPermission.UNSET, help="File permission, can be public, protected, private, or unset")
|
20
31
|
sp_upload.add_argument("--retries", type=int, default=0, help="Number of retries")
|
21
32
|
|
22
33
|
# download
|
lfss/cli/user.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import argparse, asyncio
|
2
2
|
from contextlib import asynccontextmanager
|
3
|
+
from .cli import parse_permission, FileReadPermission
|
3
4
|
from ..src.database import Database, FileReadPermission, transaction, UserConn
|
4
5
|
from ..src.connection_pool import global_entrance
|
5
6
|
|
@@ -21,9 +22,9 @@ async def _main():
|
|
21
22
|
sp_add = sp.add_parser('add')
|
22
23
|
sp_add.add_argument('username', type=str)
|
23
24
|
sp_add.add_argument('password', type=str)
|
24
|
-
sp_add.add_argument('--admin', action='store_true')
|
25
|
-
sp_add.add_argument(
|
26
|
-
sp_add.add_argument('--max-storage', type=parse_storage_size, default="1G")
|
25
|
+
sp_add.add_argument('--admin', action='store_true', help='Set user as admin')
|
26
|
+
sp_add.add_argument("--permission", type=parse_permission, default=FileReadPermission.UNSET, help="File permission, can be public, protected, private, or unset")
|
27
|
+
sp_add.add_argument('--max-storage', type=parse_storage_size, default="1G", help="Maximum storage size, e.g. 1G, 100M, 10K")
|
27
28
|
|
28
29
|
sp_delete = sp.add_parser('delete')
|
29
30
|
sp_delete.add_argument('username', type=str)
|
lfss/src/database.py
CHANGED
@@ -13,7 +13,7 @@ from .connection_pool import execute_sql, unique_cursor, transaction
|
|
13
13
|
from .datatype import UserRecord, FileReadPermission, FileRecord, DirectoryRecord, PathContents
|
14
14
|
from .config import LARGE_BLOB_DIR
|
15
15
|
from .log import get_logger
|
16
|
-
from .utils import decode_uri_compnents, hash_credential
|
16
|
+
from .utils import decode_uri_compnents, hash_credential, concurrent_wrap
|
17
17
|
from .error import *
|
18
18
|
|
19
19
|
class DBObjectBase(ABC):
|
@@ -661,6 +661,7 @@ class Database:
|
|
661
661
|
continue
|
662
662
|
yield r, blob
|
663
663
|
|
664
|
+
@concurrent_wrap()
|
664
665
|
async def zip_path(self, top_url: str, urls: Optional[list[str]]) -> io.BytesIO:
|
665
666
|
if top_url.startswith('/'):
|
666
667
|
top_url = top_url[1:]
|
lfss/src/utils.py
CHANGED
@@ -3,6 +3,10 @@ import urllib.parse
|
|
3
3
|
import asyncio
|
4
4
|
import functools
|
5
5
|
import hashlib
|
6
|
+
from concurrent.futures import ThreadPoolExecutor
|
7
|
+
from typing import TypeVar, Callable, Awaitable
|
8
|
+
from functools import wraps, partial
|
9
|
+
import os
|
6
10
|
|
7
11
|
def hash_credential(username: str, password: str):
|
8
12
|
return hashlib.sha256((username + password).encode()).hexdigest()
|
@@ -56,4 +60,33 @@ def now_stamp() -> float:
|
|
56
60
|
return datetime.datetime.now().timestamp()
|
57
61
|
|
58
62
|
def stamp_to_str(stamp: float) -> str:
|
59
|
-
return datetime.datetime.fromtimestamp(stamp).strftime('%Y-%m-%d %H:%M:%S')
|
63
|
+
return datetime.datetime.fromtimestamp(stamp).strftime('%Y-%m-%d %H:%M:%S')
|
64
|
+
|
65
|
+
|
66
|
+
_FnReturnT = TypeVar('_FnReturnT')
|
67
|
+
_AsyncReturnT = Awaitable[_FnReturnT]
|
68
|
+
_g_executor = None
|
69
|
+
def get_global_executor():
|
70
|
+
global _g_executor
|
71
|
+
if _g_executor is None:
|
72
|
+
_g_executor = ThreadPoolExecutor(max_workers=4 if (cpu_count:=os.cpu_count()) and cpu_count > 4 else cpu_count)
|
73
|
+
return _g_executor
|
74
|
+
def async_wrap(executor=None):
|
75
|
+
if executor is None:
|
76
|
+
executor = get_global_executor()
|
77
|
+
def _async_wrap(func: Callable[..., _FnReturnT]) -> Callable[..., Awaitable[_FnReturnT]]:
|
78
|
+
@wraps(func)
|
79
|
+
async def run(*args, **kwargs):
|
80
|
+
loop = asyncio.get_event_loop()
|
81
|
+
pfunc = partial(func, *args, **kwargs)
|
82
|
+
return await loop.run_in_executor(executor, pfunc)
|
83
|
+
return run
|
84
|
+
return _async_wrap
|
85
|
+
def concurrent_wrap(executor=None):
|
86
|
+
def _concurrent_wrap(func: Callable[..., _AsyncReturnT]) -> Callable[..., _AsyncReturnT]:
|
87
|
+
@async_wrap(executor)
|
88
|
+
def sync_fn(*args, **kwargs):
|
89
|
+
loop = asyncio.new_event_loop()
|
90
|
+
return loop.run_until_complete(func(*args, **kwargs))
|
91
|
+
return sync_fn
|
92
|
+
return _concurrent_wrap
|
@@ -1,18 +1,20 @@
|
|
1
1
|
Readme.md,sha256=vsPotlwPAaHI5plh4aaszpi3rr7ZGDn7-wLdEYTWQ0k,1275
|
2
2
|
docs/Known_issues.md,sha256=rfdG3j1OJF-59S9E06VPyn0nZKbW-ybPxkoZ7MEZWp8,81
|
3
3
|
docs/Permission.md,sha256=X0VNfBKU52f93QYqcVyiBFJ3yURiSkhIo9S_5fdSgzM,2265
|
4
|
-
frontend/api.js,sha256=
|
5
|
-
frontend/index.html,sha256=
|
6
|
-
frontend/
|
4
|
+
frontend/api.js,sha256=DxWmqO0AAOsWLXYtbgAzEnSmVyEJyzcxXSCH7H3STUk,7925
|
5
|
+
frontend/index.html,sha256=Mem8de9vwmZoe4x1DKqpu_aFgIBURqT3mIGdeOOTbIs,2051
|
6
|
+
frontend/info.css,sha256=Ny0N3GywQ3a9q1_Qph_QFEKB4fEnTe_2DJ1Y5OsLLmQ,595
|
7
|
+
frontend/info.js,sha256=N3fAniM0Zir_P9t-Mt9_zOWEeqDuhA2uDRfIZsOHn4w,5459
|
8
|
+
frontend/popup.css,sha256=06Z3ut9-RviCUB2CjiONuEyJRpV3M7LAT6Qzl_nYf9s,1025
|
7
9
|
frontend/popup.js,sha256=3PgaGZmxSdV1E-D_MWgcR7aHWkcsHA1BNKSOkmP66tA,5191
|
8
|
-
frontend/scripts.js,sha256=
|
9
|
-
frontend/styles.css,sha256=
|
10
|
+
frontend/scripts.js,sha256=zp_ENEnWxiwZRM_gE7HP0j_MXEI8yActO6cQfiTkaD8,20171
|
11
|
+
frontend/styles.css,sha256=l5_SECKR6vkEe4llydRIFxx0C2SOqcacmdMrtdLZRVM,4086
|
10
12
|
frontend/utils.js,sha256=Ts4nlef8pkrEgpwX-uQwAhWvwxlIzex8ijDLNCa22ps,2372
|
11
13
|
lfss/cli/balance.py,sha256=heOgwH6oNnfYsKJfA4VxWKdEXPstdVbbRXWxcDqLIS0,4176
|
12
|
-
lfss/cli/cli.py,sha256=
|
14
|
+
lfss/cli/cli.py,sha256=LH1nx5wI1K2DZ3hvHz7oq5HcXVDoW2V6sr7q9gJ8gqo,4621
|
13
15
|
lfss/cli/panel.py,sha256=iGdVmdWYjA_7a78ZzWEB_3ggIOBeUKTzg6F5zLaB25c,1401
|
14
16
|
lfss/cli/serve.py,sha256=bO3GT0kuylMGN-7bZWP4e71MlugGZ_lEMkYaYld_Ntg,985
|
15
|
-
lfss/cli/user.py,sha256
|
17
|
+
lfss/cli/user.py,sha256=-ePmx_jhqfPQDl_9i_C_St9ujlCyyWSqNJUvc26v4_4,3686
|
16
18
|
lfss/client/__init__.py,sha256=8uvcKs3PYQamDd_cjfN-fX9QUohEzJqeJlOYkBlzC3M,4556
|
17
19
|
lfss/client/api.py,sha256=kSkB4wADTu012-1wl6v90OiZrw6aTQ42GU4jtV4KO0k,5764
|
18
20
|
lfss/sql/init.sql,sha256=C-JtQAlaOjESI8uoF1Y_9dKukEVSw5Ll-7yA3gG-XHU,1210
|
@@ -20,14 +22,14 @@ lfss/sql/pragma.sql,sha256=uENx7xXjARmro-A3XAK8OM8v5AxDMdCCRj47f86UuXg,206
|
|
20
22
|
lfss/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
23
|
lfss/src/config.py,sha256=aTfjWORE9Mx7LSEjbfmHnULlrmIWEvEBSZ4fJKWZNjM,530
|
22
24
|
lfss/src/connection_pool.py,sha256=teW_4DMiwlCN_bS7AhjkbY9cHZqUFlmHE_J2yPjHVsA,5125
|
23
|
-
lfss/src/database.py,sha256
|
25
|
+
lfss/src/database.py,sha256=-itbpGb7cQrywZLFk4aNcuy38Krsyemtyiz8GIt4i7M,31944
|
24
26
|
lfss/src/datatype.py,sha256=BLS7vuuKnFZQg0nrKeP9SymqUhcN6HwPgejU0yBd_Ak,1622
|
25
27
|
lfss/src/error.py,sha256=imbhwnbhnI3HLhkbfICROe3F0gleKrOk4XnqHJDOtuI,285
|
26
28
|
lfss/src/log.py,sha256=xOnkuH-gB_jSVGqNnDVEW05iki6SCJ2xdEhjz5eEsMo,5136
|
27
29
|
lfss/src/server.py,sha256=EA5fK4qc98tF8qoS9F6VaxIE65D5X8Ztkjqy8EUYIv8,16276
|
28
30
|
lfss/src/stat.py,sha256=hTMtQyM_Ukmhc33Bb9FGCfBMIX02KrGHQg8nL7sC8sU,2082
|
29
|
-
lfss/src/utils.py,sha256=
|
30
|
-
lfss-0.7.
|
31
|
-
lfss-0.7.
|
32
|
-
lfss-0.7.
|
33
|
-
lfss-0.7.
|
31
|
+
lfss/src/utils.py,sha256=LXsjNatuwg3iDCKTBgC-t7iEuM2X_mPsgNdDy92zhwo,3405
|
32
|
+
lfss-0.7.7.dist-info/METADATA,sha256=j3VO-GRmoC-_fseS5NAUcUJaOMBUyIx0OS9Amiji9Hg,1967
|
33
|
+
lfss-0.7.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
34
|
+
lfss-0.7.7.dist-info/entry_points.txt,sha256=d_Ri3GXxUW-S0E6q953A8od0YMmUAnZGlJSKS46OiW8,172
|
35
|
+
lfss-0.7.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|