feat: streamline portfolio UI and support atomic record edits
This commit is contained in:
1 parent
932a864ed6
commit
9adf7fdbc5
26 files changed
+988
-112
No files matched your search
@@ -0,0 +1,20 @@
|
||||
require('dotenv/config');
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
const { writeFileSync } = require('node:fs');
|
||||
const { resolve } = require('node:path');
|
||||
const db = new PrismaClient();
|
||||
(async () => {
|
||||
const tables = await db.$queryRaw`SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME <> '_prisma_migrations' ORDER BY TABLE_NAME`;
|
||||
const columns = await db.$queryRaw`SELECT TABLE_NAME, COLUMN_NAME, COLUMN_COMMENT FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME <> '_prisma_migrations' ORDER BY TABLE_NAME, ORDINAL_POSITION`;
|
||||
if (tables.some((t) => !t.TABLE_COMMENT) || columns.some((c) => !c.COLUMN_COMMENT))
|
||||
throw Error('Application tables or columns are missing comments.');
|
||||
writeFileSync(resolve(__dirname, '../../../docs/database-comments.json'), JSON.stringify({
|
||||
tableCount: tables.length, columnCount: columns.length, tables, columns,
|
||||
}, null, 2) + '\n');
|
||||
console.log(`Database comments verified: ${tables.length} tables, ${columns.length} columns.`);
|
||||
})().catch(() => {
|
||||
console.error('Comment verification failed. Check database access and table/column comments.');
|
||||
process.exitCode = 1;
|
||||
}).finally(() => db.$disconnect());
|
||||
@@ -1,16 +1,26 @@
|
||||
"""Fetch public brand icons from the listed official websites; keep existing catalog."""
|
||||
import hashlib, io, json, re, urllib.request, urllib.parse
|
||||
import hashlib, io, json, re, sys, urllib.request, urllib.parse
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
folder = Path(__file__).resolve().parent.parent / 'assets' / 'icons'
|
||||
catalog = json.loads((folder / 'sources.json').read_text(encoding='utf-8'))
|
||||
brands = [('汇丰香港', 'https://www.hsbc.com.hk'), ('恒生银行', 'https://www.hangseng.com'), ('中银香港', 'https://www.bochk.com/en'), ('渣打香港', 'https://www.sc.com/hk'), ('东亚银行', 'https://www.hkbea.com'), ('星展香港', 'https://www.dbs.com.hk'), ('花旗香港', 'https://www.citibank.com.hk'), ('币安 Binance', 'https://www.binance.com'), ('Gate', 'https://www.gate.com'), ('Bybit', 'https://www.bybit.com'), ('Bitget', 'https://www.bitget.com')]
|
||||
brands += [('ZA BANK 众安银行', 'https://bank.za.group'), ('大象银行 EleBank', 'https://www.elebank.com'), ('微众银行 WeBank', 'https://www.webank.com')]
|
||||
if len(sys.argv) > 1:
|
||||
brands = [item for item in brands if item[0] in sys.argv[1:]]
|
||||
|
||||
def get(url):
|
||||
return urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}), timeout=20).read()
|
||||
failed = []
|
||||
for name, site in brands:
|
||||
if any(v['name'] == name for v in catalog): continue
|
||||
urls = {'币安 Binance': ['https://bin.bnbstatic.com/static/images/common/favicon.ico', 'https://bin.bnbstatic.com/static/images/common/favicon.png'], '中银香港': ['https://www.bochk.com/dam/bochk/desktop/top/favicon.ico', 'https://www.bochk.com/favicon.ico']}.get(name, [])
|
||||
urls = {'币安 Binance': ['https://bin.bnbstatic.com/static/images/common/favicon.ico', 'https://bin.bnbstatic.com/static/images/common/favicon.png'], '中银香港': ['https://www.bochk.com/dam/bochk/desktop/top/favicon.ico', 'https://www.bochk.com/favicon.ico'], '大象银行 EleBank': ['https://is1-ssl.mzstatic.com/image/thumb/Purple211/v4/d4/bc/60/d4bc6004-3898-c9c3-3571-f3ffe4b46b48/Bank-Airstar-0-0-1x_U007epad-0-1-sRGB-85-220.png/512x512bb.jpg']}.get(name, [])
|
||||
if name == '微众银行 WeBank':
|
||||
try:
|
||||
script = get('https://tctp.tctpwebankcdn.net/owb-res/owb-res/assets/mainGenerator.811f2078.js').decode('utf-8')
|
||||
logo = re.search(r'Tr="(data:image/png;base64,[^"]+)"', script)
|
||||
if logo: urls.insert(0, logo.group(1))
|
||||
except Exception: pass
|
||||
try:
|
||||
html = get(site).decode('utf-8', errors='replace')
|
||||
for tag in re.findall(r'<link\b[^>]*>', html, re.I):
|
||||
@@ -26,7 +36,7 @@ for name, site in brands:
|
||||
out = io.BytesIO(); im.save(out, 'PNG'); png = out.getvalue()
|
||||
file = str(len(catalog) + 1).zfill(2) + '.png'
|
||||
(folder / file).write_bytes(png)
|
||||
catalog.append({'name':name,'source':url,'file':file,'sha256':hashlib.sha256(raw).hexdigest()})
|
||||
catalog.append({'name':name,'source':site if url.startswith('data:') else url,'file':file,'sha256':hashlib.sha256(raw).hexdigest()})
|
||||
print('Added:', name)
|
||||
break
|
||||
except Exception: continue
|
||||
|
||||
Reference in new issue
Block a user