From 01abe7f86fa65b5f36db95e2b4a95b48c202473f Mon Sep 17 00:00:00 2001 From: hz4th_coder Date: Sat, 11 Jul 2026 00:34:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BA=E5=90=84=E7=B1=BB=E5=88=AB=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=95=B0=E6=8D=AE=E5=AF=BC=E5=85=A5=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/app.log | 1 + .../__pycache__/api_cpus.cpython-310.pyc | Bin 4036 -> 5705 bytes .../__pycache__/api_gpus.cpython-310.pyc | Bin 4066 -> 5735 bytes .../__pycache__/api_items.cpython-310.pyc | Bin 4119 -> 5949 bytes .../__pycache__/api_models.cpython-310.pyc | Bin 3935 -> 5622 bytes modules/routes/api_cpus.py | 63 +++- modules/routes/api_gpus.py | 63 +++- modules/routes/api_items.py | 66 +++- modules/routes/api_models.py | 63 +++- templates/admin.html | 296 ++++++++++++------ 10 files changed, 457 insertions(+), 95 deletions(-) diff --git a/logs/app.log b/logs/app.log index 21fe9dd..a1aad73 100644 --- a/logs/app.log +++ b/logs/app.log @@ -13,3 +13,4 @@ WARNING: This is a development server. Do not use it in a production deployment. * Running on http://127.0.0.1:16041 * Running on http://192.168.0.101:16041 Press CTRL+C to quit +127.0.0.1 - - [11/Jul/2026 00:34:38] "GET /api/models/export HTTP/1.1" 200 - diff --git a/modules/routes/__pycache__/api_cpus.cpython-310.pyc b/modules/routes/__pycache__/api_cpus.cpython-310.pyc index 0fdea067282771b3e025d131c4d33e62dbec6140..e568bf6c8a8084428a2d2989b14ddb38457bbfd0 100644 GIT binary patch literal 5705 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 X6mr5b9M;2?$Cbkc8T}58=K22sQknp( delta 1786 zcma)6O>7%Q6!xsw_IkbEBu?TsX+jLhkS$5vL&8sRL(){xN{w*H56TK@+3ZXbC%azj z?Ye1u$)UY+KyyMu0**Ow;);ZjDg-AEh|5ZFMCy$Tm!6n6Ymz7eiAec8^Szlj-+b@Q z{xtJ+$_&e8JtO|UtE?aXYJ6%=octkXG-GEdp^8szB7U>J5-7GpkW>9^pxSDXvvWb- z&Py)m7Xr=J0q~L8^a6Xo(h{WF8ZH zNHT_C%JjsPRE3#`Vi6tKCv@-=g$~h*#^B`NAvN4&Z;D4!~-;zmm511P>>bNlw+?3{lq8OhN7!p61HEB)X4R)!! z^dkQyw=h$|T^iJIWeLF$kQBSn_L$R)C|pO$MxW8@(3kU&e6*0WAmY{}Dc3_|1~FKqpU0k{8>Z_5~x8VsoE$*n=)CHF!;1 z+c{H}sDh5=xu;oJ#GLa2lB`IP-t#TwWPMGy+jcNDWwG1#@$L}{(3Gp9MFvHyz$*Vi z`+4(CK|UX<@66VN**dM&?=05W7V8_Sxnm-@nOclR1?)9hO+X{xwanBgZJ))?VH9@y z4)SNk>#MSB@@`i&GX?C=)G#GcsT;de`Q-*kMa#ZH?F)QcpCT=OU;ldcbxiOE!kKv> zZ!4$L-?v5d4z6hOR_WD~n>buTcn9Gj^xQDQY zfLE~8)b{t0K=0ueg4A%y`%;<(?{S+G;~LrElg4M$X!n0#0pHLju6Tj}Zp^QrtGnRe zdEx){T*`bFGdy-(B$6KnO{ox_7zDPtSN`_@TKN&`X!3hzWBZbovMWS?Z{Z4@SosT2 zXBk;EQofwD@+&yK=1{vYC>Xc%VzX7o|ZpZ)t diff --git a/modules/routes/__pycache__/api_gpus.cpython-310.pyc b/modules/routes/__pycache__/api_gpus.cpython-310.pyc index 511d1321e2dff19caccc02937f19f86a9e04eb28..2a95b63d89ea15c681ec68556a0730accb9b5887 100644 GIT binary patch literal 5735 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 delta 1810 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 delta 1736 zcma)6&u`pB6yCADw%6WxGMM=68w#0!=0we{Ju!KsKB1%J>7J*f2IXlzMCiZ$Y z-lQoK%Ap8x0SS!+r;1kUC0tQ^LmasB7x=(|8~;KhB<79NbrlJTmHh3zZ{EE5-VgiN zxgQ%=SST17{`;+RWqaNF-kLo4lUq3BWH7>#j~vARWnT&;M?%0Uz8oly5@elhpgOAH zvc4ARjxN@!p9>7f2=Y!oFdY+Rh=MgzxGOmpvF;bK{s7?|*1pQzMNUaD250iv6ik^A zYnwaaLKk7{TL~9&No-UEZ{YGVU%_L7A3Nr&$NV_13BD@$`Y6r>J|*~ZENx`$=h!8* zfMoV1n)>A-`Ux$K^Q516^IKs+=EDK$cYN>m{M=}DJES|&!0V9K))$MzEw>Y5Lg(2B zavP1YZ{(j_C4SzPW3(U3JLFC*?|D9jm#1})_Ow{xILT-iF2!6(kJ@^+MZ>PwDr}| zwLDF-KcrbWnWe8n(6<0y19+W7tU$CR)SE!o*qUCgLm?uQLrK!n1X555>4=_ZJNjP- zi8Q|VH#pEY03h;{a`UrRGiWt&qj_<;`O$K7BdKbUZ(d7k+W_Zj00hwFaKW~c;^R$=)z zKpQ~pyv$d2USvq$2X@pO`VN?&J63lGTr#0BNAyF0O8}6Vtv=JX%fOC$N8bfgs1dpr z#kh;?l6WFJaEhuNbP1Z6G}Ay8WI#)=vW6Kv&6=5OGoqdUnQ)!Q*??Oo*paz(GtK2> z4^E8PGb&S&nE2$l1?wOZrXS`Fq?w3bXWtcme=6k=0s2`dId;ExdK(Ht&jUbx=v9F001z{g wCA0|;+Vs^R#JfIe!|N5D<)CDA*+f-d&?+({UFKb%Fp!nwPl-PT{^%C^7x70^jsO4v diff --git a/modules/routes/__pycache__/api_models.cpython-310.pyc b/modules/routes/__pycache__/api_models.cpython-310.pyc index 9a3af040cb42b3ecba3d63d8b9c9ddfc6f76bf8d..125024461e524d200e34342bc65da51e2b9b93e8 100644 GIT binary patch literal 5622 zcmb7ITW=f36`q;BaCyXk@uf+T0X3i7!b|Te(O{U=}G$aaPhwT#}w$ z$~HlzE|efLiU19ewm{<+(nEpzp=n1pO+%QwhgkH%re5aNTbETYVyS02+C>6rKQeW6#>JJA>1DyGe%=m*FqBP_W zZ4FCro6D@syvsIt>5$>%z{$&k;T-mdRxn2PEpgfZfsg}o(D08KeohXh{9$>>@JCYq z;go+wju`%^;g5FL8I$9NKW6wx<*{`9aruzpj~o68c?>gzdGojAgn4pQiaA#NcIyyi zm3#i>oxg0~`O~i_&c6P}xrygreK~g*Y&Kiu+LM*okAu)J+B*9}t>rgWP>*#+`Bz$g zgvLtLs0WMJwfDLoH5>KF$E5tFSI(WkI9I;#^2PHsuhyu@G8U@nY*bnEljdl%q9W7w z%W~SmzzCMx-U}cUgy>gSxt79O-V0EoYl;x!dd}+hpp|_t_$s z8I1J~jP+!8TioR7>N%Om>Upd#Hem*$2+jX2A^YiKCs5r&*=I4 z9IFpvjMouz=mTqSRB?D#Ta{W(^V3z>arYz5(kZX zQK&&;I<>}CU#TIqA&U$Q3n!OG2@XK!xm??1hB_nt#Y(FdYX@_D zrETd))qO`HC3O|^I655$g&3X=h2^VDL%rUT{evkqm z6byIUsuz#2#FMLd5GGlh#0FWphZHBPJnn~nA?!;2!Kr~qp?MJ769+0Lv|Ef9oQKJ z>CWz3oJ2(nKZfgz36NhAdgKW`BpZkH0bD?jHyLMF;AeA;;3AN-iP*_?U}E0x#`lz= zvolM6-M`jUXNt5m9YEYT!P<{>ZL2K(xDwO=2fMhiJ3~8g%Z9o)aETy(qTYxn78|X) zy!#RCyJ+vJ5iSmMGm5%xsi*J`MF-K2*-)|@0+Gb-2M@mbJ9re4p?P8q`hi}!dV%KK zuf8%z_?QWOK%b1NV0M2bGCIy#T+v4$F(dGym=Rlu9Xz%vVyw6XzVC@~2vmw07H76R z#F4DDw&DBIo&kR=CryMw-%0x!qzwZ7Oxn-NoatxNKGFx%&!f+`@*TF#Ig1O>FW(So z^(8T-*f)EZ3{gD+a&Ktz^3-HFH7O@1UwV4-($kZ3iB}Hb;k@xp1C#1enn4(%9tOEk z>`(l}LzpO2Q16v|s})G?5Pe)*%l!xB%k6L@>0K?l(4s|JigU)dNcK(#`iGBsU+-h;N7#z` zF%ffQ@*rhQv?~GiGKTy4VG_hlSt4hMTp%(9Qp_6-suzi|hkiey zE@?u$$;w7~>ZioMNQ6`cM>8HZOAK-=^*j-?&jU`?yFJ=KGz>!OO57?3r5RPd4z_lY z2cDllzfW6aI|M^?MW;b_F|J2P2I1gc0J8e+i3dlI=`_RJCt;Fo*B=(p*_H7B6Gb`#d9y1s+oHgbpSrMqr$-wZ;*B%u-AZCj z_I{nvHjFKOLqB*E;}bvlK1n@pEG^Yish87Zegl6nIEP3*PpeU<=sw6B3|u8DdX7dO zF!iyo+rb=baj*yauxuVUthkKQTfJn)s_2T*YDjs%!W)8J0hfIk z1B81E)q>69?DdPBt=pWn2jiUVkMkXSgPWXTU{Sn<gX{J?_FQ_u%CEBGP-_eu zyz{ru@4Wxn&dnP;w{9kt;?8eB-TB~?1StrB+SU(Foh~}sj;}X;?F6wOM%t+&kjFZo z2*Ol&s7R2WAuqphmtX)TO2My3E#;RhQ8fs(8`P0;#cc$JW-X|i6n>hbQTxzY$~Wur z)VTn4W)x6z54?=yN_9B||Cx!!0I4XdfM-oY5KSeoooW{*FHeS(a`L6gOOtbL=M*i6 z7keRsmf3D**{>k2j|{Tj{WoIbY_YIsGupzs2S8KXw2F3QD{MxJGJl;tf34~_XvqRF$KHPGCW>Se0_f`qOxuL%Atbp@$5G2L{9)|r=N)G&yJ-@k zjx7&4$Xn&%C$Rk)c4Y>fBjZT4Hklk5H)FsP)TeNcr=$DF$9a_D!lY7y+#z+9!u5~g zDz0%AoC|4|o4U$a*Hwnr-FIBLin$wC8E;V_Bv*Ow&-O(qAtIj9Yp>0r5dx_$GsWWLghKRKqAXAh3;s&`S#SH@O=p=|K5c>8C z1PZ4Ii-ZFS4oU=@c9qz(DUvA1xk*dX+TzB;X;e)%m44cJ0I<(eHD~0aVobb?(=pW| zLV2!=h){4#Ggs9l27zmm0a0d=fc$}6=rww9iHJ#37pb)bQq1Ulx+jyc*69I>Lig{= zubcpz3%{k$0iJ&iA3}oql3Jy)#(CGMMaND&c;6wff*t)3L|8dwx1K1NI?+RB+b@!M z;+bf2SmY@9iyW%S)SKVIwBj+HDVJrVS}v=X&{vWOX-zwewMw+A$c>XM&prMq3E6VLmT4KVU zK^jFH>;bVoB=e}&#n+@Wq(GDK*~|lk6#!Ut<%9=uc%wM1hpQh~0aqru503Qn{{cST B4T}H( delta 1580 zcma)6O^@4D6wPyN$8kQJNoLaTq0llW%#1+o0BxGK5-k!{3t$QMMqeCl*L7sE`mybj1&Vf6%?Vs0#m~3s&6w95SP*3nJy@`uM$j&pY?Nyy!eT zQH?4UJBOb?8vSjp^00dB{$KdSb8e0^t_93x_?r*3P;)g#oF3#u-PJ?GHA2%h6=wv6 z&~hymn?W(OT{|qfrLgRl!-`vBIid4{s6No#nyCF)=hh>}i@fkv?g4WTDaJ;o#LLKx znA;GwZCu1FL&mFLYrMwmYU!NfZGLFNH~5_5n-jh{;ahxO@h!zKO!Kt)Va3mLZ9V54 zksq@T%gZm>@!x-9zp!(2c<_HVSGS^2tVUz8GYI^>)s<<`59*1faImN$sF zfK}PgUt$gUF#io}%SFA}sbQAX2%w| z4S~wX5hUK`-2>&%;G0+Q8*2zQm3tk7v;g9^fLJ(1DBv@g1UkLZeHg|{Q)!bgnum8O_1g%kp^{|hd52gknAZLA$jj1mk@%zFG_o-{VQFoqvpTaFT#+AH zPuQ9~SzPEUzpD6t15P0VwE{W>)B_-ct~u4r0Ba}`Z##~5CeBDGzHwFc+VsabEF3J_ zLsA_r)w!SeO6zRnl*p{xnbsBgym*q`l+1p-e3nw2BbZtS>U`72!+ZGl3avOVpW4p- z3lw$2t?%nQ;NgC|Us@T5NF*XFO!8ye zYH1B-voeDX`J(*i|F!c|Qg~keU3qKPPBj}i+3U1|9vt&!W@dL)X}?4(XPj!&Tqe/export', methods=['GET']) +def api_export_items(category_id): + """导出指定分类的所有数据""" + try: + items_file = DATA_DIR / f'items_{category_id}.json' + items = load_data(items_file) + export_data = { + 'type': 'items', + 'category_id': category_id, + 'items': items, + 'count': len(items), + 'export_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'version': '1.0' + } + json_str = json.dumps(export_data, ensure_ascii=False, indent=2) + response = Response( + json_str, + mimetype='application/json', + headers={'Content-Disposition': f'attachment; filename={category_id}-export-{datetime.now().strftime("%Y%m%d%H%M%S")}.json'} + ) + return response + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@items_bp.route('/api/items//import', methods=['GET', 'POST']) +def api_import_items(category_id): + """导入指定分类的数据""" + try: + if request.method == 'GET': + return jsonify({'endpoint': f'/api/items/{category_id}/import', 'method': 'POST', 'params': {'mode': 'merge 或 replace'}}) + + import_data = request.get_json() + if not import_data or 'items' not in import_data: + return jsonify({'error': '缺少 items 字段'}), 400 + + mode = request.args.get('mode', 'merge') + items_file = DATA_DIR / f'items_{category_id}.json' + items = load_data(items_file) + imported_items = import_data['items'] + + result = {'success': True, 'imported': 0, 'updated': 0, 'skipped': []} + + for item in imported_items: + existing = next((i for i in items if i['id'] == item['id']), None) + if existing: + if mode == 'replace': + existing.update(item) + result['updated'] += 1 + else: + result['skipped'].append(item['id']) + else: + items.append(item) + result['imported'] += 1 + + save_data(items_file, items) + return jsonify(result) + except Exception as e: + return jsonify({'error': str(e)}), 500 diff --git a/modules/routes/api_models.py b/modules/routes/api_models.py index c58f017..6287dcd 100644 --- a/modules/routes/api_models.py +++ b/modules/routes/api_models.py @@ -2,8 +2,9 @@ AI模型 CRUD API """ import uuid +import json from datetime import datetime -from flask import Blueprint, request, jsonify +from flask import Blueprint, request, jsonify, Response from config import MODELS_FILE from utils import load_data, save_data, parse_date_to_timestamp, safe_sort_key @@ -88,3 +89,63 @@ def api_toggle_model_visible(model_id): model['visible'] = not model.get('visible', True) save_data(MODELS_FILE, models) return jsonify({'success': True, 'visible': model['visible']}) + + +# ─── 导出导入 ─────────────────────────────────────────────────────── + +@models_bp.route('/api/models/export', methods=['GET']) +def api_export_models(): + """导出所有模型数据""" + try: + models = load_data(MODELS_FILE) + export_data = { + 'type': 'models', + 'items': models, + 'count': len(models), + 'export_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'version': '1.0' + } + json_str = json.dumps(export_data, ensure_ascii=False, indent=2) + response = Response( + json_str, + mimetype='application/json', + headers={'Content-Disposition': f'attachment; filename=models-export-{datetime.now().strftime("%Y%m%d%H%M%S")}.json'} + ) + return response + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@models_bp.route('/api/models/import', methods=['GET', 'POST']) +def api_import_models(): + """导入模型数据""" + try: + if request.method == 'GET': + return jsonify({'endpoint': '/api/models/import', 'method': 'POST', 'params': {'mode': 'merge 或 replace'}}) + + import_data = request.get_json() + if not import_data or 'items' not in import_data: + return jsonify({'error': '缺少 items 字段'}), 400 + + mode = request.args.get('mode', 'merge') + models = load_data(MODELS_FILE) + imported_items = import_data['items'] + + result = {'success': True, 'imported': 0, 'updated': 0, 'skipped': []} + + for item in imported_items: + existing = next((m for m in models if m['id'] == item['id']), None) + if existing: + if mode == 'replace': + existing.update(item) + result['updated'] += 1 + else: + result['skipped'].append(item['id']) + else: + models.append(item) + result['imported'] += 1 + + save_data(MODELS_FILE, models) + return jsonify(result) + except Exception as e: + return jsonify({'error': str(e)}), 500 diff --git a/templates/admin.html b/templates/admin.html index e1104d6..3f5c98b 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -160,6 +160,8 @@

数据管理

+ +
@@ -179,6 +181,8 @@

大模型管理

+ +
@@ -200,6 +204,8 @@

GPU管理

+ +
@@ -221,6 +227,8 @@

CPU管理

+ +
@@ -501,20 +509,18 @@