Ë
    ®¿¦j„$  ã                  ó  — d dl mZ d dlZd dlZd dlZd dlZd dlZd dlZd dlmZm	Z	 d dl
mZ d dlmZmZmZmZ d dlmZmZmZ d dlmZ  e ej.                  dd	«      «      j1                  «       Zej5                  d
d
¬«       edz  Zedz  Zej5                  d
¬«        ej.                  dd«      Z ej.                  dd«      Z edddd¬«      Zd„ Z d„ Z! e!«        d2d„Z"d„ Z#d„ Z$ G d„ de«      Z%ejM                  d«      d„ «       Z'ejQ                  d«      d3d„«       Z)ejM                  d «      d!„ «       Z*ejM                  d"«      d4d#„«       Z+ejM                  d$e¬%«      d2d&„«       Z,ejQ                  d'«      d2d(„«       Z-ejQ                  d)«      d*„ «       Z. G d+„ d,e«      Z/ejQ                  d-«       ee"«      fd5d.„«       Z0ejc                  d/«       ee"«      fd6d0„«       Z2ejg                  d/«       ee"«      fd6d1„«       Z3y)7é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚPath)ÚFastAPIÚRequestÚHTTPExceptionÚDepends)ÚHTMLResponseÚJSONResponseÚFileResponse)Ú	BaseModelÚSTREAMFORGE_LICENSE_DATAz./license_server/dataT)ÚparentsÚexist_okzlicences.dbÚupdates)r   ÚSTREAMFORGE_LICENSE_ADMINÚadminÚSTREAMFORGE_LICENSE_PASSWORDzchange-me-nowzStreamForge Licence Server)ÚtitleÚdocs_urlÚ	redoc_urlÚopenapi_urlc                 ób   — t        j                  t        «      } t         j                  | _        | S )N)Úsqlite3ÚconnectÚDBÚRowÚrow_factory©Úcs    ú+/var/www/html/streamforge-license/server.pyÚconnr#      s   € Ü
‡?�?”2Ó€¤W§[¡[�q”}¸°ó    c                 ód   — t        «       5 } | j                  d«       d d d «       y # 1 sw Y   y xY w)Na  CREATE TABLE IF NOT EXISTS licences(id INTEGER PRIMARY KEY AUTOINCREMENT,identifier TEXT UNIQUE NOT NULL,identifier_type TEXT NOT NULL CHECK(identifier_type IN ('ip','domain')),plan TEXT NOT NULL CHECK(plan IN ('basic','premium','legend')),status TEXT DEFAULT 'active',customer TEXT DEFAULT '',notes TEXT DEFAULT '',created_at TEXT DEFAULT CURRENT_TIMESTAMP,updated_at TEXT DEFAULT CURRENT_TIMESTAMP,last_seen_at TEXT,last_install_id TEXT DEFAULT '');CREATE TABLE IF NOT EXISTS sessions(token_hash TEXT PRIMARY KEY,expires REAL NOT NULL);)r#   Úexecutescriptr    s    r"   Úinitr'      s4   € Ü
ƒfð  D	��!—/‘/ð  #C	ô  D	÷  D	÷  D	ñ  D	ús   ‹&¦/c                ó„  — | j                   j                  d«      }|st        d«      ‚t        j                  |j                  «       «      j                  «       }t        «       5 }|j                  d|f«      j                  «       }d d d «       r|d   t        j                  «       k  rt        d«      ‚y# 1 sw Y   Œ1xY w)NÚsflsé‘  z/SELECT expires FROM sessions WHERE token_hash=?ÚexpiresT)ÚcookiesÚgetr	   ÚhashlibÚsha256ÚencodeÚ	hexdigestr#   ÚexecuteÚfetchoneÚtime)ÚreqÚrawÚhr!   Úrs        r"   Úauthr9      s“   € Ø‡[�[‡_�_�VÓ€Ù”- Ó$Ð$Ü
‡>�>�#—*‘*“,Ó×)Ñ)Ó+€Ü
ƒfÐ`��A—I‘IÐOÐQRÐPTÓU×^Ñ^Ó`�!×`Ù	ˆQˆy‰\œ$Ÿ)™)›+Ò%¬M¸#Ó,>Ð&>Ø÷ aÐ`ús   Á$"B6Â6B?c                ó€   — | j                  «       j                  «       j                  d«      d   j                  d«      S )Nú:r   ú.)ÚstripÚlowerÚsplitÚrstrip)Úds    r"   Únormalize_domainrB      s/   € ˜qŸw™w›yŸ™Ó0×6Ñ6°sÓ;¸AÑ>×EÑEÀcÓJÐJr$   c                óJ   — | j                   r| j                   j                  S dS )NÚ )ÚclientÚhost)r5   s    r"   Ú	client_iprG      s   € àŸ:š:ˆ�
‰
�‰Ð-¨2Ð-r$   c                  ó*   — e Zd ZU dZded<   dZded<   y)ÚCheckrD   ÚstrÚdomainÚ
install_idN)Ú__name__Ú
__module__Ú__qualname__rK   Ú__annotations__rL   © r$   r"   rI   rI       s   …  "�v˜c“}°B Z°Ô%6r$   rI   z/healthc                 ól   — ddt        j                  t        j                  «      j	                  «       dœS )NTzstreamforge-licence-server)ÚokÚservicer4   )r   Únowr   ÚutcÚ	isoformatrQ   r$   r"   ÚhealthrX   !   s)   € ØÐ)EÌXÏ\É\ÔZb×ZfÑZfÓMg×MqÑMqÓMsÑtÐtr$   z	/v1/checkc           	     óò  — t        |«      }t        | j                  «      }t        «       5 }d }|r!|j	                  d|f«      j                  «       }|s#|r!|j	                  d|f«      j                  «       }|sdddd||dœcd d d «       S |j	                  d| j                  |d   f«       |j                  «        |d   }d	||d
v |dk(  ||d   t        j                  «       dœcd d d «       S # 1 sw Y   y xY w)NzaSELECT * FROM licences WHERE identifier_type='domain' AND lower(identifier)=? AND status='active'zVSELECT * FROM licences WHERE identifier_type='ip' AND identifier=? AND status='active'FÚnone)ÚvalidÚplanr   Úload_balancingÚobserved_iprK   zlUPDATE licences SET last_seen_at=CURRENT_TIMESTAMP,last_install_id=?,updated_at=CURRENT_TIMESTAMP WHERE id=?Úidr\   T)ÚpremiumÚlegendra   Ú
identifier)r[   r\   r   r]   r^   Úmatched_identifierÚlast_valid_unix)	rG   rB   rK   r#   r2   r3   rL   Úcommitr4   )rA   r5   ÚiprK   r!   r8   r\   s          r"   Úcheckrg   #   s…  € äˆcƒN€Ô*¨1¯8©8Ó4�6Ü
ƒfð �Ø€!Ùˆa�i‰iÐ{ð  ~Dð  }Fó  G÷  Pñ  Pó  RˆAÙ	
‰r�A—I‘IÐvÐxzÐw|Ó}÷  Gñ  Gó  I�!Ù	
˜5¨¸ÐPUÐdfÐpvÑw÷	ñ ð
 ‡)�)ÐzÐ|}÷  }Iñ  }Ið  JKð  LPñ  JQð  |Rô  Sð  TU÷  T\ñ  T\ô  T^ð  deð  flñ  dmð  _cØ˜d¨TÐ5IÐ-IÐ[_ÐaiÑ[iÐxzð  QRð  S_ñ  Q`ô  sw÷  s|ñ  s|ó  s~ñ  
÷÷ ò ús   «AC-Â
AC-Ã-C6z/v1/updates/stable.jsonc                 ó†   — t         dz  } | j                  «       sddddœS t        j                  | j	                  «       «      S )Nzstable.jsonrD   )ÚversionÚurlr/   )ÚUPDATESÚexistsÚjsonÚloadsÚ	read_text)Úps    r"   Úupdate_manifestrq   -   s6   € ä
ˆ=Ñ€Ø	�‰Œ
 R¨b¸"Ñ=Ð=Ü�
‰
�1—;‘;“=Ó!Ð!r$   z/v1/updates/files/{name}c                ó†   — t         t        | «      j                  z  }|j                  «       st	        d«      ‚t        |«      S )Ni”  )rk   r   Únamerl   r	   r   )rs   rp   s     r"   Úupdate_filert   2   s1   € äŒD�‹J�O‰OÑ€Ø	�‰Œ
œ sÓ+Ð+Ü�Q‹Ðr$   ú/)Úresponse_classc                óJ  — t        | «       d}|st        d«      S t        «       5 }|j                  d«      D �cg c]  }t	        |«      ‘Œ }}d d d «       t        j                  «      j                  dd«      }t        d|z   dz   «      S #  d}Y ŒƒxY wc c}w # 1 sw Y   ŒMxY w)	NTFaÜ  <!doctype html><html><head><meta name=viewport content="width=device-width"><title>StreamForge Licence Server</title><style>body{font-family:system-ui;background:#080914;color:#eee;display:grid;place-items:center;height:100vh;margin:0}.c{width:380px;background:#101322;border:1px solid #34364b;padding:30px;border-radius:12px}input,button{width:100%;box-sizing:border-box;padding:12px;margin:7px 0;background:#171a2b;color:white;border:1px solid #383b55;border-radius:7px}button{background:#6519c9}</style></head><body><div class=c><h2>StreamForge Licence Server</h2><form id=f><input id=u placeholder=Username><input id=p type=password placeholder=Password><button>Sign in</button></form><div id=e></div></div><script>f.onsubmit=async x=>{x.preventDefault();let r=await fetch('/login',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({username:u.value,password:p.value})});if(r.ok)location.reload();else e.textContent='Invalid login'}</script></body></html>z'SELECT * FROM licences ORDER BY id DESCz</z<\/u±  <!doctype html><html><head><meta name=viewport content="width=device-width"><title>StreamForge Licence Server</title><style>body{font-family:system-ui;background:#070812;color:#eee;margin:0;padding:28px}h1{margin-top:0}.top{display:flex;justify-content:space-between}.card{background:#0f1220;border:1px solid #2d3042;border-radius:10px;padding:20px;margin:18px 0}input,select,button{padding:10px;background:#171a2a;color:white;border:1px solid #3b3e52;border-radius:6px}button{cursor:pointer;background:#6017c5}table{width:100%;border-collapse:collapse}td,th{padding:11px;border-bottom:1px solid #242738;text-align:left}.good{color:#50e37b}</style></head><body><div class=top><div><h1>StreamForge Licence Server</h1><div class=good>â—� Online</div></div><button onclick="fetch('/logout',{method:'POST'}).then(()=>location.reload())">Logout</button></div><div class=card><h3>Register IP / Domain</h3><input id=i placeholder="203.0.113.10 or panel.example.com"><select id=t><option value=ip>Public IP</option><option value=domain>Domain</option></select><select id=p><option>basic</option><option>premium</option><option>legend</option></select><input id=c placeholder="Customer"><input id=n placeholder="Notes"><button onclick=add()>Register</button></div><div class=card><table><thead><tr><th>Identifier</th><th>Type</th><th>Plan</th><th>Status</th><th>Customer</th><th>Last seen</th><th></th></tr></thead><tbody id=rows></tbody></table></div><script>let D=aò  ;function draw(){rows.innerHTML=D.map(x=>`<tr><td>${x.identifier}</td><td>${x.identifier_type}</td><td><select onchange="edit(${x.id},'plan',this.value)"><option ${x.plan=='basic'?'selected':''}>basic</option><option ${x.plan=='premium'?'selected':''}>premium</option><option ${x.plan=='legend'?'selected':''}>legend</option></select></td><td><select onchange="edit(${x.id},'status',this.value)"><option ${x.status=='active'?'selected':''}>active</option><option ${x.status=='suspended'?'selected':''}>suspended</option></select></td><td>${x.customer||''}</td><td>${x.last_seen_at||'Never'}</td><td><button onclick="del(${x.id})">Delete</button></td></tr>`).join('')}draw();async function add(){let r=await fetch('/admin/licences',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({identifier:i.value,identifier_type:t.value,plan:p.value,customer:c.value,notes:n.value})});if(r.ok)location.reload();else alert((await r.json()).detail)}async function edit(id,k,v){await fetch('/admin/licences/'+id,{method:'PATCH',headers:{'content-type':'application/json'},body:JSON.stringify({[k]:v})})}async function del(id){if(confirm('Delete licence?')){await fetch('/admin/licences/'+id,{method:'DELETE'});location.reload()}}</script></body></html>)r9   r   r#   r2   Údictrm   ÚdumpsÚreplace)r5   Úloggedr!   ÚxÚrowsÚdatas         r"   Úhomer   7   s¯   € ä	ˆ#„Y�dˆvá”lð  $Fó  Gð  GÜ
ƒfÐ^�¨¯©Ð3\Ó)]Ö^ Aœ˜Q�Ð^�$Ð^×^Ü
‡j�j�Ó×Ñ˜t FÓ+€Üð  Mð  NRñ  Rð  SK+ñ  K+ó  	L+ð  L+øð	 ˆuŠüâ^×^Ð^ús'   �B ¥B¹BÁBÂBÂBÂB"z/loginc              ƒ  ó¨  K  — | j                  «       ƒ d {  –—† }t        j                  t        |j	                  dd«      «      t
        «      r3t        j                  t        |j	                  dd«      «      t        «      st        d«      ‚t        j                  d«      }t        j                  |j                  «       «      j                  «       }t        «       5 }|j                  d|t        j                  «       dz   f«       |j!                  «        d d d «       t#        dd	i«      }|j%                  d
|d	dd¬«       |S 7 �Œ-# 1 sw Y   Œ1xY w­w)NÚusernamerD   Úpasswordr*   é    z4INSERT INTO sessions(token_hash,expires) VALUES(?,?)i€Q rS   Tr)   Ústrict)ÚhttponlyÚsamesiteÚmax_age)rm   ÚsecretsÚcompare_digestrJ   r-   Ú
ADMIN_USERÚ
ADMIN_PASSr	   Útoken_urlsafer.   r/   r0   r1   r#   r2   r4   re   r   Ú
set_cookie)r5   rA   r6   r7   r!   r8   s         r"   ÚloginrŽ   ?   s   è ø€ à	�‰‹×€Ü×Ñœs 1§5¡5¨°BÓ#7Ó8¼ÔDÌG×LbÑLbÔcfÐgh×glÑglÐmwÐxzÓg{Óc|ô  ~Hô  MIô  P]ð  ^aó  Pbð  JbÜ×Ñ˜2Ó€¤§¡°·
±
³Ó!=×!GÑ!GÓ!I˜qÜ
ƒfÐt��!—)‘)ÐRÐTUÔVZ×V_ÑV_ÓVaÐbgÑVgÐShÔiÐjk×jrÑjrÔjt×tÜ��d�Ó€˜QŸ\™\¨&°¸dÈHÐ]b˜\ÔcÐklÐdlð	 ù÷ uÐtüs(   ‚E–E—CEÃ:EÄ.EÅEÅEz/logoutc                 óB   — t        ddi«      } | j                  d«       | S )NrS   Tr)   )r   Údelete_cookie)r8   s    r"   Úlogoutr‘   F   s   € Ü˜T $˜KÓ(ˆQ¨¯©¸Ô)@ÈÀr$   c                  óH   — e Zd ZU ded<   ded<   ded<   dZded<   dZded<   y)	ÚLicInrJ   rb   Úidentifier_typer\   rD   ÚcustomerÚnotesN)rM   rN   rO   rP   r•   r–   rQ   r$   r"   r“   r“   H   s$   …  #“~°cÓ&9¸s»(ÐPRÀ8ÈCÃ?Ð]_ÐSXÐY\ÔS_r$   r“   z/admin/licencesc           	     ó  — | j                   dk(  rt        | j                  «      n| j                  j                  «       }| j                   dvs| j                  dvrt        dd«      ‚	 t        «       5 }|j                  d|| j                   | j                  | j                  | j                  f«      }|j                  «        d|j                  icd d d «       S # 1 sw Y   y xY w# t        $ r}t        dt        |«      «      ‚d }~ww xY w)NrK   )rf   rK   )Úbasicr`   ra   i�  zInvalid licence fieldszVINSERT INTO licences(identifier,identifier_type,plan,customer,notes) VALUES(?,?,?,?,?)r_   )r”   rB   rb   r=   r\   r	   r#   r2   r•   r–   re   Ú	lastrowidÚ	ExceptionrJ   )r|   r5   Ú_Úidentr!   ÚcurÚes          r"   Úadd_licrŸ   I   sT  € à)*×):Ñ):¸HÒ)DÔ˜Ÿ™Ô%È!Ï,É,×J\ÑJ\ÓJ^€Ø×Ñ˜_Ñ,°·±Ð>ZÑ0ZÔanÐorð  tLó  bMð  \Mð7Üƒvð  V��q—y‘yÐ!yð  |Að  BC÷  BSñ  BSð  TU÷  TZñ  TZð  [\÷  [eñ  [eð  fg÷  fmñ  fmð  {nó  o�3ð  pq÷  pxñ  pxô  pzð  CGð  HK÷  HUñ  HUð  BV÷  V÷  Vò  VûÜÒ6œ]¨3¬s°1«vÓ6Ð6ûÐ6ús7   Á(
C% Á2ACÃ	C% ÃC"ÃC% Ã"C% Ã%	DÃ.DÄDz/admin/licences/{lid}c              ƒ  óº  K  — |j                  «       ƒ d {  –—† }h d£}g }g }|j                  «       D ]/  \  }}||v sŒ|j                  |dz   «       |j                  |«       Œ1 |r_|j                  | «       t        «       5 }	|	j	                  ddj                  |«      z   dz   |«       |	j                  «        d d d «       ddiS ddiS 7 Œ³# 1 sw Y   ddiS xY w­w)N>   r\   r–   Ústatusr•   z=?zUPDATE licences SET ú,z(,updated_at=CURRENT_TIMESTAMP WHERE id=?rS   T)rm   ÚitemsÚappendr#   r2   Újoinre   )
Úlidr5   r›   rA   ÚallowedÚsetsÚargsÚkÚvr!   s
             r"   Úeditr¬   P   sÍ   è ø€ à	�‰‹×€Ò@�GÀbÀÈbÈØ�G‰G‹Iò 5�S€Q€qØˆ'‚\�$—+‘+˜a ™fÔ% d§k¡k°!¥nð5áØ‡+�+ˆcÔÜƒvÐ~��1—9‘9Ð3°C·H±H¸T³NÑBÐCmÑmÐnrÔsÐtu×t|Ñt|Ôt~×~Ø	ˆdˆÐˆˆdˆÐð ø÷
 Ø	ˆdˆÐüs-   ‚C–C
—&C¾ACÂ8CÂ:CÃCÃ
Cc                ó”   — t        «       5 }|j                  d| f«       |j                  «        d d d «       ddiS # 1 sw Y   ddiS xY w)NzDELETE FROM licences WHERE id=?rS   T)r#   r2   re   )r¦   r5   r›   r!   s       r"   Údeleter®   Y   sB   € ä
ƒfÐP��!—)‘)Ð=¸s¸fÔEÀaÇhÁhÄj×PØ	ˆdˆÐ÷ QØ	ˆdˆÐús	   ‹$;»A)r5   r   )rA   rI   r5   r   )rs   rJ   )r|   r“   r5   r   )r¦   Úintr5   r   )4Ú
__future__r   r.   rm   Úosrˆ   r   r4   r   r   Úpathlibr   Úfastapir   r   r	   r
   Úfastapi.responsesr   r   r   Úpydanticr   ÚgetenvÚresolveÚDATAÚmkdirr   rk   rŠ   r‹   Úappr#   r'   r9   rB   rG   rI   r-   rX   Úpostrg   rq   rt   r   rŽ   r‘   r“   rŸ   Úpatchr¬   r®   rQ   r$   r"   ú<module>r½      s]  ðÝ "ß +× +× +ß &Ý ß 9Ó 9ß DÑ DÝ á	ˆ)ˆ"�)‰)Ð.Ð/FÓ
GÓH×PÑPÓR€ÐSW×S]ÑS]ÐfjÐtxÐS]ÔSyð  ~Bð  CPñ  ~PÐz|ð  Y]ð  ^gñ  Ygð  QXð  ho÷  huñ  huð  Cð  huô  hDØˆ2�9‰9Ð0°Ó9€
ÀYÀRÇYÁYÐOmÐn}ÓE~¸*ÙÐ.¸ÀtÐX\Ô]€ò:òD	á „óò Kò.ô 7ˆIÔ 6Ø‡�ˆÓÙ tó Ø tØ‡�ˆ+Óòó ðð ‡�Ð	"Ó#ñ"ó $ð"ð ‡�Ð	#Ó$òó %ðð ‡�ˆ˜L€Ó)òL+ó *ðL+ð ‡�ˆ(Óòmó ðmð ‡�ˆ)ÓÙ Ió Ø IÜ _ˆIÔ _Ø‡�Ð
ÓÙ")¨$£-ó 7ó ð7ð ‡�Ð"Ó#Ù%,¨T£]ó ó $ðð ‡�Ð#Ó$Ù!(¨£ó ó %ñr$   