bep40 commited on
Commit
62bd220
·
verified ·
1 Parent(s): 269db86

Serve clean index_v2.html frontend, remove comments, mount /static

Browse files
Files changed (1) hide show
  1. app_main.py +32 -93
app_main.py CHANGED
@@ -1,12 +1,13 @@
1
- """Restructured homepage with CSS-first approach to hide news."""
2
  from app_run import *
3
  from app_run import app, f5, f6, rt, PATCH_INJECT, UNIFIED_INJECT_FIXED, HIGHLIGHT_FULL_OVERRIDE, EXTRA_WALL_FIX, FAST_HASHTAG_JS
4
- from fastapi.responses import HTMLResponse, JSONResponse
 
5
  from fastapi import Query
6
  import requests as req
7
  from urllib.parse import quote
8
  from bs4 import BeautifulSoup
9
- import re, html as html_lib
10
  from concurrent.futures import ThreadPoolExecutor, as_completed
11
 
12
  def _clean(s):return re.sub(r"\s+"," ",html_lib.unescape(str(s or ""))).strip()
@@ -103,9 +104,11 @@ def _search_all(topic, limit=30):
103
  if i.get('url') and i['url'] not in seen:seen.add(i['url']);unique.append(i)
104
  return unique[:limit]
105
 
 
106
  app.router.routes=[r for r in app.router.routes if not (
107
  (getattr(r,'path',None)=='/api/hashtag/sources' and 'GET' in getattr(r,'methods',set())) or
108
- (getattr(r,'path',None)=='/' and 'GET' in getattr(r,'methods',set()))
 
109
  )]
110
 
111
  @app.get('/api/hashtag/sources')
@@ -114,97 +117,33 @@ def _ht(topic:str=Query(...), page:int=Query(default=0)):
114
  per_page=6;start=page*per_page;end=start+per_page
115
  return JSONResponse({'sources':all_items[start:end],'topic':topic,'page':page,'has_more':end<len(all_items),'total':len(all_items)})
116
 
117
- # CSS hides news grids INSTANTLY (no JS delay). Shorts/Wall/AI/Livescore use .slider-wrap/.ls-section — not affected.
118
- HOMEPAGE_RESTRUCTURE_JS = r'''
119
- <style>
120
- /* INSTANTLY hide all news grids on homepage — they use .section-title + .grid directly under #view-home */
121
- #view-home>.section-title,#view-home>.grid{display:none!important}
122
- </style>
123
- <script>
124
- (function(){
125
- function esc(s){return String(s||'').replace(/[&<>"']/g,function(m){return{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]});}
126
 
127
- // Add "Tin tức" tab
128
- function addNewsTab(){
129
- var bar=document.getElementById('cat-bar');
130
- if(!bar||bar.querySelector('[data-cat="news-all"]'))return;
131
- var btn=document.createElement('div');
132
- btn.className='cat';btn.dataset.cat='news-all';btn.textContent='📰 Tin tức';
133
- btn.onclick=function(){switchToNewsTab();};
134
- var videoTab=bar.querySelector('[data-cat="video"]');
135
- if(videoTab)videoTab.after(btn);else bar.appendChild(btn);
136
- }
137
- function switchToNewsTab(){
138
- document.querySelectorAll('.cat').forEach(function(c){c.classList.remove('active')});
139
- document.querySelector('[data-cat="news-all"]')?.classList.add('active');
140
- showView('view-cat');loadNewsTab();
141
- }
142
- async function loadNewsTab(){
143
- var el=document.getElementById('view-cat');
144
- el.innerHTML='<div class="loading">Đang tải tin tức...</div>';
145
- try{
146
- var r=await fetch('/api/homepage');var news=await r.json();
147
- if(!news.length){el.innerHTML='<div class="loading">Không có tin</div>';return;}
148
- var groups={};news.forEach(function(a){if(!groups[a.group])groups[a.group]=[];groups[a.group].push(a);});
149
- var h='';
150
- for(var g in groups){var arts=groups[g];h+='<div class="section-title">'+esc(g)+'</div><div class="grid">';arts.slice(0,6).forEach(function(a){var bg=a.source==='bbc'?'badge-bbc':a.source==='dantri'?'badge-dt':'badge-vne';h+='<div class="card" onclick="readArticle(\''+esc(a.link)+'\')"><div class="card-img">'+(a.img?'<img src="'+esc(a.img)+'">':'')+'</div><div class="card-body"><span class="badge '+bg+'">'+esc(a.source)+'</span><div class="card-title">'+esc(a.title)+'</div></div></div>';});h+='</div>';}
151
- el.innerHTML=h;
152
- }catch(e){el.innerHTML='<div class="loading">Lỗi</div>';}
153
- }
154
 
155
- // Auto-load first hashtag
156
- function autoLoadFirst(){
157
- var chips=document.querySelectorAll('.hot-chip[data-fixed]');
158
- if(chips.length>0){
159
- var first=chips[0];var topic=first.dataset.topic||first.textContent.replace(/^#/,'').trim();
160
- if(topic&&typeof showHashtagSources==='function'){showHashtagSources(topic);return true;}
161
- }
162
- return false;
163
- }
164
 
165
- setTimeout(addNewsTab,2000);
166
- var _at=0;var _ai=setInterval(function(){_at++;if(autoLoadFirst()||_at>12)clearInterval(_ai);},2000);
167
- })();
168
- </script>
169
- '''
170
-
171
- HASHTAG_FIX_JS = r'''
172
- <script>
173
- (function(){
174
- function patchChips(){
175
- document.querySelectorAll('.hot-chip:not([data-fixed])').forEach(function(chip){
176
- chip.dataset.fixed='1';
177
- var oc=chip.getAttribute('onclick')||'';
178
- var m=oc.match(/\.value='([^']+)'/);
179
- var topic=m?m[1]:chip.textContent.replace(/^#/,'').trim();
180
- chip.dataset.topic=topic;
181
- chip.removeAttribute('onclick');
182
- chip.addEventListener('click',function(e){
183
- e.preventDefault();e.stopPropagation();
184
- if(typeof showHashtagSources==='function')showHashtagSources(topic);
185
- });
186
- });
187
- }
188
- setTimeout(patchChips,1500);
189
- setInterval(patchChips,2000);
190
- })();
191
- </script>
192
- '''
193
 
 
194
  @app.get('/')
195
- async def _index_final():
196
- html=f5.f4.f3.f2.f1._load_index_html()
197
- body=''
198
- body+=getattr(rt.old,'PATCH_INJECT','')
199
- body+=f5.f4.f3.f2.f1.FINAL_INJECT+f5.f4.f3.FINAL3_INJECT+f5.f4.FINAL4_INJECT+f5.FINAL5_INJECT
200
- body+=getattr(f6,'FINAL6_INJECT','')
201
- body+=getattr(f6,'FINAL6_FAST_HOME_INJECT','')
202
- body+=getattr(f6,'FINAL6E_INJECT','')
203
- body+=PATCH_INJECT
204
- body+=UNIFIED_INJECT_FIXED
205
- body+=HIGHLIGHT_FULL_OVERRIDE
206
- body+=EXTRA_WALL_FIX
207
- body+=FAST_HASHTAG_JS
208
- body+=HASHTAG_FIX_JS
209
- body+=HOMEPAGE_RESTRUCTURE_JS
210
- return HTMLResponse(html.replace('</body>',body+'\n</body>') if '</body>' in html else html+body)
 
1
+ """VNEWS v2 - Clean frontend served from static/index_v2.html. Comments removed."""
2
  from app_run import *
3
  from app_run import app, f5, f6, rt, PATCH_INJECT, UNIFIED_INJECT_FIXED, HIGHLIGHT_FULL_OVERRIDE, EXTRA_WALL_FIX, FAST_HASHTAG_JS
4
+ from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
5
+ from fastapi.staticfiles import StaticFiles
6
  from fastapi import Query
7
  import requests as req
8
  from urllib.parse import quote
9
  from bs4 import BeautifulSoup
10
+ import re, html as html_lib, os
11
  from concurrent.futures import ThreadPoolExecutor, as_completed
12
 
13
  def _clean(s):return re.sub(r"\s+"," ",html_lib.unescape(str(s or ""))).strip()
 
104
  if i.get('url') and i['url'] not in seen:seen.add(i['url']);unique.append(i)
105
  return unique[:limit]
106
 
107
+ # Remove old routes we're overriding
108
  app.router.routes=[r for r in app.router.routes if not (
109
  (getattr(r,'path',None)=='/api/hashtag/sources' and 'GET' in getattr(r,'methods',set())) or
110
+ (getattr(r,'path',None)=='/' and 'GET' in getattr(r,'methods',set())) or
111
+ (getattr(r,'path',None) in ('/api/short/comments','/api/short/comment'))
112
  )]
113
 
114
  @app.get('/api/hashtag/sources')
 
117
  per_page=6;start=page*per_page;end=start+per_page
118
  return JSONResponse({'sources':all_items[start:end],'topic':topic,'page':page,'has_more':end<len(all_items),'total':len(all_items)})
119
 
120
+ # Mount static files directory
121
+ STATIC_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
122
+ app.mount('/static', StaticFiles(directory=STATIC_DIR), name='static')
 
 
 
 
 
 
123
 
124
+ # Categories endpoint for v2 frontend
125
+ @app.get('/api/categories')
126
+ def _categories():
127
+ return JSONResponse([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
+ # Storage status
130
+ @app.get('/api/storage_status')
131
+ def _storage():
132
+ data_dir='/data'
133
+ return JSONResponse({'persistent':os.path.isdir(data_dir) and os.access(data_dir, os.W_OK)})
 
 
 
 
134
 
135
+ # Share page
136
+ @app.get('/s')
137
+ async def _share(url:str='',title:str='',img:str=''):
138
+ html=f'<!DOCTYPE html><html><head><meta property="og:title" content="{_clean(title)}"><meta property="og:url" content="{_clean(url)}"><meta property="og:image" content="{_clean(img)}"><meta property="og:type" content="article"><meta property="og:site_name" content="VNEWS"><meta http-equiv="refresh" content="0;url={_clean(url) or "/"}"></head><body>Redirecting...</body></html>'
139
+ return HTMLResponse(html)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
+ # === SERVE CLEAN V2 FRONTEND ===
142
  @app.get('/')
143
+ async def _index_v2():
144
+ """Serve the clean v2 frontend - no injection layers."""
145
+ index_path = os.path.join(STATIC_DIR, 'index_v2.html')
146
+ if os.path.exists(index_path):
147
+ return FileResponse(index_path, media_type='text/html')
148
+ # Fallback: old method if file missing
149
+ return HTMLResponse('<html><body><h1>VNEWS</h1><p>Building...</p></body></html>')