index.html 987 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609
  1. <!DOCTYPE html>
  2. <html lang="el" class="dark">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>DebTik | Debian Linux Router</title>
  7. <link rel="icon" type="image/svg+xml" href="debtik-emblem.svg">
  8. <link rel="alternate icon" type="image/png" href="debtik-logo.png">
  9. <script src="tailwind.js"></script>
  10. <script>
  11. tailwind.config = {
  12. darkMode: 'class',
  13. theme: {
  14. extend: {
  15. colors: {
  16. brand: {
  17. 50: '#ecfdf5',
  18. 100: '#d1fae5',
  19. 400: '#34d399',
  20. 500: '#10b981',
  21. 600: '#059669',
  22. 700: '#047857',
  23. 800: '#065f46',
  24. 900: '#064e3b',
  25. }
  26. }
  27. }
  28. }
  29. }
  30. </script>
  31. <link rel="stylesheet" href="style.css?v=2.1">
  32. </head>
  33. <body class="bg-[#0b101e] text-slate-100 min-h-screen flex flex-col font-sans antialiased selection:bg-brand-500 selection:text-white">
  34. <!-- Toast Notification Container -->
  35. <div id="toast-container" class="fixed top-5 right-5 z-50 flex flex-col space-y-2 pointer-events-none"></div>
  36. <!-- Full-Screen Login Screen Overlay -->
  37. <div id="login-screen" class="fixed inset-0 z-[100] bg-[#0b101e] flex items-center justify-center p-4">
  38. <div class="w-full max-w-md bg-slate-900/95 border border-slate-800 rounded-2xl shadow-2xl p-8 relative overflow-hidden backdrop-blur-xl">
  39. <!-- Ambient Glows -->
  40. <div class="absolute -top-20 -right-20 w-52 h-52 bg-rose-500/10 rounded-full blur-3xl pointer-events-none"></div>
  41. <div class="absolute -bottom-20 -left-20 w-52 h-52 bg-cyan-500/10 rounded-full blur-3xl pointer-events-none"></div>
  42. <!-- Brand Header -->
  43. <div class="text-center mb-6 relative z-10">
  44. <div class="inline-flex items-center justify-center p-3 rounded-2xl bg-slate-950/80 border border-slate-800 shadow-xl mb-3">
  45. <img src="debtik-emblem.svg" alt="DebTik" class="w-12 h-12 object-contain" />
  46. </div>
  47. <h2 class="text-2xl font-black text-white tracking-tight flex items-center justify-center gap-2">
  48. <span>Deb<span class="text-rose-500">T</span><span class="text-sky-400">ik</span></span>
  49. <span class="text-[10px] bg-rose-500/20 text-rose-300 font-mono px-2 py-0.5 rounded border border-rose-500/30 font-bold uppercase tracking-wider">Gateway</span>
  50. </h2>
  51. <p class="text-xs text-slate-400 mt-1 font-sans">Appliance Controller &amp; Network Gateway</p>
  52. </div>
  53. <!-- Login Form -->
  54. <form id="login-form" onsubmit="handleLoginSubmit(event)" class="space-y-4 relative z-10">
  55. <div>
  56. <label class="block text-xs font-semibold text-slate-300 mb-1.5 font-sans">Username</label>
  57. <div class="relative">
  58. <span class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-500">
  59. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>
  60. </span>
  61. <input type="text" id="login-username" required autocomplete="username" autofocus
  62. placeholder="e.g. admin" value="admin"
  63. class="w-full bg-slate-950 border border-slate-800 rounded-xl pl-10 pr-3 py-2.5 text-white font-mono text-sm focus:outline-none focus:border-cyan-500 transition shadow-inner" />
  64. </div>
  65. </div>
  66. <div>
  67. <div class="flex items-center justify-between mb-1.5">
  68. <label class="block text-xs font-semibold text-slate-300 font-sans">Password</label>
  69. <span class="text-[10px] text-slate-500 font-mono">Default: admin / admin</span>
  70. </div>
  71. <div class="relative">
  72. <span class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-500">
  73. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" /></svg>
  74. </span>
  75. <input type="password" id="login-password" required autocomplete="current-password"
  76. placeholder="••••••••"
  77. class="w-full bg-slate-950 border border-slate-800 rounded-xl pl-10 pr-10 py-2.5 text-white font-mono text-sm focus:outline-none focus:border-cyan-500 transition shadow-inner" />
  78. <button type="button" onclick="togglePasswordVisibility('login-password')" class="absolute inset-y-0 right-0 pr-3.5 flex items-center text-slate-500 hover:text-slate-300">
  79. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
  80. </button>
  81. </div>
  82. </div>
  83. <div id="login-error" class="hidden p-3 rounded-xl bg-rose-500/10 border border-rose-500/30 text-rose-300 text-xs flex items-center gap-2">
  84. <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  85. <span id="login-error-text">Invalid username or password</span>
  86. </div>
  87. <button type="submit" id="login-submit-btn"
  88. class="w-full py-2.5 px-4 rounded-xl bg-gradient-to-r from-emerald-500 to-cyan-500 hover:from-emerald-400 hover:to-cyan-400 text-slate-950 font-bold text-sm shadow-lg shadow-emerald-500/20 hover:shadow-cyan-500/30 transition flex items-center justify-center gap-2 cursor-pointer mt-2">
  89. <span id="login-btn-text">Sign In to Controller</span>
  90. <svg id="login-btn-spinner" class="hidden animate-spin w-4 h-4 text-slate-950" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"></path></svg>
  91. </button>
  92. </form>
  93. <div class="mt-6 pt-4 border-t border-slate-800/80 text-center text-[11px] text-slate-500 relative z-10 flex items-center justify-center gap-2">
  94. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/></svg>
  95. <span>Protected appliance system • Authorized access only</span>
  96. </div>
  97. </div>
  98. </div>
  99. <!-- Main Layout -->
  100. <div class="flex flex-1 overflow-hidden h-screen">
  101. <!-- Sidebar -->
  102. <aside id="sidebar" class="w-64 bg-[#0f172a]/95 border-r border-slate-800 flex flex-col flex-shrink-0 transition-all duration-300 z-30">
  103. <!-- Brand Header -->
  104. <div class="h-16 flex items-center px-4 border-b border-slate-800 justify-between bg-slate-950/60">
  105. <div class="flex items-center space-x-3">
  106. <img src="debtik-emblem.svg" alt="DebTik" class="w-9 h-9 rounded-xl shadow-lg shadow-rose-900/40 object-contain p-0.5 bg-slate-900 border border-slate-800 flex-shrink-0" />
  107. <div class="leading-tight">
  108. <div class="font-black text-sm tracking-wide text-white flex items-center gap-1.5">
  109. <span>Deb<span class="text-rose-500">T</span><span class="text-sky-400">ik</span></span>
  110. <span class="text-[9px] bg-rose-500/20 text-rose-300 font-mono px-1.5 py-0.5 rounded border border-rose-500/30 font-bold">ROUTER</span>
  111. </div>
  112. <div class="text-[10px] text-slate-400 font-medium">Debian Linux Router</div>
  113. </div>
  114. </div>
  115. </div>
  116. <!-- Live Health Pill -->
  117. <div class="px-4 py-3 border-b border-slate-800/60 bg-slate-900/40">
  118. <div class="flex items-center justify-between text-xs mb-1.5">
  119. <span class="text-slate-400 font-medium">Gateway Status</span>
  120. <span class="flex items-center text-emerald-400 font-medium text-[11px]">
  121. <span class="relative flex h-2 w-2 mr-1.5">
  122. <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
  123. <span class="relative inline-flex rounded-full h-2 w-2 bg-emerald-500"></span>
  124. </span>
  125. Online
  126. </span>
  127. </div>
  128. <div class="text-[11px] text-slate-400 font-mono truncate" id="side-gateway-ip">GW: Scanning...</div>
  129. </div>
  130. <!-- Navigation Menu -->
  131. <nav class="flex-1 px-3 py-3 space-y-1 overflow-y-auto">
  132. <!-- CATEGORY: OVERVIEW -->
  133. <div class="px-3 pt-2 pb-1 text-[10px] font-bold text-slate-500 uppercase tracking-wider">Overview</div>
  134. <a href="#dashboard" onclick="navigate('dashboard')" id="nav-dashboard"
  135. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group bg-brand-500/10 text-brand-400 border border-brand-500/20">
  136. <div class="flex items-center space-x-2.5">
  137. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  138. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
  139. </svg>
  140. <span>Dashboard</span>
  141. </div>
  142. </a>
  143. <!-- CATEGORY: NETWORKING -->
  144. <div class="px-3 pt-3 pb-1 text-[10px] font-bold text-slate-500 uppercase tracking-wider">Networking</div>
  145. <a href="#interfaces" onclick="navigate('interfaces')" id="nav-interfaces"
  146. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  147. <div class="flex items-center space-x-2.5">
  148. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  149. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
  150. </svg>
  151. <span>Interfaces</span>
  152. </div>
  153. <span id="badge-ifaces-count" class="text-[10px] bg-slate-800 text-slate-300 px-1.5 py-0.2 rounded-full font-mono">0</span>
  154. </a>
  155. <a href="#dhcp-client" onclick="navigate('dhcp-client')" id="nav-dhcp-client"
  156. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  157. <div class="flex items-center space-x-2.5">
  158. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  159. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
  160. </svg>
  161. <span>DHCP Client</span>
  162. </div>
  163. <span id="badge-dhcp-client-count" class="text-[10px] bg-slate-800 text-slate-300 px-1.5 py-0.2 rounded-full font-mono">0</span>
  164. </a>
  165. <a href="#dhcp-server" onclick="navigate('dhcp-server')" id="nav-dhcp-server"
  166. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  167. <div class="flex items-center space-x-2.5">
  168. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  169. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />
  170. </svg>
  171. <span>DHCP Server</span>
  172. </div>
  173. <span id="badge-dhcp-server-count" class="text-[10px] bg-emerald-950 text-emerald-300 border border-emerald-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  174. </a>
  175. <a href="#bridge" onclick="navigate('bridge')" id="nav-bridge"
  176. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  177. <div class="flex items-center space-x-2.5">
  178. <svg class="w-4 h-4 text-sky-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  179. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M3 14h18M5 14v4a2 2 0 002 2h2a2 2 0 002-2v-4m6 0v4a2 2 0 002 2h2a2 2 0 002-2v-4M4 10V6a2 2 0 012-2h12a2 2 0 012 2v4" />
  180. </svg>
  181. <span>Bridge</span>
  182. </div>
  183. <span id="badge-bridge-count" class="text-[10px] bg-sky-950 text-sky-300 border border-sky-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  184. </a>
  185. <a href="#firewall" onclick="navigate('firewall')" id="nav-firewall"
  186. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  187. <div class="flex items-center space-x-2.5">
  188. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  189. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
  190. </svg>
  191. <span>Firewall</span>
  192. </div>
  193. <span id="badge-firewall-count" class="text-[10px] bg-emerald-950 text-emerald-400 border border-emerald-800/50 px-1.5 py-0.2 rounded-full font-mono">0</span>
  194. </a>
  195. <!-- CATEGORY: ROUTING -->
  196. <div class="px-3 pt-3 pb-1 text-[10px] font-bold text-slate-500 uppercase tracking-wider">Routing</div>
  197. <a href="#routes" onclick="navigate('routes')" id="nav-routes"
  198. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  199. <div class="flex items-center space-x-2.5">
  200. <svg class="w-4 h-4 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  201. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
  202. </svg>
  203. <span>Routes & Policy</span>
  204. </div>
  205. <span id="badge-routes-count" class="text-[10px] bg-slate-800 text-slate-300 px-1.5 py-0.2 rounded-full font-mono">0</span>
  206. </a>
  207. <a href="#sdwan" onclick="navigate('sdwan')" id="nav-sdwan"
  208. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  209. <div class="flex items-center space-x-2.5">
  210. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  211. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
  212. </svg>
  213. <span>SD-WAN</span>
  214. </div>
  215. <span id="badge-sdwan-count" class="text-[10px] bg-amber-950 text-amber-400 border border-amber-800/50 px-1.5 py-0.2 rounded-full font-mono">0</span>
  216. </a>
  217. <a href="#bgp" onclick="navigate('bgp')" id="nav-bgp"
  218. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  219. <div class="flex items-center space-x-2.5">
  220. <svg class="w-4 h-4 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  221. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
  222. </svg>
  223. <span>BGP Routing</span>
  224. </div>
  225. <div class="flex items-center gap-1.5">
  226. <span id="badge-bgp-dot" class="w-2 h-2 rounded-full bg-slate-600" title="BGP Status"></span>
  227. <span id="badge-bgp-count" class="text-[10px] bg-purple-950 text-purple-300 border border-purple-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  228. </div>
  229. </a>
  230. <a href="#ospf" onclick="navigate('ospf')" id="nav-ospf"
  231. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  232. <div class="flex items-center space-x-2.5">
  233. <svg class="w-4 h-4 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  234. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
  235. </svg>
  236. <span>OSPF Routing</span>
  237. </div>
  238. <div class="flex items-center gap-1.5">
  239. <span id="badge-ospf-dot" class="w-2 h-2 rounded-full bg-slate-600" title="OSPF Status"></span>
  240. <span id="badge-ospf-count" class="text-[10px] bg-indigo-950 text-indigo-300 border border-indigo-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  241. </div>
  242. </a>
  243. <a href="#rip" onclick="navigate('rip')" id="nav-rip"
  244. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  245. <div class="flex items-center space-x-2.5">
  246. <svg class="w-4 h-4 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  247. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
  248. </svg>
  249. <span>RIP Routing</span>
  250. </div>
  251. <div class="flex items-center gap-1.5">
  252. <span id="badge-rip-dot" class="w-2 h-2 rounded-full bg-slate-600" title="RIP Status"></span>
  253. <span id="badge-rip-count" class="text-[10px] bg-rose-950 text-rose-300 border border-rose-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  254. </div>
  255. </a>
  256. <a href="#bfd" onclick="navigate('bfd')" id="nav-bfd"
  257. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  258. <div class="flex items-center space-x-2.5">
  259. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  260. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
  261. </svg>
  262. <span>BFD Sessions</span>
  263. </div>
  264. <div class="flex items-center gap-1.5">
  265. <span id="badge-bfd-dot" class="w-2 h-2 rounded-full bg-slate-600" title="BFD Status"></span>
  266. <span id="badge-bfd-count" class="text-[10px] bg-amber-950 text-amber-300 border border-amber-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  267. </div>
  268. </a>
  269. <a href="#babel" onclick="navigate('babel')" id="nav-babel"
  270. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  271. <div class="flex items-center space-x-2.5">
  272. <svg class="w-4 h-4 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  273. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" />
  274. </svg>
  275. <span>Babel Routing</span>
  276. </div>
  277. <div class="flex items-center gap-1.5">
  278. <span id="badge-babel-dot" class="w-2 h-2 rounded-full bg-slate-600" title="Babel Status"></span>
  279. <span id="badge-babel-count" class="text-[10px] bg-teal-950 text-teal-300 border border-teal-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  280. </div>
  281. </a>
  282. <a href="#radv" onclick="navigate('radv')" id="nav-radv"
  283. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  284. <div class="flex items-center space-x-2.5">
  285. <svg class="w-4 h-4 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  286. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" />
  287. </svg>
  288. <span>IPv6 RAdv</span>
  289. </div>
  290. <div class="flex items-center gap-1.5">
  291. <span id="badge-radv-dot" class="w-2 h-2 rounded-full bg-slate-600" title="RAdv Status"></span>
  292. <span id="badge-radv-count" class="text-[10px] bg-blue-950 text-blue-300 border border-blue-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  293. </div>
  294. </a>
  295. <a href="#routing-filters" onclick="navigate('routing-filters')" id="nav-routing-filters"
  296. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  297. <div class="flex items-center space-x-2.5">
  298. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  299. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
  300. </svg>
  301. <span>Routing Filters</span>
  302. </div>
  303. <span id="badge-filters-count" class="text-[10px] bg-slate-800 text-slate-300 px-1.5 py-0.2 rounded-full font-mono">0</span>
  304. </a>
  305. <!-- CATEGORY: HIGH AVAILABILITY -->
  306. <div class="px-3 pt-3 pb-1 text-[10px] font-bold text-slate-500 uppercase tracking-wider">High Availability</div>
  307. <a href="#vrrp" onclick="navigate('vrrp')" id="nav-vrrp"
  308. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  309. <div class="flex items-center space-x-2.5">
  310. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  311. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
  312. </svg>
  313. <span>VRRP (HA)</span>
  314. </div>
  315. <div class="flex items-center gap-1.5">
  316. <span id="badge-vrrp-dot" class="w-2 h-2 rounded-full bg-slate-600" title="VRRP State"></span>
  317. <span id="badge-vrrp-count" class="text-[10px] bg-emerald-950 text-emerald-300 border border-emerald-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  318. </div>
  319. </a>
  320. <!-- CATEGORY: WAN & ACCESS -->
  321. <div class="px-3 pt-3 pb-1 text-[10px] font-bold text-slate-500 uppercase tracking-wider">WAN & Access</div>
  322. <a href="#pppoe" onclick="navigate('pppoe')" id="nav-pppoe"
  323. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  324. <div class="flex items-center space-x-2.5">
  325. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  326. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
  327. </svg>
  328. <span>PPPoE Client</span>
  329. </div>
  330. <span id="badge-pppoe-status" class="w-2 h-2 rounded-full bg-slate-600"></span>
  331. </a>
  332. <!-- CATEGORY: VPN & TUNNELS -->
  333. <div class="px-3 pt-3 pb-1 text-[10px] font-bold text-slate-500 uppercase tracking-wider">VPN & Tunnels</div>
  334. <a href="#wireguard" onclick="navigate('wireguard')" id="nav-wireguard"
  335. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  336. <div class="flex items-center space-x-2.5">
  337. <svg class="w-4 h-4 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  338. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
  339. </svg>
  340. <span>WireGuard</span>
  341. </div>
  342. <span id="badge-wg-status" class="w-2 h-2 rounded-full bg-slate-600"></span>
  343. </a>
  344. <a href="#ipsec" onclick="navigate('ipsec')" id="nav-ipsec"
  345. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  346. <div class="flex items-center space-x-2.5">
  347. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  348. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
  349. </svg>
  350. <span>IPSec (IKEv2)</span>
  351. </div>
  352. <span id="badge-ipsec-status" class="w-2 h-2 rounded-full bg-slate-600"></span>
  353. </a>
  354. <a href="#l2tp" onclick="navigate('l2tp')" id="nav-l2tp"
  355. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  356. <div class="flex items-center space-x-2.5">
  357. <svg class="w-4 h-4 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  358. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
  359. </svg>
  360. <span>L2TP Client</span>
  361. </div>
  362. <span id="badge-l2tp-status" class="w-2 h-2 rounded-full bg-slate-600"></span>
  363. </a>
  364. <a href="#pptp" onclick="navigate('pptp')" id="nav-pptp"
  365. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  366. <div class="flex items-center space-x-2.5">
  367. <svg class="w-4 h-4 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  368. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
  369. </svg>
  370. <span>PPTP Client</span>
  371. </div>
  372. <span id="badge-pptp-status" class="w-2 h-2 rounded-full bg-slate-600"></span>
  373. </a>
  374. <a href="#ppp" onclick="navigate('ppp')" id="nav-ppp"
  375. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  376. <div class="flex items-center space-x-2.5">
  377. <svg class="w-4 h-4 text-violet-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  378. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
  379. </svg>
  380. <span>PPP (Servers & Users)</span>
  381. </div>
  382. <span id="badge-ppp-sessions" class="text-[10px] bg-violet-950 text-violet-300 border border-violet-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  383. </a>
  384. <a href="#eoip" onclick="navigate('eoip')" id="nav-eoip"
  385. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  386. <div class="flex items-center space-x-2.5">
  387. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  388. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
  389. </svg>
  390. <span>EoIP Tunnels</span>
  391. </div>
  392. <span id="badge-eoip-count" class="text-[10px] bg-slate-800 text-slate-300 px-1.5 py-0.2 rounded-full font-mono">0</span>
  393. </a>
  394. <a href="#vxlan" onclick="navigate('vxlan')" id="nav-vxlan"
  395. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  396. <div class="flex items-center space-x-2.5">
  397. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  398. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
  399. </svg>
  400. <span>VXLAN Tunnels</span>
  401. </div>
  402. <span id="badge-vxlan-count" class="text-[10px] bg-slate-800 text-slate-300 px-1.5 py-0.2 rounded-full font-mono">0</span>
  403. </a>
  404. <a href="#aerotun" onclick="navigate('aerotun')" id="nav-aerotun"
  405. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  406. <div class="flex items-center space-x-2.5">
  407. <svg class="w-4 h-4 text-sky-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  408. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
  409. </svg>
  410. <span class="flex items-center gap-1.5">
  411. <span>AeroTUN</span>
  412. <span class="text-[9px] px-1 py-0.2 rounded bg-sky-500/20 text-sky-300 border border-sky-500/30 font-semibold font-mono">LZ4</span>
  413. </span>
  414. </div>
  415. <span id="badge-aerotun-count" class="text-[10px] bg-slate-800 text-slate-300 px-1.5 py-0.2 rounded-full font-mono">0</span>
  416. </a>
  417. <!-- CATEGORY: DIAGNOSTICS & TOOLS -->
  418. <div class="px-3 pt-3 pb-1 text-[10px] font-bold text-slate-500 uppercase tracking-wider">Diagnostics</div>
  419. <a href="#tools" onclick="navigate('tools')" id="nav-tools"
  420. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  421. <div class="flex items-center space-x-2.5">
  422. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  423. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
  424. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
  425. </svg>
  426. <span>Troubleshooting Tools</span>
  427. </div>
  428. <span class="text-[10px] bg-amber-950 text-amber-300 border border-amber-800/40 px-1.5 py-0.2 rounded-full font-mono">Tools</span>
  429. </a>
  430. <a href="#bwtest" onclick="navigate('bwtest')" id="nav-bwtest"
  431. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  432. <div class="flex items-center space-x-2.5">
  433. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  434. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
  435. </svg>
  436. <span class="flex items-center gap-1.5">
  437. <span>Bandwidth Test</span>
  438. <span class="text-[9px] px-1 py-0.2 rounded bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 font-semibold font-mono">BwTest</span>
  439. </span>
  440. </div>
  441. <span id="badge-bwtest-server" class="text-[10px] bg-emerald-950 text-emerald-300 border border-emerald-800/40 px-1.5 py-0.2 rounded-full font-mono">2000</span>
  442. </a>
  443. <!-- CATEGORY: SYSTEM -->
  444. <div class="px-3 pt-3 pb-1 text-[10px] font-bold text-slate-500 uppercase tracking-wider">System</div>
  445. <a href="#settings" onclick="navigate('settings')" id="nav-settings"
  446. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  447. <div class="flex items-center space-x-2.5">
  448. <svg class="w-4 h-4 text-cyan-400 group-hover:rotate-45 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  449. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
  450. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
  451. </svg>
  452. <span>Settings</span>
  453. </div>
  454. <span class="text-[10px] bg-cyan-950 text-cyan-400 border border-cyan-800/60 px-1.5 py-0.2 rounded font-mono">DNS</span>
  455. </a>
  456. <a href="#users" onclick="navigate('users')" id="nav-users"
  457. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  458. <div class="flex items-center space-x-2.5">
  459. <svg class="w-4 h-4 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  460. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
  461. </svg>
  462. <span>Users & Security</span>
  463. </div>
  464. <span id="badge-users-count" class="text-[10px] bg-rose-950 text-rose-300 border border-rose-800/40 px-1.5 py-0.2 rounded-full font-mono">1</span>
  465. </a>
  466. <a href="#certificates" onclick="navigate('certificates')" id="nav-certificates"
  467. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  468. <div class="flex items-center space-x-2.5">
  469. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  470. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
  471. </svg>
  472. <span>Certificates & SSL</span>
  473. </div>
  474. <span id="badge-certs-count" class="text-[10px] bg-amber-950 text-amber-300 border border-amber-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  475. </a>
  476. <a href="#reverse_proxy" onclick="navigate('reverse_proxy')" id="nav-reverse_proxy"
  477. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  478. <div class="flex items-center space-x-2.5">
  479. <svg class="w-4 h-4 text-violet-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  480. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
  481. </svg>
  482. <span>Reverse Proxy</span>
  483. </div>
  484. <span id="badge-proxy-count" class="text-[10px] bg-violet-950 text-violet-300 border border-violet-800/40 px-1.5 py-0.2 rounded-full font-mono">0</span>
  485. </a>
  486. <a href="#services" onclick="navigate('services')" id="nav-services"
  487. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  488. <div class="flex items-center space-x-2.5">
  489. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  490. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />
  491. </svg>
  492. <span>Management Services</span>
  493. </div>
  494. <span id="badge-services-status" class="w-2 h-2 rounded-full bg-emerald-500"></span>
  495. </a>
  496. <a href="#updates" onclick="navigate('updates')" id="nav-updates" class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium text-slate-300 hover:bg-slate-800/80 hover:text-white"><span>↻ Updates</span></a>
  497. <a href="#logs" onclick="navigate('logs')" id="nav-logs"
  498. class="nav-link flex items-center justify-between px-3 py-2 rounded-lg text-xs font-medium transition-all group text-slate-300 hover:bg-slate-800/80 hover:text-white">
  499. <div class="flex items-center space-x-2.5">
  500. <svg class="w-4 h-4 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  501. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
  502. </svg>
  503. <span>System Logs</span>
  504. </div>
  505. <span class="text-[10px] bg-blue-950 text-blue-300 border border-blue-800/40 px-1.5 py-0.2 rounded-full font-mono">LOG</span>
  506. </a>
  507. </nav>
  508. <!-- Sidebar Footer: Host System Metrics -->
  509. <div class="p-4 border-t border-slate-800/80 bg-slate-900/60">
  510. <div class="flex items-center justify-between text-xs text-slate-400 mb-2">
  511. <span class="truncate font-mono" id="side-hostname">debian-lxc</span>
  512. <span class="text-[11px] text-slate-500" id="side-uptime">0h 0m</span>
  513. </div>
  514. <div class="mb-2">
  515. <div class="flex justify-between text-[11px] text-slate-400 mb-1">
  516. <span>CPU Usage</span>
  517. <span id="side-cpu-val" class="font-mono text-slate-300">0.0%</span>
  518. </div>
  519. <div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden">
  520. <div id="side-cpu-bar" class="bg-brand-500 h-full rounded-full transition-all duration-500" style="width: 5%"></div>
  521. </div>
  522. </div>
  523. <div>
  524. <div class="flex justify-between text-[11px] text-slate-400 mb-1">
  525. <span>RAM Usage</span>
  526. <span id="side-ram-val" class="font-mono text-slate-300">0%</span>
  527. </div>
  528. <div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden">
  529. <div id="side-ram-bar" class="bg-cyan-500 h-full rounded-full transition-all duration-500" style="width: 25%"></div>
  530. </div>
  531. </div>
  532. </div>
  533. </aside>
  534. <!-- Main Content Area -->
  535. <div class="flex-1 flex flex-col min-w-0 overflow-hidden">
  536. <!-- Top Navbar -->
  537. <header class="h-16 bg-[#0f172a]/80 backdrop-blur border-b border-slate-800 flex items-center justify-between px-6 z-20">
  538. <div class="flex items-center space-x-4">
  539. <button onclick="toggleSidebar()" class="lg:hidden p-2 rounded-lg text-slate-400 hover:text-white hover:bg-slate-800">
  540. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  541. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
  542. </svg>
  543. </button>
  544. <div class="flex items-center gap-3">
  545. <img src="debtik-emblem.svg" alt="DebTik" class="w-7 h-7 sm:hidden" />
  546. <div>
  547. <h1 id="page-title" class="text-lg font-bold text-white tracking-tight">Dashboard Overview</h1>
  548. <div id="telemetry-health" role="status" aria-live="polite" class="text-[10px] text-amber-400">Waiting for fresh data</div>
  549. <p id="page-subtitle" class="text-xs text-slate-400">DebTik - Debian Linux Router appliance management</p>
  550. </div>
  551. </div>
  552. </div>
  553. <div class="flex items-center gap-3">
  554. <div class="hidden sm:flex items-center gap-2 px-3 py-1 bg-slate-900/90 border border-slate-800 rounded-xl shadow-sm">
  555. <img src="debtik-emblem.svg" alt="DebTik" class="w-5 h-5" />
  556. <span class="text-xs font-black text-white tracking-wide">Deb<span class="text-rose-500">T</span><span class="text-sky-400">ik</span></span>
  557. <span class="text-[10px] text-slate-400 font-mono">Debian Linux Router</span>
  558. </div>
  559. <!-- User Profile & Session Menu -->
  560. <div class="relative">
  561. <button onclick="toggleUserMenu()" class="flex items-center gap-2 px-2.5 py-1.5 bg-slate-900/90 border border-slate-800 hover:border-slate-700 rounded-xl text-xs transition cursor-pointer">
  562. <div class="w-6 h-6 rounded-lg bg-emerald-500/20 text-emerald-400 border border-emerald-500/30 flex items-center justify-center font-bold text-[10px]" id="nav-user-avatar">AD</div>
  563. <span class="font-medium text-white font-mono" id="nav-user-name">admin</span>
  564. <span class="text-[9px] px-1.5 py-0.2 rounded bg-brand-500/10 text-brand-400 border border-brand-500/20 font-mono uppercase font-bold" id="nav-user-role">full</span>
  565. <svg class="w-3.5 h-3.5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
  566. </button>
  567. <!-- Dropdown Menu -->
  568. <div id="user-menu-dropdown" class="hidden absolute right-0 mt-2 w-52 bg-slate-900 border border-slate-800 rounded-2xl shadow-2xl py-1.5 z-50 animate-in">
  569. <div class="px-3.5 py-2 border-b border-slate-800 text-[11px] text-slate-400">
  570. Signed in as <span class="font-bold text-white font-mono" id="dropdown-user-name">admin</span>
  571. </div>
  572. <button id="dropdown-item-users" onclick="navigate('users'); closeUserMenu();" class="w-full text-left px-3.5 py-2 text-xs text-slate-300 hover:bg-slate-800/80 hover:text-white flex items-center gap-2.5 transition">
  573. <svg class="w-4 h-4 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
  574. <span>User Manager</span>
  575. </button>
  576. <button onclick="openSelfPasswordModal(); closeUserMenu();" class="w-full text-left px-3.5 py-2 text-xs text-slate-300 hover:bg-slate-800/80 hover:text-white flex items-center gap-2.5 transition">
  577. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg>
  578. <span>Change Password</span>
  579. </button>
  580. <div class="border-t border-slate-800 my-1"></div>
  581. <button onclick="handleLogoutClick(); closeUserMenu();" class="w-full text-left px-3.5 py-2 text-xs text-rose-400 hover:bg-rose-500/10 flex items-center gap-2.5 transition cursor-pointer">
  582. <svg class="w-4 h-4 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/></svg>
  583. <span class="font-medium">Sign Out</span>
  584. </button>
  585. </div>
  586. </div>
  587. </div>
  588. </header>
  589. <!-- Scrollable Main Viewport -->
  590. <main class="flex-1 overflow-y-auto p-6 space-y-6">
  591. <!-- ================= SECTION: DASHBOARD ================= -->
  592. <section id="section-dashboard" class="space-y-6">
  593. <!-- DebTik Hero Brand Banner -->
  594. <div class="bg-gradient-to-r from-slate-900 via-[#0f172a] to-slate-900 border border-slate-800 rounded-2xl p-5 flex flex-wrap items-center justify-between gap-4 shadow-xl relative overflow-hidden">
  595. <div class="absolute -right-10 -bottom-10 w-64 h-64 bg-rose-500/5 rounded-full blur-3xl pointer-events-none"></div>
  596. <div class="flex items-center gap-4 z-10">
  597. <img src="debtik-logo.png" alt="DebTik - Debian Linux Router" class="w-16 h-16 sm:w-20 sm:h-20 rounded-xl shadow-2xl border border-slate-700/80 object-cover" />
  598. <div>
  599. <div class="flex items-center gap-2 mb-1">
  600. <h2 class="text-xl sm:text-2xl font-black text-white tracking-tight flex items-center gap-2">
  601. <span>Deb<span class="text-rose-500">T</span><span class="text-sky-400">ik</span></span>
  602. </h2>
  603. <span class="px-2 py-0.5 text-[10px] font-mono font-bold rounded-full bg-rose-500/15 text-rose-300 border border-rose-500/30">Debian Linux Router</span>
  604. <span class="px-2 py-0.5 text-[10px] font-mono font-bold rounded-full bg-emerald-500/15 text-emerald-300 border border-emerald-500/30">v7 Engine</span>
  605. </div>
  606. <p class="text-xs text-slate-400 max-w-xl">Enterprise-grade network routing appliance combining the rock-solid stability of Debian Linux with high-performance dynamic routing & advanced policy control.</p>
  607. </div>
  608. </div>
  609. <div class="flex items-center gap-3 z-10">
  610. <div class="px-3 py-2 bg-slate-950/80 border border-slate-800 rounded-xl text-right">
  611. <div class="text-[10px] text-slate-500 uppercase tracking-wider font-semibold">Engine</div>
  612. <div class="text-xs font-mono font-bold text-white flex items-center gap-1.5 justify-end">
  613. <span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span>
  614. <span>BIRD 2 + nftables</span>
  615. </div>
  616. </div>
  617. </div>
  618. </div>
  619. <div class="grid grid-cols-1 md:grid-cols-3 gap-5">
  620. <!-- CPU Usage -->
  621. <div class="bg-slate-900/80 border border-slate-800 rounded-xl p-5 relative overflow-hidden group hover:border-slate-700 transition">
  622. <div class="flex items-center justify-between mb-3">
  623. <span class="text-xs font-semibold text-slate-400 uppercase tracking-wider">CPU Usage</span>
  624. <div class="p-2 bg-emerald-500/10 text-emerald-400 rounded-lg">
  625. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  626. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M3 9h2m-2 6h2m14-6h2m-2 6h2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
  627. </svg>
  628. </div>
  629. </div>
  630. <div class="flex items-baseline justify-between">
  631. <div class="flex items-baseline space-x-2">
  632. <span id="dash-cpu-val" class="text-2xl font-bold text-white">0.0%</span>
  633. <span id="dash-cpu-cores" class="text-xs text-slate-400 font-mono">0 Cores</span>
  634. </div>
  635. <span id="dash-cpu-status" class="text-[11px] font-semibold text-emerald-400 uppercase px-2 py-0.5 rounded bg-emerald-500/10">Normal</span>
  636. </div>
  637. <div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden my-2.5">
  638. <div id="dash-cpu-bar" class="h-full rounded-full bg-emerald-500 transition-all duration-500" style="width: 0%"></div>
  639. </div>
  640. <div class="text-xs text-slate-400 flex items-center justify-between">
  641. <span>Load: <span id="dash-cpu-load" class="font-mono text-slate-300">0.00, 0.00, 0.00</span></span>
  642. <span class="text-[11px] text-slate-500 font-mono">Container vCPU</span>
  643. </div>
  644. </div>
  645. <!-- RAM Usage -->
  646. <div class="bg-slate-900/80 border border-slate-800 rounded-xl p-5 relative overflow-hidden group hover:border-slate-700 transition">
  647. <div class="flex items-center justify-between mb-3">
  648. <span class="text-xs font-semibold text-slate-400 uppercase tracking-wider">RAM Usage</span>
  649. <div class="p-2 bg-indigo-500/10 text-indigo-400 rounded-lg">
  650. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  651. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
  652. </svg>
  653. </div>
  654. </div>
  655. <div class="flex items-baseline justify-between">
  656. <div class="flex items-baseline space-x-2">
  657. <span id="dash-ram-val" class="text-2xl font-bold text-white">0%</span>
  658. <span id="dash-ram-used-total" class="text-xs text-slate-400 font-mono">0 / 0 MB</span>
  659. </div>
  660. <span id="dash-ram-status" class="text-[11px] font-semibold text-indigo-400 uppercase px-2 py-0.5 rounded bg-indigo-500/10">Active</span>
  661. </div>
  662. <div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden my-2.5">
  663. <div id="dash-ram-bar" class="h-full rounded-full bg-indigo-500 transition-all duration-500" style="width: 0%"></div>
  664. </div>
  665. <div class="text-xs text-slate-400 flex items-center justify-between">
  666. <span>Free: <span id="dash-ram-free" class="font-mono text-slate-300">0 MB</span></span>
  667. <span class="text-[11px] text-slate-500 font-mono">Dynamic Alloc</span>
  668. </div>
  669. </div>
  670. <!-- Live Traffic -->
  671. <div class="bg-slate-900/80 border border-slate-800 rounded-xl p-5 relative overflow-hidden group hover:border-slate-700 transition">
  672. <div class="flex items-center justify-between mb-3">
  673. <span class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Live Traffic</span>
  674. <div class="p-2 bg-amber-500/10 text-amber-400 rounded-lg">
  675. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  676. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
  677. </svg>
  678. </div>
  679. </div>
  680. <div class="space-y-1">
  681. <div class="flex items-center justify-between text-xs">
  682. <span class="text-slate-400 flex items-center gap-1.5"><span class="text-emerald-400 font-bold">↓</span> RX:</span>
  683. <span id="stat-traffic-rx" class="font-mono font-semibold text-emerald-400 text-sm">0 B/s</span>
  684. </div>
  685. <div class="flex items-center justify-between text-xs">
  686. <span class="text-slate-400 flex items-center gap-1.5"><span class="text-cyan-400 font-bold">↑</span> TX:</span>
  687. <span id="stat-traffic-tx" class="font-mono font-semibold text-cyan-400 text-sm">0 B/s</span>
  688. </div>
  689. </div>
  690. <div class="mt-2.5 pt-2 border-t border-slate-800/80 text-[11px] text-slate-500 flex items-center justify-between font-mono">
  691. <span>Throughput</span>
  692. <span class="text-emerald-400 font-semibold flex items-center gap-1"><span class="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"></span> Streaming</span>
  693. </div>
  694. </div>
  695. </div>
  696. <!-- Port Matrix -->
  697. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5">
  698. <div class="flex items-center justify-between mb-4">
  699. <div>
  700. <h3 class="text-sm font-bold text-white uppercase tracking-wider flex items-center gap-2">
  701. <span>Hardware & Virtual Port Matrix</span>
  702. <span class="text-[10px] bg-slate-800 text-slate-400 font-normal px-2 py-0.5 rounded-full">Live Hardware State</span>
  703. </h3>
  704. </div>
  705. </div>
  706. <div id="port-matrix" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-3"></div>
  707. </div>
  708. <!-- Quick Overview: Interfaces -->
  709. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5">
  710. <div class="flex items-center justify-between mb-4">
  711. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  712. <span>Interfaces Overview</span>
  713. <span class="text-[10px] bg-slate-800 text-slate-400 font-normal px-2 py-0.5 rounded-full">Active Ports & Addresses</span>
  714. </h3>
  715. <button onclick="navigate('interfaces')" class="text-xs text-brand-400 hover:text-brand-300 font-medium">View All Interfaces →</button>
  716. </div>
  717. <div class="overflow-x-auto">
  718. <table class="w-full text-left text-xs">
  719. <thead class="text-slate-400 border-b border-slate-800 text-[11px] uppercase tracking-wider">
  720. <tr>
  721. <th class="pb-2.5">Interface</th>
  722. <th class="pb-2.5">Role / Type</th>
  723. <th class="pb-2.5">MAC Address</th>
  724. <th class="pb-2.5">IP Address</th>
  725. <th class="pb-2.5">Live Traffic</th>
  726. <th class="pb-2.5 text-right">Status</th>
  727. </tr>
  728. </thead>
  729. <tbody id="quick-interfaces-table" class="divide-y divide-slate-800/60 font-mono"></tbody>
  730. </table>
  731. </div>
  732. </div>
  733. </section>
  734. <!-- ================= SECTION: INTERFACES (HIERARCHICAL TREE) ================= -->
  735. <section id="section-interfaces" class="hidden space-y-6">
  736. <!-- Top Sub-Tab Navigation -->
  737. <div class="flex items-center justify-between border-b border-slate-800 pb-3 flex-wrap gap-3">
  738. <div class="flex items-center space-x-2">
  739. <button id="ifaces-subtab-btn-tree" onclick="switchIfaceSubTab('tree')"
  740. class="px-3.5 py-1.5 rounded-lg text-xs font-semibold bg-brand-500/10 text-brand-400 border border-brand-500/20 transition flex items-center gap-1.5">
  741. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
  742. <span>Interfaces List</span>
  743. </button>
  744. <button id="ifaces-subtab-btn-roles" onclick="switchIfaceSubTab('roles')"
  745. class="px-3.5 py-1.5 rounded-lg text-xs font-medium text-slate-400 hover:text-white hover:bg-slate-800 transition flex items-center gap-1.5">
  746. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/></svg>
  747. <span>Role Tags Management</span>
  748. <span id="badge-roles-count" class="px-1.5 py-0.2 rounded text-[10px] font-mono bg-slate-800 text-slate-300 border border-slate-700">0</span>
  749. </button>
  750. </div>
  751. </div>
  752. <!-- SUBTAB 1: Interfaces Tree View -->
  753. <div id="ifaces-subtab-tree" class="space-y-6">
  754. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex flex-wrap items-center justify-between gap-4">
  755. <div class="relative flex-1 min-w-[280px]">
  756. <div class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
  757. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  758. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
  759. </svg>
  760. </div>
  761. <input type="text" id="iface-search" oninput="renderInterfacesList()"
  762. placeholder="Search interfaces by Name or IP (e.g. eth0, 192.168.1.1)..."
  763. class="w-full bg-slate-800/90 border border-slate-700 rounded-lg pl-10 pr-4 py-2 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-brand-500 transition">
  764. </div>
  765. <div class="flex items-center space-x-2">
  766. <button onclick="expandAllIfaces(true)" class="text-xs text-cyan-400 hover:text-cyan-300 font-medium px-3 py-2 rounded-lg bg-cyan-950/50 hover:bg-cyan-950 border border-cyan-800/60 transition flex items-center gap-1.5">
  767. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
  768. <span>Expand All</span>
  769. </button>
  770. <button onclick="expandAllIfaces(false)" class="text-xs text-slate-400 hover:text-slate-300 font-medium px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 border border-slate-700 transition flex items-center gap-1.5">
  771. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"/></svg>
  772. <span>Collapse All</span>
  773. </button>
  774. </div>
  775. </div>
  776. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden shadow-2xl">
  777. <div class="p-4 border-b border-slate-800 bg-slate-950/40 flex items-center justify-between">
  778. <div>
  779. <h3 class="text-sm font-bold text-white">Interface Table View</h3>
  780. </div>
  781. <div class="text-xs text-slate-400">
  782. <span id="ifaces-list-summary" class="font-mono">Loading interfaces...</span>
  783. </div>
  784. </div>
  785. <div class="overflow-x-auto">
  786. <table class="w-full text-left text-xs">
  787. <thead class="text-slate-400 bg-slate-950/80 border-b border-slate-800 uppercase tracking-wider text-[11px]">
  788. <tr>
  789. <th class="py-3 px-3 font-semibold w-8 text-center"></th>
  790. <th class="py-3 px-4 font-semibold">Interface / Type</th>
  791. <th class="py-3 px-4 font-semibold">Role & Alias</th>
  792. <th class="py-3 px-4 font-semibold">IP Address (CIDR)</th>
  793. <th class="py-3 px-4 font-semibold">MAC & MTU</th>
  794. <th class="py-3 px-4 font-semibold">Throughput (RX/TX)</th>
  795. <th class="py-3 px-4 font-semibold">Link State</th>
  796. <th class="py-3 px-4 font-semibold text-right">Actions</th>
  797. </tr>
  798. </thead>
  799. <tbody id="interfaces-tree-tbody" class="divide-y divide-slate-800 font-mono"></tbody>
  800. </table>
  801. </div>
  802. <div id="interfaces-empty-state" class="hidden p-12 text-center text-slate-500">
  803. No interfaces found matching your search.
  804. </div>
  805. </div>
  806. </div>
  807. <!-- SUBTAB 2: Role Tags Management -->
  808. <div id="ifaces-subtab-roles" class="hidden space-y-6">
  809. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden shadow-2xl">
  810. <div class="p-4 border-b border-slate-800 bg-slate-950/40 flex items-center justify-between flex-wrap gap-3">
  811. <div>
  812. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  813. <span>Interface Role Tags</span>
  814. </h3>
  815. <p class="text-xs text-slate-400 mt-0.5">Define custom organizational role tags with colors to classify, filter, and assign to network interfaces.</p>
  816. </div>
  817. <button onclick="openRoleModal()"
  818. class="px-3.5 py-2 rounded-lg font-semibold text-xs bg-brand-600 hover:bg-brand-500 text-white shadow-lg shadow-brand-500/20 transition flex items-center gap-1.5">
  819. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/></svg>
  820. <span>+ Add Role Tag</span>
  821. </button>
  822. </div>
  823. <div class="overflow-x-auto">
  824. <table class="w-full text-left text-xs">
  825. <thead class="text-slate-400 bg-slate-950/80 border-b border-slate-800 uppercase tracking-wider text-[11px] font-sans">
  826. <tr>
  827. <th class="py-3 px-4 font-semibold w-28">Badge Preview</th>
  828. <th class="py-3 px-4 font-semibold">Role Name</th>
  829. <th class="py-3 px-4 font-semibold">Color Theme</th>
  830. <th class="py-3 px-4 font-semibold">Description</th>
  831. <th class="py-3 px-4 font-semibold">Assigned Interfaces</th>
  832. <th class="py-3 px-4 font-semibold text-right">Actions</th>
  833. </tr>
  834. </thead>
  835. <tbody id="roles-table-body" class="divide-y divide-slate-800 font-mono"></tbody>
  836. </table>
  837. </div>
  838. <div id="roles-empty-state" class="hidden p-12 text-center text-slate-500 font-sans">
  839. No role tags defined. Click "+ Add Role Tag" to create one.
  840. </div>
  841. </div>
  842. </div>
  843. </section>
  844. <!-- ================= SECTION: DHCP CLIENT ================= -->
  845. <section id="section-dhcp-client" class="hidden space-y-6">
  846. <div class="flex items-center justify-between">
  847. <button onclick="openDHCPClientModal()" class="px-4 py-2 rounded-lg font-semibold text-xs bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-lg shadow-amber-500/20 transition flex items-center gap-1.5">
  848. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/></svg>
  849. <span>+ Add</span>
  850. </button>
  851. </div>
  852. <!-- KPI Summary Cards -->
  853. <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
  854. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  855. <div class="text-xs text-slate-400">Total Configured</div>
  856. <div id="dhcp-stat-total" class="text-xl font-bold text-white font-mono mt-1">0</div>
  857. </div>
  858. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  859. <div class="text-xs text-slate-400">Bound Leases</div>
  860. <div id="dhcp-stat-bound" class="text-xl font-bold text-emerald-400 font-mono mt-1">0</div>
  861. </div>
  862. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  863. <div class="text-xs text-slate-400">Searching / Requesting</div>
  864. <div id="dhcp-stat-searching" class="text-xl font-bold text-amber-400 font-mono mt-1">0</div>
  865. </div>
  866. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  867. <div class="text-xs text-slate-400">Disabled</div>
  868. <div id="dhcp-stat-disabled" class="text-xl font-bold text-slate-500 font-mono mt-1">0</div>
  869. </div>
  870. </div>
  871. <!-- DHCP Clients Table -->
  872. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  873. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  874. <div>
  875. <h3 class="text-xs font-bold text-white">DHCP Client Interfaces</h3>
  876. <p class="text-[11px] text-slate-400 mt-0.5">Active leases, assigned IP addresses, default gateways and lease duration</p>
  877. </div>
  878. </div>
  879. <div class="overflow-x-auto">
  880. <table class="w-full text-left text-xs">
  881. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px]">
  882. <tr>
  883. <th class="py-3 px-4">Interface</th>
  884. <th class="py-3 px-4">Status</th>
  885. <th class="py-3 px-4">IP Address / Netmask</th>
  886. <th class="py-3 px-4">Gateway</th>
  887. <th class="py-3 px-4">DHCP Server</th>
  888. <th class="py-3 px-4">DNS Servers</th>
  889. <th class="py-3 px-4">Expires In</th>
  890. <th class="py-3 px-4">Options</th>
  891. <th class="py-3 px-4 text-right">Actions</th>
  892. </tr>
  893. </thead>
  894. <tbody id="dhcp-clients-tbody" class="divide-y divide-slate-800 font-mono"></tbody>
  895. </table>
  896. </div>
  897. <div id="dhcp-clients-empty" class="hidden p-12 text-center text-slate-500">
  898. <div class="text-3xl mb-2">⚡</div>
  899. <div class="font-semibold text-slate-400 text-sm">No DHCP Clients Configured</div>
  900. <div class="text-xs mt-1 text-slate-500">Click &ldquo;Add DHCP Client&rdquo; to automatically obtain an IP on any interface.</div>
  901. </div>
  902. </div>
  903. </section>
  904. <!-- ================= SECTION: DHCP SERVER ================= -->
  905. <section id="section-dhcp-server" class="hidden space-y-6">
  906. <div class="bg-gradient-to-r from-emerald-950/40 via-slate-900 to-slate-900 border border-emerald-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  907. <div>
  908. <div class="flex items-center gap-2 mb-1">
  909. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 tracking-wide uppercase">Core Network Service</span>
  910. <h2 class="text-base font-bold text-white flex items-center gap-2">
  911. <span>DHCP Server & Subnets</span>
  912. <span class="px-2 py-0.5 text-[10px] rounded-full bg-emerald-500/20 text-emerald-300 border border-emerald-500/40 font-mono font-normal">Enterprise Engine</span>
  913. </h2>
  914. </div>
  915. <p class="text-xs text-slate-400">Dynamic Host Configuration Protocol server, subnet network parameters (NTP, TFTP/PXE, DNS, Option Codes), active lease allocations & static IP reservations.</p>
  916. </div>
  917. <div class="flex items-center gap-2">
  918. <button onclick="openDHCPServerSetupModal()" class="text-xs font-semibold px-3 py-2 rounded-lg bg-emerald-600/30 hover:bg-emerald-600/50 text-emerald-300 border border-emerald-500/40 transition flex items-center gap-1.5" title="Run Quick 1-Click DHCP Setup Wizard">
  919. <span>⚡ DHCP Setup</span>
  920. </button>
  921. <button id="dhcp-server-primary-add-btn" onclick="handleDHCPServerPrimaryAdd()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20 transition flex items-center gap-1.5">
  922. <span>+ Add Server</span>
  923. </button>
  924. <button onclick="loadDHCPServer(true)" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition" title="Refresh">
  925. <span>🔄</span>
  926. </button>
  927. </div>
  928. </div>
  929. <!-- DHCP Subtabs -->
  930. <div class="flex items-center border-b border-slate-800 overflow-x-auto space-x-1">
  931. <button id="dhcp-server-tab-btn-servers" onclick="switchDHCPServerTab('servers')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-emerald-500 text-white transition flex items-center gap-2 whitespace-nowrap">
  932. <span>DHCP</span>
  933. <span id="dhcp-server-tab-badge-servers" class="px-1.5 py-0.2 rounded-full text-[10px] bg-slate-800 text-slate-300 font-mono">0</span>
  934. </button>
  935. <button id="dhcp-server-tab-btn-networks" onclick="switchDHCPServerTab('networks')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  936. <span>Networks</span>
  937. <span id="dhcp-server-tab-badge-networks" class="px-1.5 py-0.2 rounded-full text-[10px] bg-slate-800 text-slate-300 font-mono">0</span>
  938. </button>
  939. <button id="dhcp-server-tab-btn-leases" onclick="switchDHCPServerTab('leases')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  940. <span>Leases</span>
  941. <span id="dhcp-server-tab-badge-leases" class="px-1.5 py-0.2 rounded-full text-[10px] bg-slate-800 text-slate-300 font-mono">0</span>
  942. </button>
  943. <button id="dhcp-server-tab-btn-options" onclick="switchDHCPServerTab('options')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  944. <span>Options</span>
  945. <span id="dhcp-server-tab-badge-options" class="px-1.5 py-0.2 rounded-full text-[10px] bg-slate-800 text-slate-300 font-mono">0</span>
  946. </button>
  947. </div>
  948. <!-- KPI Summary Cards -->
  949. <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
  950. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  951. <div>
  952. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Active Servers</div>
  953. <div id="dhcp-server-stat-active" class="text-xl font-bold text-white font-mono mt-1">0 / 0</div>
  954. <div class="text-[10px] text-slate-500 mt-0.5">Instances online</div>
  955. </div>
  956. <div class="w-10 h-10 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400">
  957. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/></svg>
  958. </div>
  959. </div>
  960. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  961. <div>
  962. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Active Leases</div>
  963. <div id="dhcp-server-stat-leases" class="text-xl font-bold text-emerald-400 font-mono mt-1">0</div>
  964. <div id="dhcp-server-stat-leases-sub" class="text-[10px] text-slate-500 mt-0.5">0 dynamic, 0 static</div>
  965. </div>
  966. <div class="w-10 h-10 rounded-xl bg-sky-500/10 border border-sky-500/20 flex items-center justify-center text-sky-400">
  967. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
  968. </div>
  969. </div>
  970. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  971. <div>
  972. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Configured Subnets</div>
  973. <div id="dhcp-server-stat-networks" class="text-xl font-bold text-cyan-400 font-mono mt-1">0</div>
  974. <div class="text-[10px] text-slate-500 mt-0.5">DHCP network scopes</div>
  975. </div>
  976. <div class="w-10 h-10 rounded-xl bg-cyan-500/10 border border-cyan-500/20 flex items-center justify-center text-cyan-400">
  977. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16"/></svg>
  978. </div>
  979. </div>
  980. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  981. <div>
  982. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">DHCP Engine</div>
  983. <div class="text-xs font-bold text-emerald-300 font-mono mt-1">dnsmasq v2.91</div>
  984. <div class="text-[10px] text-slate-400 mt-0.5">Authoritative Mode • Port 67</div>
  985. </div>
  986. <div class="w-10 h-10 rounded-xl bg-indigo-500/10 border border-indigo-500/20 flex items-center justify-center text-indigo-400">
  987. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/></svg>
  988. </div>
  989. </div>
  990. </div>
  991. <!-- SUBTAB 1: SERVERS -->
  992. <div id="dhcp-server-subtab-servers" class="space-y-4">
  993. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  994. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  995. <div>
  996. <h3 class="text-xs font-bold text-white">DHCP Server Instances</h3>
  997. <p class="text-[11px] text-slate-400 mt-0.5">Per-interface DHCP daemon listeners and address pools</p>
  998. </div>
  999. <div class="text-xs text-slate-400 font-mono">
  1000. <span class="text-emerald-400">R</span> = Running &nbsp;|&nbsp; <span class="text-rose-400">X</span> = Disabled
  1001. </div>
  1002. </div>
  1003. <div class="overflow-x-auto">
  1004. <table class="w-full text-left text-xs font-mono">
  1005. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1006. <tr>
  1007. <th class="py-2.5 px-3 w-12 text-center">Flag</th>
  1008. <th class="py-2.5 px-3">Name</th>
  1009. <th class="py-2.5 px-3">Interface</th>
  1010. <th class="py-2.5 px-3">Assigned Network</th>
  1011. <th class="py-2.5 px-3">Address Pool</th>
  1012. <th class="py-2.5 px-3">Lease Time</th>
  1013. <th class="py-2.5 px-3">Authoritative</th>
  1014. <th class="py-2.5 px-3">Active Leases</th>
  1015. <th class="py-2.5 px-3">Comment</th>
  1016. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1017. </tr>
  1018. </thead>
  1019. <tbody id="dhcp-server-tbody" class="divide-y divide-slate-800/80"></tbody>
  1020. </table>
  1021. </div>
  1022. <div id="dhcp-server-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1023. No DHCP servers configured. Click "+ Add Server" or "⚡ DHCP Setup" to configure one.
  1024. </div>
  1025. </div>
  1026. </div>
  1027. <!-- SUBTAB 2: NETWORKS -->
  1028. <div id="dhcp-server-subtab-networks" class="hidden space-y-4">
  1029. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1030. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1031. <div>
  1032. <h3 class="text-xs font-bold text-white">DHCP Subnet Networks</h3>
  1033. <p class="text-[11px] text-slate-400 mt-0.5">Subnet parameters, Gateways, DNS, NTP servers, and TFTP / PXE Boot options</p>
  1034. </div>
  1035. <button onclick="openAddDHCPNetworkModal()" class="text-xs font-semibold px-3 py-1.5 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white transition">
  1036. + Add Network
  1037. </button>
  1038. </div>
  1039. <div class="overflow-x-auto">
  1040. <table class="w-full text-left text-xs font-mono">
  1041. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1042. <tr>
  1043. <th class="py-2.5 px-3">Address</th>
  1044. <th class="py-2.5 px-3">Gateway</th>
  1045. <th class="py-2.5 px-3">Netmask</th>
  1046. <th class="py-2.5 px-3">DNS Servers</th>
  1047. <th class="py-2.5 px-3">Domain</th>
  1048. <th class="py-2.5 px-3">DHCP Options</th>
  1049. <th class="py-2.5 px-3">Comment</th>
  1050. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1051. </tr>
  1052. </thead>
  1053. <tbody id="dhcp-networks-tbody" class="divide-y divide-slate-800/80"></tbody>
  1054. </table>
  1055. </div>
  1056. <div id="dhcp-networks-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1057. No DHCP networks configured. Click "+ Add Network" to configure subnet parameters.
  1058. </div>
  1059. </div>
  1060. </div>
  1061. <!-- SUBTAB 3: LEASES -->
  1062. <div id="dhcp-server-subtab-leases" class="hidden space-y-4">
  1063. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1064. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1065. <div>
  1066. <h3 class="text-xs font-bold text-white">DHCP Leases & Static Reservations</h3>
  1067. <p class="text-[11px] text-slate-400 mt-0.5">Active dynamic leases and static MAC-to-IP reservations</p>
  1068. </div>
  1069. <div class="flex items-center gap-2">
  1070. <span class="text-xs text-slate-400 font-mono">
  1071. <span class="text-sky-400">D</span> = Dynamic &nbsp;|&nbsp; <span class="text-purple-400">S</span> = Static &nbsp;|&nbsp; <span class="text-rose-400">X</span> = Disabled
  1072. </span>
  1073. <button onclick="openAddDHCPStaticLeaseModal()" class="text-xs font-semibold px-3 py-1.5 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white transition">
  1074. + Add Static Lease
  1075. </button>
  1076. </div>
  1077. </div>
  1078. <div class="overflow-x-auto">
  1079. <table class="w-full text-left text-xs font-mono">
  1080. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1081. <tr>
  1082. <th class="py-2.5 px-3 w-12 text-center">Flag</th>
  1083. <th class="py-2.5 px-3">Address</th>
  1084. <th class="py-2.5 px-3">MAC Address</th>
  1085. <th class="py-2.5 px-3">Host Name</th>
  1086. <th class="py-2.5 px-3">Server</th>
  1087. <th class="py-2.5 px-3">Expires After</th>
  1088. <th class="py-2.5 px-3">Status</th>
  1089. <th class="py-2.5 px-3">Comment</th>
  1090. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1091. </tr>
  1092. </thead>
  1093. <tbody id="dhcp-leases-tbody" class="divide-y divide-slate-800/80"></tbody>
  1094. </table>
  1095. </div>
  1096. <div id="dhcp-leases-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1097. No active leases or static reservations found.
  1098. </div>
  1099. </div>
  1100. </div>
  1101. <!-- SUBTAB 4: OPTIONS -->
  1102. <div id="dhcp-server-subtab-options" class="hidden space-y-4">
  1103. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1104. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1105. <div>
  1106. <h3 class="text-xs font-bold text-white">Custom DHCP Options (Codes 1-254)</h3>
  1107. <p class="text-[11px] text-slate-400 mt-0.5">Standard & Vendor DHCP Options (NTP, TFTP, UniFi Inform, CAPWAP, VoIP)</p>
  1108. </div>
  1109. <button onclick="openAddDHCPOptionModal()" class="text-xs font-semibold px-3 py-1.5 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white transition">
  1110. + Add Option
  1111. </button>
  1112. </div>
  1113. <div class="overflow-x-auto">
  1114. <table class="w-full text-left text-xs font-mono">
  1115. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1116. <tr>
  1117. <th class="py-2.5 px-3">Name</th>
  1118. <th class="py-2.5 px-3">Code</th>
  1119. <th class="py-2.5 px-3">Value</th>
  1120. <th class="py-2.5 px-3">Comment</th>
  1121. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1122. </tr>
  1123. </thead>
  1124. <tbody id="dhcp-options-tbody" class="divide-y divide-slate-800/80"></tbody>
  1125. </table>
  1126. </div>
  1127. <div id="dhcp-options-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1128. No custom DHCP options configured.
  1129. </div>
  1130. </div>
  1131. </div>
  1132. </section>
  1133. <!-- ================= SECTION: BRIDGE ================= -->
  1134. <section id="section-bridge" class="hidden space-y-6">
  1135. <!-- Bridge Navigation Header -->
  1136. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex flex-wrap items-center justify-between gap-4">
  1137. <div class="flex items-center space-x-3">
  1138. <div class="p-2.5 rounded-lg bg-sky-500/10 border border-sky-500/20 text-sky-400">
  1139. <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  1140. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M3 14h18M5 14v4a2 2 0 002 2h2a2 2 0 002-2v-4m6 0v4a2 2 0 002 2h2a2 2 0 002-2v-4M4 10V6a2 2 0 012-2h12a2 2 0 012 2v4" />
  1141. </svg>
  1142. </div>
  1143. <div>
  1144. <div class="flex items-center gap-2">
  1145. <h2 class="text-base font-bold text-white">Bridge Interfaces</h2>
  1146. <span class="px-2 py-0.5 rounded text-[10px] font-mono bg-sky-500/20 text-sky-300 border border-sky-500/30 font-semibold">IEEE 802.1D / 802.1Q</span>
  1147. </div>
  1148. <p class="text-xs text-slate-400">Layer 2 software bridge with Rapid Spanning Tree Protocol (RSTP), 802.1Q VLAN filtering, horizon port isolation, and dynamic forwarding database (FDB).</p>
  1149. </div>
  1150. </div>
  1151. <div class="flex items-center space-x-3">
  1152. <button id="bridge-top-action-btn" onclick="openCurrentBridgeModal()" class="flex items-center space-x-2 bg-gradient-to-r from-sky-600 to-cyan-600 hover:from-sky-500 hover:to-cyan-500 text-white font-semibold text-xs px-4 py-2.5 rounded-lg shadow-lg shadow-sky-500/20 transition">
  1153. <span>+ Add Bridge</span>
  1154. </button>
  1155. </div>
  1156. </div>
  1157. <!-- Bridge Subtabs Nav -->
  1158. <div class="bg-slate-900/60 border border-slate-800/80 rounded-xl overflow-hidden p-1 flex flex-wrap items-center justify-between gap-2">
  1159. <div class="flex items-center space-x-1">
  1160. <button id="bridge-subtab-btn-bridges" onclick="switchBridgeSubTab('bridges')" class="px-4 py-2 text-xs font-bold rounded-lg bg-slate-800 text-white shadow-sm transition flex items-center gap-2">
  1161. <span>Bridges</span>
  1162. <span id="bridge-badge-bridges" class="px-1.5 py-0.2 rounded text-[10px] bg-sky-950 text-sky-300 border border-sky-800/40">0</span>
  1163. </button>
  1164. <button id="bridge-subtab-btn-ports" onclick="switchBridgeSubTab('ports')" class="px-4 py-2 text-xs font-bold rounded-lg text-slate-400 hover:text-slate-200 transition flex items-center gap-2">
  1165. <span>Ports</span>
  1166. <span id="bridge-badge-ports" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  1167. </button>
  1168. <button id="bridge-subtab-btn-vlans" onclick="switchBridgeSubTab('vlans')" class="px-4 py-2 text-xs font-bold rounded-lg text-slate-400 hover:text-slate-200 transition flex items-center gap-2">
  1169. <span>VLANs</span>
  1170. <span id="bridge-badge-vlans" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  1171. </button>
  1172. <button id="bridge-subtab-btn-hosts" onclick="switchBridgeSubTab('hosts')" class="px-4 py-2 text-xs font-bold rounded-lg text-slate-400 hover:text-slate-200 transition flex items-center gap-2">
  1173. <span>Hosts (MAC Table)</span>
  1174. <span id="bridge-badge-hosts" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  1175. </button>
  1176. </div>
  1177. <!-- Stats Bar -->
  1178. <div class="flex items-center space-x-4 px-3 text-xs font-mono text-slate-400">
  1179. <div>Bridges: <span id="bridge-stat-bridges" class="font-bold text-white">0</span></div>
  1180. <div>Ports: <span id="bridge-stat-ports" class="font-bold text-sky-400">0</span></div>
  1181. <div>Learned MACs: <span id="bridge-stat-hosts" class="font-bold text-emerald-400">0</span></div>
  1182. </div>
  1183. </div>
  1184. <!-- ================= SUBTAB 1: BRIDGES ================= -->
  1185. <div id="bridge-subtab-bridges" class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1186. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1187. <h3 class="text-sm font-bold text-white">Configured Bridges</h3>
  1188. <span id="bridge-count-badge" class="text-xs font-mono text-slate-400">0 Bridges</span>
  1189. </div>
  1190. <div class="overflow-x-auto">
  1191. <table class="w-full text-left text-xs font-mono">
  1192. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1193. <tr>
  1194. <th class="py-2.5 px-3 w-10 text-center">#</th>
  1195. <th class="py-2.5 px-3 w-12 text-center" title="Flags: R=Running, X=Disabled">Flag</th>
  1196. <th class="py-2.5 px-3">Name</th>
  1197. <th class="py-2.5 px-3">MTU</th>
  1198. <th class="py-2.5 px-3">MAC Address</th>
  1199. <th class="py-2.5 px-3">STP</th>
  1200. <th class="py-2.5 px-3">VLAN Filtering</th>
  1201. <th class="py-2.5 px-3">PVID</th>
  1202. <th class="py-2.5 px-3">Ports</th>
  1203. <th class="py-2.5 px-3">IP Addresses</th>
  1204. <th class="py-2.5 px-3">Traffic</th>
  1205. <th class="py-2.5 px-3">Comment</th>
  1206. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1207. </tr>
  1208. </thead>
  1209. <tbody id="bridge-table-body" class="divide-y divide-slate-800/80"></tbody>
  1210. </table>
  1211. </div>
  1212. <div id="bridge-empty-bridges" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1213. No bridge interfaces configured. Click "+ Add Bridge" to create one.
  1214. </div>
  1215. </div>
  1216. <!-- ================= SUBTAB 2: PORTS ================= -->
  1217. <div id="bridge-subtab-ports" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1218. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  1219. <div class="flex items-center gap-3">
  1220. <h3 class="text-sm font-bold text-white">Bridge Ports</h3>
  1221. <span id="bridge-ports-count-badge" class="text-xs font-mono text-slate-400">0 Ports</span>
  1222. </div>
  1223. <div class="flex items-center space-x-2">
  1224. <label class="text-xs text-slate-400">Filter Bridge:</label>
  1225. <select id="bridge-ports-filter-bridge" onchange="renderBridgePorts()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white">
  1226. <option value="">All Bridges</option>
  1227. </select>
  1228. </div>
  1229. </div>
  1230. <div class="overflow-x-auto">
  1231. <table class="w-full text-left text-xs font-mono">
  1232. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1233. <tr>
  1234. <th class="py-2.5 px-3 w-10 text-center">#</th>
  1235. <th class="py-2.5 px-3 w-12 text-center" title="Flags: X=Disabled">Flag</th>
  1236. <th class="py-2.5 px-3">Interface</th>
  1237. <th class="py-2.5 px-3">Bridge</th>
  1238. <th class="py-2.5 px-3">PVID</th>
  1239. <th class="py-2.5 px-3">Frame Types</th>
  1240. <th class="py-2.5 px-3">Ingress Filter</th>
  1241. <th class="py-2.5 px-3">Horizon</th>
  1242. <th class="py-2.5 px-3">Role / Status</th>
  1243. <th class="py-2.5 px-3">Comment</th>
  1244. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1245. </tr>
  1246. </thead>
  1247. <tbody id="bridge-ports-table-body" class="divide-y divide-slate-800/80"></tbody>
  1248. </table>
  1249. </div>
  1250. <div id="bridge-empty-ports" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1251. No bridge ports configured. Click "+ Add Port" to attach a physical NIC, VLAN or EoIP tunnel.
  1252. </div>
  1253. </div>
  1254. <!-- ================= SUBTAB 3: VLANS ================= -->
  1255. <div id="bridge-subtab-vlans" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1256. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  1257. <div class="flex items-center gap-3">
  1258. <h3 class="text-sm font-bold text-white">Bridge VLAN Filtering Table</h3>
  1259. <span id="bridge-vlans-count-badge" class="text-xs font-mono text-slate-400">0 Entries</span>
  1260. </div>
  1261. <div class="flex items-center space-x-2">
  1262. <label class="text-xs text-slate-400">Filter Bridge:</label>
  1263. <select id="bridge-vlans-filter-bridge" onchange="renderBridgeVLANs()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white">
  1264. <option value="">All Bridges</option>
  1265. </select>
  1266. </div>
  1267. </div>
  1268. <div class="overflow-x-auto">
  1269. <table class="w-full text-left text-xs font-mono">
  1270. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1271. <tr>
  1272. <th class="py-2.5 px-3 w-10 text-center">#</th>
  1273. <th class="py-2.5 px-3 w-12 text-center" title="Flags: X=Disabled">Flag</th>
  1274. <th class="py-2.5 px-3">Bridge</th>
  1275. <th class="py-2.5 px-3">VLAN IDs</th>
  1276. <th class="py-2.5 px-3">Tagged Member Ports</th>
  1277. <th class="py-2.5 px-3">Untagged Member Ports</th>
  1278. <th class="py-2.5 px-3">Comment</th>
  1279. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1280. </tr>
  1281. </thead>
  1282. <tbody id="bridge-vlans-table-body" class="divide-y divide-slate-800/80"></tbody>
  1283. </table>
  1284. </div>
  1285. <div id="bridge-empty-vlans" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1286. No bridge VLAN filtering entries configured. Click "+ Add VLAN Entry" to configure 802.1Q tagged ports.
  1287. </div>
  1288. </div>
  1289. <!-- ================= SUBTAB 4: HOSTS (MAC TABLE) ================= -->
  1290. <div id="bridge-subtab-hosts" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1291. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  1292. <div class="flex items-center gap-3">
  1293. <h3 class="text-sm font-bold text-white">Forwarding Database (MAC Table)</h3>
  1294. <span id="bridge-hosts-count-badge" class="text-xs font-mono text-slate-400">0 Entries</span>
  1295. </div>
  1296. <div class="flex items-center space-x-2">
  1297. <input type="text" id="bridge-hosts-search" oninput="renderBridgeHosts()" placeholder="Search MAC or Interface..." class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white">
  1298. <select id="bridge-hosts-filter-bridge" onchange="loadBridgeHosts()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white">
  1299. <option value="">All Bridges</option>
  1300. </select>
  1301. <button onclick="loadBridgeHosts()" class="px-2.5 py-1 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-semibold text-slate-200 border border-slate-700">🔄 Refresh</button>
  1302. <button onclick="flushBridgeHosts()" class="px-2.5 py-1 rounded-lg bg-rose-950/60 hover:bg-rose-900/80 text-rose-300 border border-rose-800/40 text-xs font-semibold">🗑️ Flush Dynamic</button>
  1303. </div>
  1304. </div>
  1305. <div class="overflow-x-auto">
  1306. <table class="w-full text-left text-xs font-mono">
  1307. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1308. <tr>
  1309. <th class="py-2.5 px-3">MAC Address</th>
  1310. <th class="py-2.5 px-3">Interface / Port</th>
  1311. <th class="py-2.5 px-3">Bridge</th>
  1312. <th class="py-2.5 px-3">VLAN</th>
  1313. <th class="py-2.5 px-3">Age (s)</th>
  1314. <th class="py-2.5 px-3">Flags (L=Local, S=Static, D=Dynamic)</th>
  1315. </tr>
  1316. </thead>
  1317. <tbody id="bridge-hosts-table-body" class="divide-y divide-slate-800/80"></tbody>
  1318. </table>
  1319. </div>
  1320. <div id="bridge-empty-hosts" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1321. No MAC addresses learned in the forwarding database.
  1322. </div>
  1323. </div>
  1324. </section>
  1325. <!-- ================= SECTION: PPPoE CLIENTS ================= -->
  1326. <section id="section-pppoe" class="hidden space-y-6">
  1327. <div class="bg-gradient-to-r from-blue-950/40 via-slate-900 to-slate-900 border border-blue-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1328. <div>
  1329. <div class="flex items-center gap-2 mb-1">
  1330. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-blue-500/20 text-blue-300 border border-blue-500/30 tracking-wide uppercase">WAN Access</span>
  1331. <h2 class="text-base font-bold text-white">PPPoE Client Sessions</h2>
  1332. </div>
  1333. <p class="text-xs text-slate-400">Point-to-Point Protocol over Ethernet (PPPoE) client management with automated reconnection, default gateway negotiation, and peer DNS acquisition.</p>
  1334. </div>
  1335. <div class="flex items-center gap-2">
  1336. <button onclick="navigate('ppp'); switchPPPSubTab('pppoe')" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-blue-300 border border-blue-900/40 transition flex items-center gap-1.5" title="Configure Router as PPPoE Access Concentrator Server">
  1337. <span>⚙ Server Settings</span>
  1338. </button>
  1339. <button onclick="openAddPPPoEModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-500 text-white shadow-lg shadow-blue-500/20 transition flex items-center gap-1.5">
  1340. <span>+ Add PPPoE Client</span>
  1341. </button>
  1342. </div>
  1343. </div>
  1344. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1345. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1346. <h3 class="text-sm font-bold text-white">Configured PPPoE Interfaces</h3>
  1347. <span id="pppoe-count-badge" class="text-xs font-mono bg-slate-800 text-slate-300 px-2.5 py-0.5 rounded-full">0 Interfaces</span>
  1348. </div>
  1349. <div class="overflow-x-auto">
  1350. <table class="w-full text-left text-xs font-mono">
  1351. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1352. <tr>
  1353. <th class="py-2.5 px-3 w-12 text-center" title="Flags: R=Running, X=Disabled">Flag</th>
  1354. <th class="py-2.5 px-3">Name</th>
  1355. <th class="py-2.5 px-3">Interface</th>
  1356. <th class="py-2.5 px-3">User</th>
  1357. <th class="py-2.5 px-3">Profile</th>
  1358. <th class="py-2.5 px-3">Assigned IP</th>
  1359. <th class="py-2.5 px-3">Device</th>
  1360. <th class="py-2.5 px-3 text-center">Def. Route</th>
  1361. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1362. </tr>
  1363. </thead>
  1364. <tbody id="pppoe-table-body" class="divide-y divide-slate-800/80"></tbody>
  1365. </table>
  1366. </div>
  1367. <div id="pppoe-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1368. No PPPoE clients configured. Click "+ Add PPPoE Client" to add a connection.
  1369. </div>
  1370. </div>
  1371. </section>
  1372. <!-- ================= SECTION: WIREGUARD ================= -->
  1373. <section id="section-wireguard" class="hidden space-y-6">
  1374. <div class="bg-gradient-to-r from-rose-950/40 via-slate-900 to-slate-900 border border-rose-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1375. <div>
  1376. <div class="flex items-center gap-2 mb-1">
  1377. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-rose-500/20 text-rose-300 border border-rose-500/30 tracking-wide uppercase">Cryptographic VPN</span>
  1378. <h2 class="text-base font-bold text-white">WireGuard VPN Suite</h2>
  1379. </div>
  1380. <p class="text-xs text-slate-400">High-performance multi-interface WireGuard VPN with kernel-accelerated cryptography, peer roaming, and endpoint keepalives.</p>
  1381. </div>
  1382. <div class="flex items-center space-x-2.5">
  1383. <button onclick="openAddWGInterfaceModal()" class="text-xs font-semibold px-3.5 py-2 rounded-lg bg-rose-600 hover:bg-rose-500 text-white shadow-lg shadow-rose-500/20 transition">
  1384. + Add Interface
  1385. </button>
  1386. <button onclick="openAddWGPeerModal()" class="text-xs font-semibold px-3.5 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-white border border-slate-700 transition">
  1387. + Add Peer
  1388. </button>
  1389. </div>
  1390. </div>
  1391. <!-- Sub Tabs: Interfaces / Peers -->
  1392. <div class="flex border-b border-slate-800 space-x-2">
  1393. <button id="wg-subtab-btn-ifaces" onclick="switchWGSubTab('ifaces')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-rose-500 text-white transition flex items-center gap-2">
  1394. <span>Interfaces</span>
  1395. <span id="wg-subtab-badge-ifaces" class="px-1.5 py-0.5 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  1396. </button>
  1397. <button id="wg-subtab-btn-peers" onclick="switchWGSubTab('peers')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2">
  1398. <span>Peers</span>
  1399. <span id="wg-subtab-badge-peers" class="px-1.5 py-0.5 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  1400. </button>
  1401. </div>
  1402. <!-- Sub-Tab 1: Interfaces -->
  1403. <div id="wg-subtab-ifaces" class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1404. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1405. <h3 class="text-sm font-bold text-white">WireGuard Interfaces</h3>
  1406. <span id="wg-ifaces-count" class="text-xs font-mono text-slate-400">0 Interfaces</span>
  1407. </div>
  1408. <div class="overflow-x-auto">
  1409. <table class="w-full text-left text-xs font-mono">
  1410. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1411. <tr>
  1412. <th class="py-2.5 px-3 w-12 text-center" title="Flags: R=Running, X=Disabled">Flag</th>
  1413. <th class="py-2.5 px-3">Name</th>
  1414. <th class="py-2.5 px-3">Listen Port</th>
  1415. <th class="py-2.5 px-3">Tunnel Address</th>
  1416. <th class="py-2.5 px-3">Public Key</th>
  1417. <th class="py-2.5 px-3">MTU</th>
  1418. <th class="py-2.5 px-3 text-center">Peers</th>
  1419. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1420. </tr>
  1421. </thead>
  1422. <tbody id="wg-ifaces-table-body" class="divide-y divide-slate-800/80"></tbody>
  1423. </table>
  1424. </div>
  1425. <div id="wg-ifaces-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1426. No WireGuard interfaces configured. Click "+ Add Interface" to create wg0.
  1427. </div>
  1428. </div>
  1429. <!-- Sub-Tab 2: Peers -->
  1430. <div id="wg-subtab-peers" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1431. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  1432. <div class="flex items-center gap-3">
  1433. <h3 class="text-sm font-bold text-white">WireGuard Peers</h3>
  1434. <span id="wg-peers-count" class="text-xs font-mono text-slate-400">0 Peers</span>
  1435. </div>
  1436. <div class="flex items-center gap-2">
  1437. <label class="text-xs text-slate-400 font-sans">Filter Interface:</label>
  1438. <select id="wg-peers-filter-iface" onchange="renderWGPeers()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white font-mono">
  1439. <option value="">All Interfaces</option>
  1440. </select>
  1441. </div>
  1442. </div>
  1443. <div class="overflow-x-auto">
  1444. <table class="w-full text-left text-xs font-mono">
  1445. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1446. <tr>
  1447. <th class="py-2.5 px-3 w-12 text-center" title="Flags: R=Active, X=Disabled">Flag</th>
  1448. <th class="py-2.5 px-3">Interface</th>
  1449. <th class="py-2.5 px-3">Peer Name</th>
  1450. <th class="py-2.5 px-3">Public Key</th>
  1451. <th class="py-2.5 px-3">Allowed IPs</th>
  1452. <th class="py-2.5 px-3">Endpoint</th>
  1453. <th class="py-2.5 px-3">Latest Handshake</th>
  1454. <th class="py-2.5 px-3">Traffic (RX/TX)</th>
  1455. <th class="py-2.5 px-3 text-right font-sans">Action</th>
  1456. </tr>
  1457. </thead>
  1458. <tbody id="wg-peers-table-body" class="divide-y divide-slate-800/80"></tbody>
  1459. </table>
  1460. </div>
  1461. <div id="wg-peers-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1462. No peers found for the selected interface. Click "+ Add Peer" to add a client or branch.
  1463. </div>
  1464. </div>
  1465. </section>
  1466. <!-- ================= SECTION: IPSEC ================= -->
  1467. <section id="section-ipsec" class="hidden space-y-6">
  1468. <div class="bg-gradient-to-r from-amber-950/40 via-slate-900 to-slate-900 border border-amber-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1469. <div>
  1470. <div class="flex items-center gap-2 mb-1">
  1471. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-amber-500/20 text-amber-300 border border-amber-500/30 tracking-wide uppercase">Site-to-Site & Roadwarrior</span>
  1472. <h2 class="text-base font-bold text-white">IPSec (IKEv2) Tunnels</h2>
  1473. </div>
  1474. <p class="text-xs text-slate-400">Enterprise IKEv2 / IPsec cryptographic tunnels with policy routing, automated SA rekeying, and Roadwarrior mobile client support.</p>
  1475. </div>
  1476. <div class="flex items-center gap-2">
  1477. <button onclick="openIPSecServerModal()" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-amber-300 border border-amber-900/40 transition flex items-center gap-1.5" title="Configure Router as IPSec IKEv2 Roadwarrior Mobile Client Server">
  1478. <span>⚙ Roadwarrior Server</span>
  1479. </button>
  1480. <button onclick="openAddIPSecModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20 transition">
  1481. + Add IPSec Tunnel
  1482. </button>
  1483. </div>
  1484. </div>
  1485. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1486. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1487. <h3 class="text-sm font-bold text-white">Configured IPSec Tunnels</h3>
  1488. <span id="ipsec-sas-badge" class="text-xs font-mono bg-slate-800 text-slate-300 px-2.5 py-0.5 rounded-full">0 Active SAs</span>
  1489. </div>
  1490. <div class="overflow-x-auto">
  1491. <table class="w-full text-left text-xs font-mono">
  1492. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1493. <tr>
  1494. <th class="py-2.5 px-3 w-12 text-center" title="Tunnel Status: Green=Active SA, Red=Down">Status</th>
  1495. <th class="py-2.5 px-4">Tunnel Name</th>
  1496. <th class="py-2.5 px-4">Remote Gateway</th>
  1497. <th class="py-2.5 px-4">Phase 1 (IKE)</th>
  1498. <th class="py-2.5 px-4">Phase 2 Selectors (Subnets)</th>
  1499. <th class="py-2.5 px-4">Phase 2 (ESP)</th>
  1500. <th class="py-2.5 px-4 text-right font-sans">Actions</th>
  1501. </tr>
  1502. </thead>
  1503. <tbody id="ipsec-table-body" class="divide-y divide-slate-800"></tbody>
  1504. </table>
  1505. </div>
  1506. <div id="ipsec-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1507. No IPSec tunnels configured. Click "+ Add IPSec Tunnel" to set up site-to-site networking.
  1508. </div>
  1509. </div>
  1510. </section>
  1511. <!-- ================= SECTION: L2TP CLIENT ================= -->
  1512. <section id="section-l2tp" class="hidden space-y-6">
  1513. <div class="bg-gradient-to-r from-cyan-950/40 via-slate-900 to-slate-900 border border-cyan-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1514. <div>
  1515. <div class="flex items-center gap-2 mb-1">
  1516. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-cyan-500/20 text-cyan-300 border border-cyan-500/30 tracking-wide uppercase">L2TP Client</span>
  1517. <h2 class="text-base font-bold text-white">L2TP Client Sessions</h2>
  1518. </div>
  1519. <p class="text-xs text-slate-400">Layer 2 Tunneling Protocol (L2TP) multi-instance client manager with optional IPsec pre-shared key encapsulation.</p>
  1520. </div>
  1521. <div class="flex items-center gap-2">
  1522. <button onclick="navigate('ppp'); switchPPPSubTab('l2tp')" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-cyan-300 border border-cyan-900/40 transition flex items-center gap-1.5" title="Configure Router as L2TP Server">
  1523. <span>⚙ Server Settings</span>
  1524. </button>
  1525. <button onclick="openAddL2TPModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20 transition">
  1526. + Add L2TP Client
  1527. </button>
  1528. </div>
  1529. </div>
  1530. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1531. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1532. <h3 class="text-sm font-bold text-white">Configured L2TP Clients</h3>
  1533. <span id="l2tp-count-badge" class="text-xs font-mono bg-slate-800 text-slate-300 px-2.5 py-0.5 rounded-full">0 Clients</span>
  1534. </div>
  1535. <div class="overflow-x-auto">
  1536. <table class="w-full text-left text-xs font-mono">
  1537. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1538. <tr>
  1539. <th class="py-2.5 px-3 w-12 text-center" title="Flags: R=Running, X=Disabled">Flag</th>
  1540. <th class="py-2.5 px-3">Name</th>
  1541. <th class="py-2.5 px-3">Server Address</th>
  1542. <th class="py-2.5 px-3">User</th>
  1543. <th class="py-2.5 px-3">Profile</th>
  1544. <th class="py-2.5 px-3">Assigned IP</th>
  1545. <th class="py-2.5 px-3">Device</th>
  1546. <th class="py-2.5 px-3 text-center">Def. Route</th>
  1547. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1548. </tr>
  1549. </thead>
  1550. <tbody id="l2tp-table-body" class="divide-y divide-slate-800/80"></tbody>
  1551. </table>
  1552. </div>
  1553. <div id="l2tp-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1554. No L2TP clients configured. Click "+ Add L2TP Client" to add a tunnel.
  1555. </div>
  1556. </div>
  1557. </section>
  1558. <!-- ================= SECTION: PPTP CLIENT ================= -->
  1559. <section id="section-pptp" class="hidden space-y-6">
  1560. <div class="bg-gradient-to-r from-purple-950/40 via-slate-900 to-slate-900 border border-purple-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1561. <div>
  1562. <div class="flex items-center gap-2 mb-1">
  1563. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-purple-500/20 text-purple-300 border border-purple-500/30 tracking-wide uppercase">PPTP Client</span>
  1564. <h2 class="text-base font-bold text-white">PPTP Client Sessions</h2>
  1565. </div>
  1566. <p class="text-xs text-slate-400">Point-to-Point Tunneling Protocol (PPTP) client management with MPPE-128 stateful encryption and automated routing.</p>
  1567. </div>
  1568. <div class="flex items-center gap-2">
  1569. <button onclick="navigate('ppp'); switchPPPSubTab('pptp')" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-purple-300 border border-purple-900/40 transition flex items-center gap-1.5" title="Configure Router as PPTP Server">
  1570. <span>⚙ Server Settings</span>
  1571. </button>
  1572. <button onclick="openAddPPTPModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 text-white shadow-lg shadow-purple-500/20 transition">
  1573. + Add PPTP Client
  1574. </button>
  1575. </div>
  1576. </div>
  1577. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1578. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1579. <h3 class="text-sm font-bold text-white">Configured PPTP Clients</h3>
  1580. <span id="pptp-count-badge" class="text-xs font-mono bg-slate-800 text-slate-300 px-2.5 py-0.5 rounded-full">0 Clients</span>
  1581. </div>
  1582. <div class="overflow-x-auto">
  1583. <table class="w-full text-left text-xs font-mono">
  1584. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1585. <tr>
  1586. <th class="py-2.5 px-3 w-12 text-center" title="Flags: R=Running, X=Disabled">Flag</th>
  1587. <th class="py-2.5 px-3">Name</th>
  1588. <th class="py-2.5 px-3">Server Address</th>
  1589. <th class="py-2.5 px-3">User</th>
  1590. <th class="py-2.5 px-3">Profile</th>
  1591. <th class="py-2.5 px-3">Assigned IP</th>
  1592. <th class="py-2.5 px-3">Device</th>
  1593. <th class="py-2.5 px-3 text-center">MPPE</th>
  1594. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1595. </tr>
  1596. </thead>
  1597. <tbody id="pptp-table-body" class="divide-y divide-slate-800/80"></tbody>
  1598. </table>
  1599. </div>
  1600. <div id="pptp-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1601. No PPTP clients configured. Click "+ Add PPTP Client" to add a tunnel.
  1602. </div>
  1603. </div>
  1604. </section>
  1605. <!-- ================= SECTION: PPP SERVERS & SECRETS ================= -->
  1606. <section id="section-ppp" class="hidden space-y-6">
  1607. <div class="bg-gradient-to-r from-violet-950/40 via-slate-900 to-slate-900 border border-violet-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1608. <div>
  1609. <div class="flex items-center gap-2 mb-1">
  1610. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-violet-500/20 text-violet-300 border border-violet-500/30 tracking-wide uppercase">Subscriber Access</span>
  1611. <h2 class="text-base font-bold text-white">PPP Servers, Secrets & Profiles</h2>
  1612. </div>
  1613. <p class="text-xs text-slate-400">Centralized subscriber credentials, dynamic address assignment pools, and multi-protocol VPN access concentrators.</p>
  1614. </div>
  1615. <div class="flex items-center space-x-2.5">
  1616. <button id="ppp-primary-add-btn" onclick="openCurrentPPPModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-violet-600 hover:bg-violet-500 text-white shadow-lg shadow-violet-500/20 transition flex items-center gap-1.5">
  1617. <span>+ Add User (Secret)</span>
  1618. </button>
  1619. </div>
  1620. </div>
  1621. <!-- Sub Tabs: Secrets / Profiles / PPPoE Server / PPTP Server / L2TP Server / Active Sessions -->
  1622. <div class="flex border-b border-slate-800 space-x-1 overflow-x-auto">
  1623. <button id="ppp-subtab-btn-secrets" onclick="switchPPPSubTab('secrets')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-violet-500 text-white transition flex items-center gap-2 whitespace-nowrap">
  1624. <span>Secrets (Users)</span>
  1625. <span id="ppp-badge-secrets" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  1626. </button>
  1627. <button id="ppp-subtab-btn-profiles" onclick="switchPPPSubTab('profiles')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  1628. <span>Profiles (Pools)</span>
  1629. <span id="ppp-badge-profiles" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  1630. </button>
  1631. <button id="ppp-subtab-btn-pppoe" onclick="switchPPPSubTab('pppoe')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  1632. <span>PPPoE Server</span>
  1633. <span id="ppp-badge-pppoe-state" class="w-2 h-2 rounded-full bg-slate-600"></span>
  1634. </button>
  1635. <button id="ppp-subtab-btn-pptp" onclick="switchPPPSubTab('pptp')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  1636. <span>PPTP Server</span>
  1637. <span id="ppp-badge-pptp-state" class="w-2 h-2 rounded-full bg-slate-600"></span>
  1638. </button>
  1639. <button id="ppp-subtab-btn-l2tp" onclick="switchPPPSubTab('l2tp')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  1640. <span>L2TP Server</span>
  1641. <span id="ppp-badge-l2tp-state" class="w-2 h-2 rounded-full bg-slate-600"></span>
  1642. </button>
  1643. <button id="ppp-subtab-btn-active" onclick="switchPPPSubTab('active')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  1644. <span>Active Sessions</span>
  1645. <span id="ppp-badge-active" class="px-1.5 py-0.2 rounded text-[10px] bg-emerald-950 text-emerald-400 border border-emerald-800/40">0</span>
  1646. </button>
  1647. </div>
  1648. <!-- TAB 1: SECRETS (USERS) -->
  1649. <div id="ppp-subtab-secrets" class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1650. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1651. <h3 class="text-sm font-bold text-white">PPP Secrets (Subscriber Credentials)</h3>
  1652. <span id="secrets-count-badge" class="text-xs font-mono text-slate-400">0 Users</span>
  1653. </div>
  1654. <div class="overflow-x-auto">
  1655. <table class="w-full text-left text-xs font-mono">
  1656. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1657. <tr>
  1658. <th class="py-2.5 px-3 w-12 text-center">Flag</th>
  1659. <th class="py-2.5 px-3">Name (Username)</th>
  1660. <th class="py-2.5 px-3">Service</th>
  1661. <th class="py-2.5 px-3">Profile</th>
  1662. <th class="py-2.5 px-3">Local Address</th>
  1663. <th class="py-2.5 px-3">Remote Address</th>
  1664. <th class="py-2.5 px-3">Comment</th>
  1665. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1666. </tr>
  1667. </thead>
  1668. <tbody id="secrets-table-body" class="divide-y divide-slate-800/80"></tbody>
  1669. </table>
  1670. </div>
  1671. <div id="secrets-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1672. No PPP secrets configured. Click "+ Add User" to define an account.
  1673. </div>
  1674. </div>
  1675. <!-- TAB 2: PROFILES (POOLS) -->
  1676. <div id="ppp-subtab-profiles" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1677. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1678. <h3 class="text-sm font-bold text-white">PPP Profiles (IP Pools & Policies)</h3>
  1679. <span id="profiles-count-badge" class="text-xs font-mono text-slate-400">0 Profiles</span>
  1680. </div>
  1681. <div class="overflow-x-auto">
  1682. <table class="w-full text-left text-xs font-mono">
  1683. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1684. <tr>
  1685. <th class="py-2.5 px-3">Profile Name</th>
  1686. <th class="py-2.5 px-3">Local Address</th>
  1687. <th class="py-2.5 px-3">Remote Address (Pool)</th>
  1688. <th class="py-2.5 px-3">DNS Servers</th>
  1689. <th class="py-2.5 px-3">Encryption</th>
  1690. <th class="py-2.5 px-3 text-center">Only One</th>
  1691. <th class="py-2.5 px-3">Comment</th>
  1692. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1693. </tr>
  1694. </thead>
  1695. <tbody id="profiles-table-body" class="divide-y divide-slate-800/80"></tbody>
  1696. </table>
  1697. </div>
  1698. </div>
  1699. <!-- TAB 3: PPPoE SERVER -->
  1700. <div id="ppp-subtab-pppoe" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl p-6">
  1701. <form onsubmit="savePPPoEServerConfig(event)" class="max-w-2xl space-y-5">
  1702. <div class="flex items-center justify-between border-b border-slate-800 pb-4">
  1703. <div>
  1704. <h3 class="text-sm font-bold text-white">PPPoE Access Concentrator Server</h3>
  1705. <p class="text-xs text-slate-400">Listens on physical interface or VLAN for PPPoE Discovery (PADI) packets</p>
  1706. </div>
  1707. <div class="flex items-center gap-3">
  1708. <span id="pppoe-server-status-pill" class="px-2 py-0.5 rounded text-[10px] font-mono font-semibold bg-slate-800 text-slate-400">STOPPED</span>
  1709. <label class="relative inline-flex items-center cursor-pointer">
  1710. <input type="checkbox" id="pppoe-server-enabled" class="sr-only peer">
  1711. <div class="w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-slate-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
  1712. </label>
  1713. </div>
  1714. </div>
  1715. <div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-xs font-sans">
  1716. <div>
  1717. <label class="block text-slate-300 font-medium mb-1">Service Name</label>
  1718. <input type="text" id="pppoe-server-name" placeholder="debtik-pppoe" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  1719. </div>
  1720. <div>
  1721. <label class="block text-slate-300 font-medium mb-1">Listen Interface</label>
  1722. <select id="pppoe-server-iface" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  1723. </div>
  1724. <div>
  1725. <label class="block text-slate-300 font-medium mb-1">Default Profile</label>
  1726. <select id="pppoe-server-profile" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  1727. </div>
  1728. <div>
  1729. <label class="block text-slate-300 font-medium mb-1">Max MTU / MRU</label>
  1730. <input type="number" id="pppoe-server-mtu" value="1480" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  1731. </div>
  1732. </div>
  1733. <div class="flex items-center space-x-2 pt-2">
  1734. <input type="checkbox" id="pppoe-server-onesession" class="rounded bg-slate-800 border-slate-700 text-blue-600">
  1735. <label for="pppoe-server-onesession" class="text-xs text-slate-300">One Session Per Host (limit 1 PPPoE session per client MAC)</label>
  1736. </div>
  1737. <div class="pt-4 border-t border-slate-800 flex justify-end">
  1738. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-blue-600 hover:bg-blue-500 text-white shadow-lg shadow-blue-500/20 transition">
  1739. Apply PPPoE Server Settings
  1740. </button>
  1741. </div>
  1742. </form>
  1743. </div>
  1744. <!-- TAB 4: PPTP SERVER -->
  1745. <div id="ppp-subtab-pptp" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl p-6">
  1746. <form onsubmit="savePPTPServerConfig(event)" class="max-w-2xl space-y-5">
  1747. <div class="flex items-center justify-between border-b border-slate-800 pb-4">
  1748. <div>
  1749. <h3 class="text-sm font-bold text-white">PPTP Server Settings</h3>
  1750. <p class="text-xs text-slate-400">Point-to-Point Tunneling Server listening on TCP port 1723 with MPPE-128 encryption</p>
  1751. </div>
  1752. <div class="flex items-center gap-3">
  1753. <span id="pptp-server-status-pill" class="px-2 py-0.5 rounded text-[10px] font-mono font-semibold bg-slate-800 text-slate-400">STOPPED</span>
  1754. <label class="relative inline-flex items-center cursor-pointer">
  1755. <input type="checkbox" id="pptp-server-enabled" class="sr-only peer">
  1756. <div class="w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-slate-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-purple-600"></div>
  1757. </label>
  1758. </div>
  1759. </div>
  1760. <div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-xs font-sans">
  1761. <div>
  1762. <label class="block text-slate-300 font-medium mb-1">Port</label>
  1763. <input type="number" id="pptp-server-port" value="1723" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  1764. </div>
  1765. <div>
  1766. <label class="block text-slate-300 font-medium mb-1">Default Profile</label>
  1767. <select id="pptp-server-profile" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  1768. </div>
  1769. </div>
  1770. <div class="flex items-center space-x-2 pt-2">
  1771. <input type="checkbox" id="pptp-server-mppe" checked class="rounded bg-slate-800 border-slate-700 text-purple-600">
  1772. <label for="pptp-server-mppe" class="text-xs text-slate-300">Require 128-bit MPPE Encryption (MS-CHAPv2)</label>
  1773. </div>
  1774. <div class="pt-4 border-t border-slate-800 flex justify-end">
  1775. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-purple-600 hover:bg-purple-500 text-white shadow-lg shadow-purple-500/20 transition">
  1776. Apply PPTP Server Settings
  1777. </button>
  1778. </div>
  1779. </form>
  1780. </div>
  1781. <!-- TAB 5: L2TP SERVER -->
  1782. <div id="ppp-subtab-l2tp" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl p-6">
  1783. <form onsubmit="saveL2TPServerConfig(event)" class="max-w-2xl space-y-5">
  1784. <div class="flex items-center justify-between border-b border-slate-800 pb-4">
  1785. <div>
  1786. <h3 class="text-sm font-bold text-white">L2TP Server Settings</h3>
  1787. <p class="text-xs text-slate-400">Layer 2 Tunneling Protocol Server listening on UDP port 1701 with optional IPSec</p>
  1788. </div>
  1789. <div class="flex items-center gap-3">
  1790. <span id="l2tp-server-status-pill" class="px-2 py-0.5 rounded text-[10px] font-mono font-semibold bg-slate-800 text-slate-400">STOPPED</span>
  1791. <label class="relative inline-flex items-center cursor-pointer">
  1792. <input type="checkbox" id="l2tp-server-enabled" class="sr-only peer">
  1793. <div class="w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-slate-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-cyan-600"></div>
  1794. </label>
  1795. </div>
  1796. </div>
  1797. <div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-xs font-sans">
  1798. <div>
  1799. <label class="block text-slate-300 font-medium mb-1">Port</label>
  1800. <input type="number" id="l2tp-server-port" value="1701" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  1801. </div>
  1802. <div>
  1803. <label class="block text-slate-300 font-medium mb-1">Default Profile</label>
  1804. <select id="l2tp-server-profile" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  1805. </div>
  1806. <div>
  1807. <label class="block text-slate-300 font-medium mb-1">Use IPSec</label>
  1808. <select id="l2tp-server-useipsec" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  1809. <option value="no">no</option>
  1810. <option value="yes">yes</option>
  1811. <option value="required">required</option>
  1812. </select>
  1813. </div>
  1814. <div>
  1815. <label class="block text-slate-300 font-medium mb-1">IPSec Secret (PSK)</label>
  1816. <input type="password" id="l2tp-server-ipsecsecret" placeholder="Optional Pre-Shared Key" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  1817. </div>
  1818. </div>
  1819. <div class="pt-4 border-t border-slate-800 flex justify-end">
  1820. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20 transition">
  1821. Apply L2TP Server Settings
  1822. </button>
  1823. </div>
  1824. </form>
  1825. </div>
  1826. <!-- TAB 6: ACTIVE SESSIONS -->
  1827. <div id="ppp-subtab-active" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1828. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1829. <h3 class="text-sm font-bold text-white">Active PPP Connections</h3>
  1830. <button onclick="loadPPPSessions()" class="text-xs text-slate-400 hover:text-white px-2 py-1 rounded bg-slate-800">Refresh</button>
  1831. </div>
  1832. <div class="overflow-x-auto">
  1833. <table class="w-full text-left text-xs font-mono">
  1834. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1835. <tr>
  1836. <th class="py-2.5 px-3">Interface</th>
  1837. <th class="py-2.5 px-3">Username</th>
  1838. <th class="py-2.5 px-3">Service</th>
  1839. <th class="py-2.5 px-3">Client IP Address</th>
  1840. <th class="py-2.5 px-3">Uptime</th>
  1841. <th class="py-2.5 px-3">RX / TX Traffic</th>
  1842. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1843. </tr>
  1844. </thead>
  1845. <tbody id="active-ppp-table-body" class="divide-y divide-slate-800/80"></tbody>
  1846. </table>
  1847. </div>
  1848. <div id="active-ppp-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1849. No active PPP sessions currently connected.
  1850. </div>
  1851. </div>
  1852. </section>
  1853. <!-- ================= SECTION: EoIP TUNNELS ================= -->
  1854. <section id="section-eoip" class="hidden space-y-6">
  1855. <div class="bg-gradient-to-r from-emerald-950/40 via-slate-900 to-slate-900 border border-emerald-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1856. <div>
  1857. <div class="flex items-center gap-2 mb-1">
  1858. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 tracking-wide uppercase">Layer 2 Tunneling</span>
  1859. <h2 class="text-base font-bold text-white">EoIP Tunnels (Ethernet over IP)</h2>
  1860. </div>
  1861. <p class="text-xs text-slate-400">Ethernet over IP (EoIP / GRETAP) encapsulation protocol to transparently bridge Layer 2 broadcast domains across IP networks.</p>
  1862. </div>
  1863. <button onclick="openAddEoIPModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20 transition flex items-center gap-1.5">
  1864. <span>+ Add EoIP Tunnel</span>
  1865. </button>
  1866. </div>
  1867. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1868. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1869. <h3 class="text-sm font-bold text-white">Configured EoIP Interfaces</h3>
  1870. <span id="eoip-count-badge" class="text-xs font-mono bg-slate-800 text-slate-300 px-2.5 py-0.5 rounded-full">0 Tunnels</span>
  1871. </div>
  1872. <div class="overflow-x-auto">
  1873. <table class="w-full text-left text-xs font-mono">
  1874. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1875. <tr>
  1876. <th class="py-2.5 px-3 w-12 text-center" title="Flags: R=Running, X=Disabled">Flag</th>
  1877. <th class="py-2.5 px-3">Name</th>
  1878. <th class="py-2.5 px-3">Tunnel ID</th>
  1879. <th class="py-2.5 px-3">Tunnel IP</th>
  1880. <th class="py-2.5 px-3">Remote Address</th>
  1881. <th class="py-2.5 px-3">Local Address</th>
  1882. <th class="py-2.5 px-3">MAC Address</th>
  1883. <th class="py-2.5 px-3">MTU</th>
  1884. <th class="py-2.5 px-3">RX / TX Traffic</th>
  1885. <th class="py-2.5 px-3">Comment</th>
  1886. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1887. </tr>
  1888. </thead>
  1889. <tbody id="eoip-table-body" class="divide-y divide-slate-800/80"></tbody>
  1890. </table>
  1891. </div>
  1892. <div id="eoip-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1893. No EoIP tunnels configured. Click "+ Add EoIP Tunnel" to create one.
  1894. </div>
  1895. </div>
  1896. </section>
  1897. <!-- ================= SECTION: VXLAN TUNNELS ================= -->
  1898. <section id="section-vxlan" class="hidden space-y-6">
  1899. <div class="bg-gradient-to-r from-amber-950/40 via-slate-900 to-slate-900 border border-amber-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1900. <div>
  1901. <div class="flex items-center gap-2 mb-1">
  1902. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-amber-500/20 text-amber-300 border border-amber-500/30 tracking-wide uppercase">Layer 2 Overlay</span>
  1903. <h2 class="text-base font-bold text-white">VXLAN (Virtual Extensible LAN)</h2>
  1904. </div>
  1905. <p class="text-xs text-slate-400">RFC 7348 standard L2 overlay network encapsulated in UDP packets (port 4789). Supports 16M VNIs and static VTEPs for unicast flooding matching MikroTik RouterOS v7.</p>
  1906. </div>
  1907. <div class="flex items-center gap-2">
  1908. <button onclick="openAddVXLANVtepModal()" class="text-xs font-semibold px-3.5 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-amber-300 border border-amber-500/30 transition flex items-center gap-1.5">
  1909. <span>+ Add Remote VTEP</span>
  1910. </button>
  1911. <button onclick="openAddVXLANModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20 transition flex items-center gap-1.5">
  1912. <span>+ Add VXLAN Tunnel</span>
  1913. </button>
  1914. </div>
  1915. </div>
  1916. <!-- VXLAN Interfaces Table -->
  1917. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1918. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1919. <h3 class="text-sm font-bold text-white">Configured VXLAN Interfaces</h3>
  1920. <span id="vxlan-count-badge" class="text-xs font-mono bg-slate-800 text-slate-300 px-2.5 py-0.5 rounded-full">0 Tunnels</span>
  1921. </div>
  1922. <div class="overflow-x-auto">
  1923. <table class="w-full text-left text-xs font-mono">
  1924. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1925. <tr>
  1926. <th class="py-2.5 px-3 w-12 text-center" title="Flags: R=Running, X=Disabled">Flag</th>
  1927. <th class="py-2.5 px-3">Name</th>
  1928. <th class="py-2.5 px-3">VNI</th>
  1929. <th class="py-2.5 px-3">Port</th>
  1930. <th class="py-2.5 px-3">Overlay IP</th>
  1931. <th class="py-2.5 px-3">Underlay (Dev / Local)</th>
  1932. <th class="py-2.5 px-3">MAC Address</th>
  1933. <th class="py-2.5 px-3">MTU</th>
  1934. <th class="py-2.5 px-3">VTEPs</th>
  1935. <th class="py-2.5 px-3">RX / TX Traffic</th>
  1936. <th class="py-2.5 px-3">Comment</th>
  1937. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1938. </tr>
  1939. </thead>
  1940. <tbody id="vxlan-table-body" class="divide-y divide-slate-800/80"></tbody>
  1941. </table>
  1942. </div>
  1943. <div id="vxlan-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  1944. No VXLAN interfaces configured. Click "+ Add VXLAN Tunnel" to create one.
  1945. </div>
  1946. </div>
  1947. <!-- Remote VTEPs Table -->
  1948. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  1949. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  1950. <div>
  1951. <h3 class="text-sm font-bold text-white">Static Remote VTEPs (Flooding Peers)</h3>
  1952. <p class="text-[11px] text-slate-400">Unicast peer IP endpoints for BUM (Broadcast, Unknown unicast, Multicast) packet replication.</p>
  1953. </div>
  1954. <span id="vxlan-vteps-count-badge" class="text-xs font-mono bg-slate-800 text-slate-300 px-2.5 py-0.5 rounded-full">0 VTEPs</span>
  1955. </div>
  1956. <div class="overflow-x-auto">
  1957. <table class="w-full text-left text-xs font-mono">
  1958. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  1959. <tr>
  1960. <th class="py-2.5 px-3">Parent Interface</th>
  1961. <th class="py-2.5 px-3">Remote IP</th>
  1962. <th class="py-2.5 px-3">UDP Port</th>
  1963. <th class="py-2.5 px-3">Comment</th>
  1964. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  1965. </tr>
  1966. </thead>
  1967. <tbody id="vxlan-vteps-table-body" class="divide-y divide-slate-800/80"></tbody>
  1968. </table>
  1969. </div>
  1970. <div id="vxlan-vteps-empty" class="hidden p-8 text-center text-slate-500 text-xs font-sans">
  1971. No static VTEPs configured. Click "+ Add Remote VTEP" to add a peer.
  1972. </div>
  1973. </div>
  1974. </section>
  1975. <!-- ================= SECTION: AeroTUN ================= -->
  1976. <section id="section-aerotun" class="hidden space-y-6">
  1977. <div class="bg-gradient-to-r from-sky-950/50 via-slate-900 to-slate-900 border border-sky-800/40 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  1978. <div>
  1979. <div class="flex items-center gap-2 mb-1">
  1980. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-sky-500/20 text-sky-300 border border-sky-500/30 tracking-wide uppercase">Encrypted Tunnel</span>
  1981. <h2 class="text-base font-bold text-white flex items-center gap-2">
  1982. <span>AeroTUN High-Speed VPN</span>
  1983. <span class="px-2 py-0.5 text-[10px] rounded-full bg-cyan-500/20 text-cyan-300 border border-cyan-500/40 font-mono font-normal">XChaCha20-Poly1305 + LZ4</span>
  1984. </h2>
  1985. </div>
  1986. <p class="text-xs text-slate-400">IP tunnels over UDP. V3 adds pinned peer identities, forward secrecy and automatic key rotation. Compression is optional.</p>
  1987. </div>
  1988. <div class="flex items-center gap-2.5">
  1989. <button onclick="loadAeroTUN(true)" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition flex items-center gap-1.5" title="Refresh AeroTUN Status">
  1990. <span>🔄 Refresh</span>
  1991. </button>
  1992. <button onclick="openAddAeroTUNModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-500/20 transition flex items-center gap-1.5">
  1993. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path></svg>
  1994. <span>+ Add AeroTUN Tunnel</span>
  1995. </button>
  1996. </div>
  1997. </div>
  1998. <!-- Summary Metric Cards -->
  1999. <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
  2000. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  2001. <div>
  2002. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Active Tunnels</div>
  2003. <div id="aerotun-stat-active" class="text-xl font-bold text-white font-mono mt-1">0 / 0</div>
  2004. <div class="text-[10px] text-slate-500 mt-0.5">Peers authenticated recently</div>
  2005. </div>
  2006. <div class="w-10 h-10 rounded-xl bg-sky-500/10 border border-sky-500/20 flex items-center justify-center text-sky-400">
  2007. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
  2008. </div>
  2009. </div>
  2010. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  2011. <div>
  2012. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Compression Savings</div>
  2013. <div id="aerotun-stat-saved" class="text-xl font-bold text-emerald-400 font-mono mt-1">0 B</div>
  2014. <div id="aerotun-stat-ratio" class="text-[10px] text-emerald-300 font-mono mt-0.5">1.00x (0% saved)</div>
  2015. </div>
  2016. <div class="w-10 h-10 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400">
  2017. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"/></svg>
  2018. </div>
  2019. </div>
  2020. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  2021. <div>
  2022. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Wire Traffic</div>
  2023. <div id="aerotun-stat-traffic" class="text-sm font-bold text-white font-mono mt-1">↓ 0 B / ↑ 0 B</div>
  2024. <div class="text-[10px] text-slate-500 mt-0.5">UDP Wire Transmit / Receive</div>
  2025. </div>
  2026. <div class="w-10 h-10 rounded-xl bg-cyan-500/10 border border-cyan-500/20 flex items-center justify-center text-cyan-400">
  2027. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"/></svg>
  2028. </div>
  2029. </div>
  2030. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  2031. <div>
  2032. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">AEAD Cipher & Anti-Replay</div>
  2033. <div class="text-xs font-bold text-sky-300 font-mono mt-1">ChaCha20-Poly1305</div>
  2034. <div class="text-[10px] text-slate-400 mt-0.5">Authenticated packets and replay protection</div>
  2035. </div>
  2036. <div class="w-10 h-10 rounded-xl bg-indigo-500/10 border border-indigo-500/20 flex items-center justify-center text-indigo-400">
  2037. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/></svg>
  2038. </div>
  2039. </div>
  2040. </div>
  2041. <!-- AeroTUN Interfaces Table -->
  2042. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2043. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  2044. <div class="flex items-center gap-2">
  2045. <h3 class="text-sm font-bold text-white">Configured AeroTUN Tunnels</h3>
  2046. <span id="aerotun-count-badge" class="text-xs font-mono bg-slate-800 text-slate-300 px-2.5 py-0.5 rounded-full">0 Tunnels</span>
  2047. </div>
  2048. <div class="text-xs text-slate-400">
  2049. <span class="font-mono text-emerald-400">R</span> = Running &nbsp;|&nbsp; <span class="font-mono text-rose-400">X</span> = Disabled
  2050. </div>
  2051. </div>
  2052. <div class="overflow-x-auto">
  2053. <table class="w-full text-left text-xs font-mono">
  2054. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2055. <tr>
  2056. <th class="py-2.5 px-3 w-12 text-center" title="R=Authenticated peer seen within 65 seconds, X=Disabled">Flag</th>
  2057. <th class="py-2.5 px-3">Name</th>
  2058. <th class="py-2.5 px-3">Mode</th>
  2059. <th class="py-2.5 px-3">Port / Remote Endpoint</th>
  2060. <th class="py-2.5 px-3">Tunnel IP (CIDR)</th>
  2061. <th class="py-2.5 px-3">Compression</th>
  2062. <th class="py-2.5 px-3">Active Peer</th>
  2063. <th class="py-2.5 px-3">Payload Traffic</th>
  2064. <th class="py-2.5 px-3">Wire Traffic</th>
  2065. <th class="py-2.5 px-3">Comment</th>
  2066. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  2067. </tr>
  2068. </thead>
  2069. <tbody id="aerotun-table-body" class="divide-y divide-slate-800/80"></tbody>
  2070. </table>
  2071. </div>
  2072. <div id="aerotun-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2073. No AeroTUN interfaces configured. Click "+ Add AeroTUN Tunnel" to create a fast encrypted tunnel.
  2074. </div>
  2075. </div>
  2076. <div id="aerotun-health-panel" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl p-5 space-y-4">
  2077. <div class="flex items-center justify-between gap-3"><h3 id="aerotun-health-title" class="font-bold text-sm text-white">Tunnel health</h3><button type="button" onclick="closeAeroTUNHealth()" class="text-slate-400 hover:text-white">Close</button></div>
  2078. <div id="aerotun-health-status" class="text-xs text-slate-300"></div>
  2079. <div class="flex gap-3 items-center"><button id="aerotun-health-check-btn" type="button" onclick="runAeroTUNHealthCheck()" class="px-3 py-2 rounded bg-sky-700 text-white text-xs">Check peer and MTU</button><span class="text-xs text-slate-400">Sends up to two ICMP probes through this tunnel.</span></div>
  2080. <div id="aerotun-health-result" role="status" class="text-xs text-slate-300"></div>
  2081. <div class="border-t border-slate-800 pt-3 space-y-2">
  2082. <div class="text-xs font-semibold text-slate-300">TCP bandwidth comparison</div>
  2083. <p class="text-xs text-slate-400">Runs four 8-second tests through the tunnel and directly. Both addresses must reach the same bandwidth test server. This can use the full available bandwidth.</p>
  2084. <div class="flex flex-wrap gap-2 items-center"><label class="text-xs text-slate-300">Direct server <input id="aerotun-compare-host" class="bg-slate-800 rounded px-2 py-1 w-44" placeholder="Server IP or hostname"></label><label class="text-xs text-slate-300">Port <input id="aerotun-compare-port" type="number" min="1" max="65535" value="2000" class="bg-slate-800 rounded px-2 py-1 w-20"></label><button id="aerotun-compare-btn" type="button" onclick="runAeroTUNComparison()" class="px-3 py-1.5 rounded bg-slate-700 text-white text-xs">Run comparison</button><button id="aerotun-compare-stop" type="button" onclick="stopAeroTUNComparison()" class="hidden px-3 py-1.5 rounded bg-slate-700 text-white text-xs">Stop after current test</button></div>
  2085. <div id="aerotun-compare-result" role="status" class="text-xs text-slate-300 whitespace-pre-line"></div>
  2086. </div>
  2087. <div><h4 class="text-xs font-semibold text-slate-300 mb-2">Health history</h4><div id="aerotun-health-history" class="text-xs text-slate-400 space-y-1 max-h-60 overflow-y-auto"></div></div>
  2088. </div>
  2089. </section>
  2090. <!-- ================= SECTION: FIREWALL ================= -->
  2091. <section id="section-firewall" class="hidden space-y-6">
  2092. <div id="fw-apply-error" class="hidden rounded border border-rose-700 bg-rose-950/40 p-3 text-sm text-rose-200"></div>
  2093. <div class="bg-gradient-to-r from-emerald-950/40 via-slate-900 to-slate-900 border border-emerald-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  2094. <div>
  2095. <div class="flex items-center gap-2 mb-1">
  2096. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 tracking-wide uppercase">Packet Inspection</span>
  2097. <h2 class="text-base font-bold text-white">iptables Firewall</h2>
  2098. </div>
  2099. <p class="text-xs text-slate-400">Manage Linux filter, NAT and mangle tables, address lists (ipset), GeoIP and connection tracking (conntrack). Rules are evaluated in order within each chain.</p>
  2100. </div>
  2101. <div class="flex items-center space-x-2.5">
  2102. <button id="fw-primary-add-btn" onclick="openCurrentFWModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20 transition flex items-center gap-1.5">
  2103. <span>+ Add Rule</span>
  2104. </button>
  2105. <button onclick="resetFirewallCounters()" class="text-xs font-medium px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition" title="Reset all packet & byte counters to zero">
  2106. Reset Counters
  2107. </button>
  2108. </div>
  2109. </div>
  2110. <!-- Sub Tabs: Filter / NAT / Mangle / Address Lists / Connections -->
  2111. <div class="flex border-b border-slate-800 space-x-1 overflow-x-auto">
  2112. <button id="fw-subtab-btn-filter" onclick="switchFWSubTab('filter')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-emerald-500 text-white transition flex items-center gap-2 whitespace-nowrap">
  2113. <span>Filter</span>
  2114. <span id="fw-badge-filter" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2115. </button>
  2116. <button id="fw-subtab-btn-nat" onclick="switchFWSubTab('nat')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  2117. <span>NAT</span>
  2118. <span id="fw-badge-nat" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2119. </button>
  2120. <button id="fw-subtab-btn-mangle" onclick="switchFWSubTab('mangle')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  2121. <span>Mangle</span>
  2122. <span id="fw-badge-mangle" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2123. </button>
  2124. <button id="fw-subtab-btn-addrlist" onclick="switchFWSubTab('addrlist')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  2125. <span>Address Lists</span>
  2126. <span id="fw-badge-addrlist" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2127. </button>
  2128. <button id="fw-subtab-btn-geo" onclick="switchFWSubTab('geo')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  2129. <span>Geo-Location</span>
  2130. <span id="fw-badge-geo" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2131. </button>
  2132. <button id="fw-subtab-btn-connections" onclick="switchFWSubTab('connections')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  2133. <span>Connections</span>
  2134. <span id="fw-badge-connections" class="px-1.5 py-0.2 rounded text-[10px] bg-emerald-950 text-emerald-400 border border-emerald-800/40">0</span>
  2135. </button>
  2136. </div>
  2137. <!-- ================= TAB 1: FILTER RULES ================= -->
  2138. <div id="fw-subtab-filter" class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2139. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2140. <div class="flex items-center gap-3">
  2141. <h3 class="text-sm font-bold text-white">Filter table</h3>
  2142. <span id="filter-count-badge" class="text-xs font-mono text-slate-400">0 Rules</span>
  2143. </div>
  2144. <div class="flex items-center gap-2">
  2145. <label class="text-xs text-slate-400 font-sans">Chain:</label>
  2146. <select id="filter-filter-chain" onchange="renderFilterRules()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white font-mono">
  2147. <option value="">All Chains</option>
  2148. <option value="input">input</option>
  2149. <option value="forward">forward</option>
  2150. <option value="output">output</option>
  2151. </select>
  2152. </div>
  2153. </div>
  2154. <div class="overflow-x-auto">
  2155. <table class="w-full text-left text-xs font-mono">
  2156. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2157. <tr>
  2158. <th class="py-2.5 px-3 w-10 text-center">#</th>
  2159. <th class="py-2.5 px-3 w-12 text-center" title="Status: X=Disabled, C=Comment">Status</th>
  2160. <th class="py-2.5 px-3">Chain</th>
  2161. <th class="py-2.5 px-3">Src. Address</th>
  2162. <th class="py-2.5 px-3">Dst. Address</th>
  2163. <th class="py-2.5 px-3">Protocol</th>
  2164. <th class="py-2.5 px-3">Ports</th>
  2165. <th class="py-2.5 px-3">In / Out</th>
  2166. <th class="py-2.5 px-3">Target</th>
  2167. <th class="py-2.5 px-3">Packets / Bytes</th>
  2168. <th class="py-2.5 px-3">Comment</th>
  2169. <th class="py-2.5 px-3 text-right font-sans">Order & Actions</th>
  2170. </tr>
  2171. </thead>
  2172. <tbody id="filter-table-body" class="divide-y divide-slate-800/80"></tbody>
  2173. </table>
  2174. </div>
  2175. <div id="filter-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2176. No filter rules found in this chain. Click "+ Add Rule" to create one.
  2177. </div>
  2178. </div>
  2179. <!-- ================= TAB 2: NAT RULES ================= -->
  2180. <div id="fw-subtab-nat" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2181. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2182. <div class="flex items-center gap-3">
  2183. <h3 class="text-sm font-bold text-white">NAT Rules</h3>
  2184. <span id="nat-count-badge" class="text-xs font-mono text-slate-400">0 Rules</span>
  2185. </div>
  2186. <div class="flex items-center gap-2">
  2187. <label class="text-xs text-slate-400 font-sans">Chain:</label>
  2188. <select id="nat-filter-chain" onchange="renderNATRules()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white font-mono">
  2189. <option value="">All Chains</option>
  2190. <option value="prerouting">PREROUTING</option>
  2191. <option value="input">INPUT</option>
  2192. <option value="output">OUTPUT</option>
  2193. <option value="postrouting">POSTROUTING</option>
  2194. </select>
  2195. </div>
  2196. </div>
  2197. <div class="overflow-x-auto">
  2198. <table class="w-full text-left text-xs font-mono">
  2199. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2200. <tr>
  2201. <th class="py-2.5 px-3 w-10 text-center">#</th>
  2202. <th class="py-2.5 px-3 w-12 text-center">Status</th>
  2203. <th class="py-2.5 px-3">Chain</th>
  2204. <th class="py-2.5 px-3">Src. Address</th>
  2205. <th class="py-2.5 px-3">Dst. Address</th>
  2206. <th class="py-2.5 px-3">Proto/Port</th>
  2207. <th class="py-2.5 px-3">Out / In</th>
  2208. <th class="py-2.5 px-3">Target</th>
  2209. <th class="py-2.5 px-3">To Address:Port</th>
  2210. <th class="py-2.5 px-3">Packets / Bytes</th>
  2211. <th class="py-2.5 px-3">Comment</th>
  2212. <th class="py-2.5 px-3 text-right font-sans">Order & Actions</th>
  2213. </tr>
  2214. </thead>
  2215. <tbody id="nat-table-body" class="divide-y divide-slate-800/80"></tbody>
  2216. </table>
  2217. </div>
  2218. <div id="nat-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2219. No NAT rules configured. Click "+ Add Rule" to add masquerade or port forwarding.
  2220. </div>
  2221. </div>
  2222. <!-- ================= TAB 3: MANGLE RULES ================= -->
  2223. <div id="fw-subtab-mangle" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2224. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2225. <div class="flex items-center gap-3">
  2226. <h3 class="text-sm font-bold text-white">Mangle table</h3>
  2227. <span id="mangle-count-badge" class="text-xs font-mono text-slate-400">0 Rules</span>
  2228. </div>
  2229. <div class="flex items-center gap-2">
  2230. <label class="text-xs text-slate-400 font-sans">Chain:</label>
  2231. <select id="mangle-filter-chain" onchange="renderMangleRules()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white font-mono">
  2232. <option value="">All Chains</option>
  2233. <option value="prerouting">prerouting</option>
  2234. <option value="forward">forward</option>
  2235. <option value="postrouting">postrouting</option>
  2236. <option value="input">input</option>
  2237. <option value="output">output</option>
  2238. </select>
  2239. </div>
  2240. </div>
  2241. <div class="overflow-x-auto">
  2242. <table class="w-full text-left text-xs font-mono">
  2243. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2244. <tr>
  2245. <th class="py-2.5 px-3 w-10 text-center">#</th>
  2246. <th class="py-2.5 px-3 w-12 text-center">Status</th>
  2247. <th class="py-2.5 px-3">Chain</th>
  2248. <th class="py-2.5 px-3">Src. Address</th>
  2249. <th class="py-2.5 px-3">Dst. Address</th>
  2250. <th class="py-2.5 px-3">Target</th>
  2251. <th class="py-2.5 px-3">New Mark / Value</th>
  2252. <th class="py-2.5 px-3 text-center">Pass</th>
  2253. <th class="py-2.5 px-3">Packets / Bytes</th>
  2254. <th class="py-2.5 px-3">Comment</th>
  2255. <th class="py-2.5 px-3 text-right font-sans">Order & Actions</th>
  2256. </tr>
  2257. </thead>
  2258. <tbody id="mangle-table-body" class="divide-y divide-slate-800/80"></tbody>
  2259. </table>
  2260. </div>
  2261. <div id="mangle-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2262. No mangle rules configured. Click "+ Add Rule" to mark connections or routing for Multi-WAN.
  2263. </div>
  2264. </div>
  2265. <!-- ================= TAB 4: ADDRESS LISTS ================= -->
  2266. <div id="fw-subtab-addrlist" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2267. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2268. <div class="flex items-center gap-3">
  2269. <h3 class="text-sm font-bold text-white">Address Lists (ipset)</h3>
  2270. <span id="addrlist-count-badge" class="text-xs font-mono text-slate-400">0 Entries</span>
  2271. </div>
  2272. <div class="flex items-center gap-2">
  2273. <label class="text-xs text-slate-400 font-sans">List:</label>
  2274. <select id="addrlist-filter-name" onchange="renderAddressLists()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white font-mono">
  2275. <option value="">All Lists</option>
  2276. </select>
  2277. </div>
  2278. </div>
  2279. <div class="overflow-x-auto">
  2280. <table class="w-full text-left text-xs font-mono">
  2281. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2282. <tr>
  2283. <th class="py-2.5 px-3 w-10 text-center">#</th>
  2284. <th class="py-2.5 px-3 w-12 text-center">Status</th>
  2285. <th class="py-2.5 px-3">List Name</th>
  2286. <th class="py-2.5 px-3">IP Address / CIDR</th>
  2287. <th class="py-2.5 px-3">Comment</th>
  2288. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  2289. </tr>
  2290. </thead>
  2291. <tbody id="addrlist-table-body" class="divide-y divide-slate-800/80"></tbody>
  2292. </table>
  2293. </div>
  2294. <div id="addrlist-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2295. No address list entries configured. Click "+ Add Item" to define a group of IPs/subnets.
  2296. </div>
  2297. </div>
  2298. <!-- ================= TAB 5: ACTIVE CONNECTIONS ================= -->
  2299. <div id="fw-subtab-connections" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2300. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2301. <div class="flex items-center gap-3">
  2302. <h3 class="text-sm font-bold text-white">Active Conntrack Connections</h3>
  2303. <span id="conns-count-badge" class="text-xs font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/30 px-2 py-0.5 rounded-full">0 Flows</span>
  2304. </div>
  2305. <div class="flex items-center gap-2 flex-wrap">
  2306. <input type="text" id="conns-search-input" oninput="renderConnections()" placeholder="Filter IP, port..." class="bg-slate-800 border border-slate-700 rounded-lg px-3 py-1 text-xs text-white font-mono focus:outline-none focus:border-emerald-500">
  2307. <select id="conns-filter-proto" onchange="renderConnections()" class="bg-slate-800 border border-slate-700 rounded-lg px-2 py-1 text-xs text-white font-mono">
  2308. <option value="">All Proto</option>
  2309. <option value="TCP">TCP</option>
  2310. <option value="UDP">UDP</option>
  2311. <option value="ICMP">ICMP</option>
  2312. </select>
  2313. <button onclick="loadConnections()" class="text-xs text-emerald-400 hover:text-emerald-300 font-semibold px-2 py-1 bg-slate-800 rounded border border-slate-700">Refresh</button>
  2314. </div>
  2315. </div>
  2316. <div class="overflow-x-auto max-h-[550px] overflow-y-auto">
  2317. <table class="w-full text-left text-xs font-mono">
  2318. <thead class="text-slate-400 bg-slate-950/80 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans sticky top-0 z-10 backdrop-blur-sm">
  2319. <tr>
  2320. <th class="py-2.5 px-3">Proto</th>
  2321. <th class="py-2.5 px-3">Source Address</th>
  2322. <th class="py-2.5 px-3">Destination Address</th>
  2323. <th class="py-2.5 px-3">Reply Source</th>
  2324. <th class="py-2.5 px-3">Reply Dest</th>
  2325. <th class="py-2.5 px-3">TCP State</th>
  2326. <th class="py-2.5 px-3">Mark</th>
  2327. <th class="py-2.5 px-3">Timeout</th>
  2328. <th class="py-2.5 px-3 text-right font-sans">Target</th>
  2329. </tr>
  2330. </thead>
  2331. <tbody id="conns-table-body" class="divide-y divide-slate-800/60"></tbody>
  2332. </table>
  2333. </div>
  2334. <div id="conns-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2335. No active connections found matching your search.
  2336. </div>
  2337. </div>
  2338. <!-- ================= TAB 5: GEO-LOCATION ================= -->
  2339. <div id="fw-subtab-geo" class="hidden space-y-4">
  2340. <!-- Geo Header & Scheduler Status -->
  2341. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex flex-wrap items-center justify-between gap-4">
  2342. <div class="flex items-center space-x-3">
  2343. <div class="p-2.5 rounded-lg bg-emerald-500/10 border border-emerald-500/20 text-emerald-400">
  2344. <span class="text-xl">🌍</span>
  2345. </div>
  2346. <div>
  2347. <div class="flex items-center gap-2">
  2348. <h3 class="text-sm font-bold text-white">Geo-Location IP Filtering</h3>
  2349. <span id="geo-status-badge" class="px-2 py-0.5 rounded text-[10px] font-mono font-bold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30">Auto-Update ON</span>
  2350. </div>
  2351. <p class="text-xs text-slate-400">Aggregated country CIDR databases loaded into Linux kernel <code class="text-emerald-300">ipset</code>. Usable anywhere in Filter, NAT, and Mangle rules.</p>
  2352. </div>
  2353. </div>
  2354. <!-- Controls -->
  2355. <div class="flex items-center flex-wrap gap-2">
  2356. <div class="flex items-center space-x-1.5 bg-slate-950/60 border border-slate-800 px-3 py-1.5 rounded-lg text-xs font-mono">
  2357. <span class="text-slate-400">Schedule:</span>
  2358. <span id="geo-interval-label" class="font-bold text-white">Every 24h</span>
  2359. </div>
  2360. <button onclick="openGeoIntervalModal()" class="text-xs font-semibold px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700 transition">
  2361. ⚙️ Settings
  2362. </button>
  2363. <button id="geo-btn-update-now" onclick="triggerGeoUpdateNow()" class="text-xs font-semibold px-3.5 py-1.5 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20 transition flex items-center gap-1.5">
  2364. <span id="geo-update-spinner" class="hidden animate-spin text-xs">⏳</span>
  2365. <span>🔄 Update Now</span>
  2366. </button>
  2367. </div>
  2368. </div>
  2369. <!-- Stats Bar -->
  2370. <div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
  2371. <div class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3">
  2372. <span class="text-[10px] uppercase font-bold text-slate-500 block">Countries in Rules</span>
  2373. <span id="geo-stat-active-countries" class="text-base font-bold font-mono text-emerald-400">0</span>
  2374. </div>
  2375. <div class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3">
  2376. <span class="text-[10px] uppercase font-bold text-slate-500 block">Total Subnets in Kernel</span>
  2377. <span id="geo-stat-total-subnets" class="text-base font-bold font-mono text-cyan-400">0</span>
  2378. </div>
  2379. <div class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3">
  2380. <span class="text-[10px] uppercase font-bold text-slate-500 block">Last Synced</span>
  2381. <span id="geo-stat-last-sync" class="text-xs font-mono text-slate-300">-</span>
  2382. </div>
  2383. <div class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3">
  2384. <span class="text-[10px] uppercase font-bold text-slate-500 block">Next Scheduled Run</span>
  2385. <span id="geo-stat-next-sync" class="text-xs font-mono text-slate-300">-</span>
  2386. </div>
  2387. </div>
  2388. <!-- Filters & Country Table -->
  2389. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2390. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2391. <div class="flex items-center flex-wrap gap-2">
  2392. <input type="text" id="geo-search-input" oninput="renderGeoCountries()" placeholder="Search country name or code (e.g. Greece, GR, Germany)..." class="bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-emerald-500 w-64">
  2393. <select id="geo-continent-filter" onchange="renderGeoCountries()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1.5 text-xs text-white font-mono">
  2394. <option value="">All Continents</option>
  2395. <option value="Europe">Europe</option>
  2396. <option value="Asia">Asia</option>
  2397. <option value="Americas">Americas</option>
  2398. <option value="Africa">Africa</option>
  2399. <option value="Oceania">Oceania</option>
  2400. </select>
  2401. <label class="flex items-center space-x-1.5 text-xs text-slate-300 cursor-pointer ml-2">
  2402. <input type="checkbox" id="geo-filter-inuse-only" onchange="renderGeoCountries()" class="rounded bg-slate-800 border-slate-700 text-emerald-500">
  2403. <span>In Use by Rules only</span>
  2404. </label>
  2405. <label class="flex items-center space-x-1.5 text-xs text-slate-300 cursor-pointer ml-2">
  2406. <input type="checkbox" id="geo-filter-cached-only" onchange="renderGeoCountries()" class="rounded bg-slate-800 border-slate-700 text-cyan-500">
  2407. <span>Cached Only</span>
  2408. </label>
  2409. </div>
  2410. <div class="flex items-center space-x-2">
  2411. <span class="text-xs text-slate-400 font-sans">Quick Filter:</span>
  2412. <button onclick="quickFilterGeo('GR')" class="px-2.5 py-1 text-xs rounded bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700 font-mono">🇬🇷 GR</button>
  2413. <button onclick="quickFilterGeo('Europe')" class="px-2.5 py-1 text-xs rounded bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700 font-mono">🇪🇺 Europe</button>
  2414. <button onclick="quickFilterGeo('HIGHRISK')" class="px-2.5 py-1 text-xs rounded bg-rose-950/40 hover:bg-rose-900/50 text-rose-300 border border-rose-800/50 font-mono">🚫 High-Risk</button>
  2415. <button onclick="quickFilterGeo('')" class="px-2.5 py-1 text-xs rounded bg-slate-800 hover:bg-slate-700 text-slate-400 border border-slate-700 font-mono">Reset</button>
  2416. </div>
  2417. </div>
  2418. <div class="overflow-x-auto max-h-[600px] overflow-y-auto">
  2419. <table class="w-full text-left text-xs font-mono">
  2420. <thead class="text-slate-400 bg-slate-950/80 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans sticky top-0 z-10 backdrop-blur-sm">
  2421. <tr>
  2422. <th class="py-2.5 px-3 w-16 text-center">Code</th>
  2423. <th class="py-2.5 px-3">Country Name</th>
  2424. <th class="py-2.5 px-3">Continent</th>
  2425. <th class="py-2.5 px-3">Subnets</th>
  2426. <th class="py-2.5 px-3">Kernel IPSet</th>
  2427. <th class="py-2.5 px-3">Rule Usage</th>
  2428. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  2429. </tr>
  2430. </thead>
  2431. <tbody id="geo-table-body" class="divide-y divide-slate-800/60"></tbody>
  2432. </table>
  2433. </div>
  2434. <div id="geo-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2435. No countries found matching your search.
  2436. </div>
  2437. </div>
  2438. </div>
  2439. </section>
  2440. <!-- ================= SECTION: ROUTES & POLICY ================= -->
  2441. <section id="section-routes" class="hidden space-y-6">
  2442. <!-- Top Routing Switcher Bar -->
  2443. <div class="flex items-center gap-1.5 p-1 bg-slate-900/90 border border-slate-800 rounded-xl w-fit flex-wrap">
  2444. <button onclick="navigate('routes')" class="px-3.5 py-1.5 text-xs font-bold rounded-lg transition bg-cyan-600/20 text-cyan-300 border border-cyan-500/40 shadow-sm flex items-center gap-2">
  2445. <svg class="w-3.5 h-3.5 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2446. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
  2447. </svg>
  2448. <span>Routes & Policy</span>
  2449. </button>
  2450. <button onclick="navigate('bgp')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2451. <svg class="w-3.5 h-3.5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2452. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
  2453. </svg>
  2454. <span>BGP Routing</span>
  2455. </button>
  2456. <button onclick="navigate('ospf')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2457. <svg class="w-3.5 h-3.5 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2458. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
  2459. </svg>
  2460. <span>OSPF Routing</span>
  2461. </button>
  2462. <button onclick="navigate('rip')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2463. <svg class="w-3.5 h-3.5 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  2464. <span>RIP Routing</span>
  2465. </button>
  2466. <button onclick="navigate('bfd')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2467. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  2468. <span>BFD Sessions</span>
  2469. </button>
  2470. <button onclick="navigate('babel')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2471. <svg class="w-3.5 h-3.5 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /></svg>
  2472. <span>Babel Mesh</span>
  2473. </button>
  2474. <button onclick="navigate('radv')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2475. <svg class="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>
  2476. <span>IPv6 RAdv</span>
  2477. </button>
  2478. <button onclick="navigate('routing-filters')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2479. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2480. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
  2481. </svg>
  2482. <span>Policy Filters</span>
  2483. </button>
  2484. </div>
  2485. <div class="bg-gradient-to-r from-cyan-950/40 via-slate-900 to-slate-900 border border-cyan-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  2486. <div>
  2487. <div class="flex items-center gap-2 mb-1">
  2488. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-cyan-500/20 text-cyan-300 border border-cyan-500/30 tracking-wide uppercase">Routing Tables</span>
  2489. <h2 class="text-base font-bold text-white">IP Routes & Policy Rules</h2>
  2490. </div>
  2491. <p class="text-xs text-slate-400">Kernel FIB/RIB routing tables, static gateways, multipath uplinks, and policy routing (ip rule / fwmark lookup).</p>
  2492. </div>
  2493. <div class="flex items-center space-x-2.5">
  2494. <button onclick="openAddRouteModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20 transition">
  2495. + Add Route
  2496. </button>
  2497. <button onclick="openAddPolicyRuleModal()" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-white border border-slate-700 transition">
  2498. + Add Rule
  2499. </button>
  2500. <button onclick="openAddTableModal()" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition">
  2501. + New Table
  2502. </button>
  2503. </div>
  2504. </div>
  2505. <!-- Sub Tabs: Routes / Policy Rules / Tables -->
  2506. <div class="flex border-b border-slate-800 space-x-1">
  2507. <button id="routes-subtab-btn-routes" onclick="switchRoutesSubTab('routes')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-cyan-500 text-white transition flex items-center gap-2">
  2508. <span>Routes</span>
  2509. <span id="routes-badge-routes" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2510. </button>
  2511. <button id="routes-subtab-btn-rules" onclick="switchRoutesSubTab('rules')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2">
  2512. <span>Policy Routing Rules</span>
  2513. <span id="routes-badge-rules" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2514. </button>
  2515. <button id="routes-subtab-btn-tables" onclick="switchRoutesSubTab('tables')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2">
  2516. <span>Routing Tables</span>
  2517. <span id="routes-badge-tables" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2518. </button>
  2519. </div>
  2520. <!-- Sub-Tab 1: Routes Table -->
  2521. <div id="routes-subtab-routes" class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2522. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2523. <div class="flex items-center gap-3">
  2524. <h3 class="text-sm font-bold text-white">Active Routing Table (RIB)</h3>
  2525. <span id="routes-count-badge" class="text-xs font-mono text-slate-400">0 Routes</span>
  2526. </div>
  2527. <div class="flex items-center gap-2">
  2528. <label class="text-xs text-slate-400 font-sans">Table Filter:</label>
  2529. <select id="routes-filter-table" onchange="renderRoutesList()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white font-mono">
  2530. <option value="">All Tables</option>
  2531. <option value="main">main</option>
  2532. </select>
  2533. <label class="text-xs text-slate-400 font-sans ml-2">Type Filter:</label>
  2534. <select id="routes-filter-type" onchange="renderRoutesList()" class="bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-xs text-white font-mono">
  2535. <option value="">All Types</option>
  2536. <option value="ecmp">⚡ ECMP (Multi-Path)</option>
  2537. <option value="bgp">BGP</option>
  2538. <option value="ospf">OSPF</option>
  2539. <option value="static">Static</option>
  2540. <option value="dynamic">Dynamic</option>
  2541. <option value="connected">Connected</option>
  2542. <option value="dhcp">DHCP</option>
  2543. <option value="multicast">Multicast / IGMP</option>
  2544. </select>
  2545. <button onclick="loadRoutes()" class="text-xs text-cyan-400 hover:text-cyan-300 font-semibold px-2 py-1 bg-slate-800 rounded border border-slate-700">Refresh</button>
  2546. </div>
  2547. </div>
  2548. <div class="overflow-x-auto">
  2549. <table class="w-full text-left text-xs font-mono">
  2550. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2551. <tr>
  2552. <th class="py-2.5 px-3 w-16 text-center" title="Flags: D=Dynamic, A=Active, C=Connect, S=Static, X=Disabled">Flags</th>
  2553. <th class="py-2.5 px-3">Dst. Address</th>
  2554. <th class="py-2.5 px-3">Protocol</th>
  2555. <th class="py-2.5 px-3">Gateway</th>
  2556. <th class="py-2.5 px-3">Distance</th>
  2557. <th class="py-2.5 px-3">Table / Mark</th>
  2558. <th class="py-2.5 px-3">Interface</th>
  2559. <th class="py-2.5 px-3">Internet / Gateway Health</th>
  2560. <th class="py-2.5 px-3">Comment</th>
  2561. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  2562. </tr>
  2563. </thead>
  2564. <tbody id="routes-table-body" class="divide-y divide-slate-800/80"></tbody>
  2565. </table>
  2566. </div>
  2567. <div id="routes-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2568. No routes found.
  2569. </div>
  2570. </div>
  2571. <!-- Sub-Tab 2: Policy Rules -->
  2572. <div id="routes-subtab-rules" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2573. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2574. <div class="flex items-center gap-3">
  2575. <h3 class="text-sm font-bold text-white">Policy Routing Rules (ip rule)</h3>
  2576. <span id="rules-count-badge" class="text-xs font-mono text-slate-400">0 Rules</span>
  2577. </div>
  2578. <div class="flex items-center gap-2">
  2579. <label class="flex items-center gap-1.5 text-xs text-slate-400 hover:text-white cursor-pointer select-none bg-slate-800/80 hover:bg-slate-800 border border-slate-700/80 px-2.5 py-1 rounded-lg transition">
  2580. <input type="checkbox" id="prule-show-system" onchange="renderPolicyRules()" class="rounded bg-slate-900 border-slate-700 text-cyan-500 focus:ring-0 w-3.5 h-3.5">
  2581. <span>Show System / Default Rules</span>
  2582. </label>
  2583. <button onclick="loadRoutes()" class="text-xs text-cyan-400 hover:text-cyan-300 font-semibold px-2 py-1 bg-slate-800 rounded border border-slate-700">Refresh</button>
  2584. </div>
  2585. </div>
  2586. <div class="overflow-x-auto">
  2587. <table class="w-full text-left text-xs font-mono">
  2588. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2589. <tr>
  2590. <th class="py-2.5 px-3 w-16">Pref</th>
  2591. <th class="py-2.5 px-3">Src. Address</th>
  2592. <th class="py-2.5 px-3">Dst. Address</th>
  2593. <th class="py-2.5 px-3">Routing Mark (fwmark)</th>
  2594. <th class="py-2.5 px-3">Lookup Table</th>
  2595. <th class="py-2.5 px-3">Action</th>
  2596. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  2597. </tr>
  2598. </thead>
  2599. <tbody id="rules-table-body" class="divide-y divide-slate-800/80"></tbody>
  2600. </table>
  2601. </div>
  2602. <div id="rules-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2603. No policy rules configured. Click "+ Add Rule" to route traffic based on routing-mark or source IP.
  2604. </div>
  2605. </div>
  2606. <!-- Sub-Tab 3: Routing Tables -->
  2607. <div id="routes-subtab-tables" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2608. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  2609. <h3 class="text-sm font-bold text-white">Defined Routing Tables</h3>
  2610. <span id="tables-count-badge" class="text-xs font-mono text-slate-400">0 Tables</span>
  2611. </div>
  2612. <div class="overflow-x-auto">
  2613. <table class="w-full text-left text-xs font-mono">
  2614. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2615. <tr>
  2616. <th class="py-2.5 px-3 w-20">FIB ID</th>
  2617. <th class="py-2.5 px-3">Table Name</th>
  2618. <th class="py-2.5 px-3">Comment / Purpose</th>
  2619. </tr>
  2620. </thead>
  2621. <tbody id="tables-table-body" class="divide-y divide-slate-800/80"></tbody>
  2622. </table>
  2623. </div>
  2624. </div>
  2625. </section>
  2626. <!-- ================= SECTION: SD-WAN ================= -->
  2627. <section id="section-sdwan" class="hidden space-y-6">
  2628. <!-- Header and Stats Banner -->
  2629. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 shadow-lg flex flex-wrap items-center justify-between gap-4">
  2630. <div>
  2631. <h3 class="text-base font-bold text-white flex items-center gap-2">
  2632. <svg class="w-5 h-5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2633. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
  2634. </svg>
  2635. <span>Software-Defined WAN (SD-WAN)</span>
  2636. </h3>
  2637. <p class="text-xs text-slate-400 mt-1">Multi-link aggregation, percentage balancing, SLA quality probes, and unified firewall zone interfaces.</p>
  2638. </div>
  2639. <div class="flex items-center gap-2 flex-wrap">
  2640. <button onclick="loadSDWAN(true)" class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700 text-xs font-semibold flex items-center gap-1.5 transition">
  2641. <svg class="w-3.5 h-3.5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /></svg>
  2642. <span>Refresh</span>
  2643. </button>
  2644. <button onclick="openAddSDWANMemberModal()" class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-amber-300 border border-amber-500/30 text-xs font-semibold flex items-center gap-1.5 transition">
  2645. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" /></svg>
  2646. <span>Add Member Link</span>
  2647. </button>
  2648. <button onclick="openAddSDWANGroupModal()" class="px-3.5 py-1.5 rounded-xl bg-amber-600 hover:bg-amber-500 text-white text-xs font-semibold flex items-center gap-1.5 shadow-lg shadow-amber-600/20 transition">
  2649. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" /></svg>
  2650. <span>Create SD-WAN Group</span>
  2651. </button>
  2652. </div>
  2653. </div>
  2654. <!-- Quick Metrics Cards -->
  2655. <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
  2656. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-sm">
  2657. <div class="text-[11px] font-mono text-slate-400 uppercase tracking-wider">SD-WAN Groups</div>
  2658. <div class="text-2xl font-bold text-amber-400 mt-1" id="sdwan-metric-groups">0</div>
  2659. <div class="text-[10px] text-slate-500 mt-1">Configured multi-link zones</div>
  2660. </div>
  2661. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-sm">
  2662. <div class="text-[11px] font-mono text-slate-400 uppercase tracking-wider">Healthy Links</div>
  2663. <div class="text-2xl font-bold text-emerald-400 mt-1 flex items-center gap-2">
  2664. <span class="w-2.5 h-2.5 rounded-full bg-emerald-500 animate-pulse"></span>
  2665. <span id="sdwan-metric-healthy">0</span>
  2666. </div>
  2667. <div class="text-[10px] text-slate-500 mt-1">Passing SLA latency & loss checks</div>
  2668. </div>
  2669. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-sm">
  2670. <div class="text-[11px] font-mono text-slate-400 uppercase tracking-wider">Degraded Links</div>
  2671. <div class="text-2xl font-bold text-amber-400 mt-1" id="sdwan-metric-degraded">0</div>
  2672. <div class="text-[10px] text-slate-500 mt-1">Exceeding SLA latency/loss thresholds</div>
  2673. </div>
  2674. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-sm">
  2675. <div class="text-[11px] font-mono text-slate-400 uppercase tracking-wider">Down Links</div>
  2676. <div class="text-2xl font-bold text-rose-400 font-mono mt-1" id="sdwan-metric-down">0</div>
  2677. <div class="text-[10px] text-slate-500 mt-1">Offline or probe unreachable</div>
  2678. </div>
  2679. </div>
  2680. <!-- SD-WAN Groups Table -->
  2681. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
  2682. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  2683. <h4 class="text-xs font-bold text-slate-300 uppercase tracking-wider font-mono">SD-WAN Groups & Zones</h4>
  2684. <span class="text-[11px] text-slate-500">Virtual aggregation interfaces usable directly in Firewall rules</span>
  2685. </div>
  2686. <div class="overflow-x-auto">
  2687. <table class="w-full text-left border-collapse text-xs">
  2688. <thead>
  2689. <tr class="border-b border-slate-800/80 bg-slate-950/40 text-[11px] font-mono uppercase text-slate-400">
  2690. <th class="py-2.5 px-4">State</th>
  2691. <th class="py-2.5 px-4">Group Name</th>
  2692. <th class="py-2.5 px-4">Balancing Mode</th>
  2693. <th class="py-2.5 px-4">Load Distribution</th>
  2694. <th class="py-2.5 px-4">Members</th>
  2695. <th class="py-2.5 px-4">Probe Interval</th>
  2696. <th class="py-2.5 px-4">Auto-NAT</th>
  2697. <th class="py-2.5 px-4 text-right">Actions</th>
  2698. </tr>
  2699. </thead>
  2700. <tbody id="sdwan-groups-table-body" class="divide-y divide-slate-800/60 font-mono">
  2701. <tr><td colspan="8" class="py-8 text-center text-slate-500 font-sans">Loading SD-WAN groups...</td></tr>
  2702. </tbody>
  2703. </table>
  2704. </div>
  2705. </div>
  2706. <!-- SD-WAN Member Interfaces Table -->
  2707. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
  2708. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  2709. <h4 class="text-xs font-bold text-slate-300 uppercase tracking-wider font-mono">Member WAN Links & Live SLA Telemetry</h4>
  2710. <span class="text-[11px] text-slate-500">Active latency, packet loss, jitter and routing tables</span>
  2711. </div>
  2712. <div class="overflow-x-auto">
  2713. <table class="w-full text-left border-collapse text-xs">
  2714. <thead>
  2715. <tr class="border-b border-slate-800/80 bg-slate-950/40 text-[11px] font-mono uppercase text-slate-400">
  2716. <th class="py-2.5 px-4">State</th>
  2717. <th class="py-2.5 px-4">Group</th>
  2718. <th class="py-2.5 px-4">Interface</th>
  2719. <th class="py-2.5 px-4">Gateway</th>
  2720. <th class="py-2.5 px-4">Weight / Prio</th>
  2721. <th class="py-2.5 px-4">Health Status</th>
  2722. <th class="py-2.5 px-4">Target Probe</th>
  2723. <th class="py-2.5 px-4">Live Latency</th>
  2724. <th class="py-2.5 px-4">Packet Loss</th>
  2725. <th class="py-2.5 px-4">Jitter</th>
  2726. <th class="py-2.5 px-4 text-right">Actions</th>
  2727. </tr>
  2728. </thead>
  2729. <tbody id="sdwan-members-table-body" class="divide-y divide-slate-800/60 font-mono">
  2730. <tr><td colspan="11" class="py-8 text-center text-slate-500 font-sans">No member links configured yet.</td></tr>
  2731. </tbody>
  2732. </table>
  2733. </div>
  2734. </div>
  2735. </section>
  2736. <!-- ================= SECTION: BGP ROUTING ================= -->
  2737. <section id="section-bgp" class="hidden space-y-6">
  2738. <!-- Top Routing Switcher Bar -->
  2739. <div class="flex items-center gap-1.5 p-1 bg-slate-900/90 border border-slate-800 rounded-xl w-fit flex-wrap">
  2740. <button onclick="navigate('routes')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2741. <svg class="w-3.5 h-3.5 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2742. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
  2743. </svg>
  2744. <span>Routes & Policy</span>
  2745. </button>
  2746. <button onclick="navigate('bgp')" class="px-3.5 py-1.5 text-xs font-bold rounded-lg transition bg-purple-600/20 text-purple-300 border border-purple-500/40 shadow-sm flex items-center gap-2">
  2747. <svg class="w-3.5 h-3.5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2748. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
  2749. </svg>
  2750. <span>BGP Routing</span>
  2751. </button>
  2752. <button onclick="navigate('ospf')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2753. <svg class="w-3.5 h-3.5 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2754. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
  2755. </svg>
  2756. <span>OSPF Routing</span>
  2757. </button>
  2758. <button onclick="navigate('rip')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2759. <svg class="w-3.5 h-3.5 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  2760. <span>RIP Routing</span>
  2761. </button>
  2762. <button onclick="navigate('bfd')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2763. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  2764. <span>BFD Sessions</span>
  2765. </button>
  2766. <button onclick="navigate('babel')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2767. <svg class="w-3.5 h-3.5 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /></svg>
  2768. <span>Babel Mesh</span>
  2769. </button>
  2770. <button onclick="navigate('radv')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2771. <svg class="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>
  2772. <span>IPv6 RAdv</span>
  2773. </button>
  2774. <button onclick="navigate('routing-filters')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2775. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2776. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
  2777. </svg>
  2778. <span>Policy Filters</span>
  2779. </button>
  2780. </div>
  2781. <div class="bg-gradient-to-r from-purple-950/40 via-slate-900 to-slate-900 border border-purple-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  2782. <div>
  2783. <div class="flex items-center gap-2 mb-1">
  2784. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-purple-500/20 text-purple-300 border border-purple-500/30 tracking-wide uppercase">BGP Routing</span>
  2785. <h2 class="text-base font-bold text-white">BGP Dynamic Routing</h2>
  2786. <span class="bgp-live-status-pill text-[10px] px-2 py-0.5 rounded font-mono bg-slate-800 text-slate-400 border border-slate-700">Checking...</span>
  2787. </div>
  2788. <p class="text-xs text-slate-400">High-Performance BIRD 2 BGP Engine: Multi-Instance / Multi-AS peering sessions, programmable route filters, prefix announcements, and BGP routing table (RIB).</p>
  2789. </div>
  2790. <div class="flex items-center space-x-2 flex-wrap gap-y-2">
  2791. <button onclick="openBgpInstanceModal()" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-purple-300 border border-purple-900/50 transition flex items-center gap-1.5">
  2792. <span>⚙ BGP Settings</span>
  2793. </button>
  2794. <button onclick="openAddBgpPeerModal()" class="text-xs font-semibold px-3 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 text-white shadow-lg shadow-purple-500/20 transition">
  2795. + Add BGP Peer
  2796. </button>
  2797. <button onclick="openAddBgpNetworkModal()" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition">
  2798. + Add Network
  2799. </button>
  2800. <button onclick="loadDynamicRouting()" class="text-xs text-slate-400 hover:text-white px-2.5 py-2 bg-slate-800 rounded-lg border border-slate-700 transition">
  2801. Refresh
  2802. </button>
  2803. </div>
  2804. </div>
  2805. <!-- BGP Sub-Tabs -->
  2806. <div class="flex border-b border-slate-800 space-x-1 overflow-x-auto">
  2807. <button id="bgp-subtab-btn-bgp-peers" onclick="switchBgpSubTab('bgp-peers')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-purple-500 text-white transition flex items-center gap-2 whitespace-nowrap">
  2808. <span>BGP Peer Connections</span>
  2809. <span id="dyn-badge-bgp-peers" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2810. </button>
  2811. <button id="bgp-subtab-btn-bgp-nets" onclick="switchBgpSubTab('bgp-nets')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  2812. <span>Advertised Networks</span>
  2813. <span id="dyn-badge-bgp-nets" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2814. </button>
  2815. <button id="bgp-subtab-btn-bgp-routes" onclick="switchBgpSubTab('bgp-routes')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  2816. <span>BGP Routes (RIB)</span>
  2817. <span id="dyn-badge-bgp-routes" class="px-1.5 py-0.2 rounded text-[10px] bg-slate-800 text-slate-300">0</span>
  2818. </button>
  2819. </div>
  2820. <!-- Sub-Tab 1: BGP Peers -->
  2821. <div id="dyn-subtab-bgp-peers" class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2822. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
  2823. <div class="flex items-center gap-3">
  2824. <h3 class="text-sm font-bold text-white">Configured BGP Peers</h3>
  2825. <span id="bgp-peers-count-badge" class="text-xs font-mono text-slate-400">0 Peers</span>
  2826. <span class="bgp-live-status-pill text-[10px] px-2 py-0.5 rounded font-mono bg-slate-800 text-slate-400 border border-slate-700">Checking...</span>
  2827. </div>
  2828. <div class="flex items-center gap-2">
  2829. <button onclick="loadDynamicRouting()" class="text-xs text-purple-400 hover:text-purple-300 font-semibold px-2.5 py-1 bg-slate-800 rounded border border-slate-700">Refresh</button>
  2830. </div>
  2831. </div>
  2832. <div class="overflow-x-auto">
  2833. <table class="w-full text-left text-xs font-mono">
  2834. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2835. <tr>
  2836. <th class="py-2.5 px-3 w-16 text-center">Flags</th>
  2837. <th class="py-2.5 px-3">Name</th>
  2838. <th class="py-2.5 px-3">Remote Address</th>
  2839. <th class="py-2.5 px-3">Remote AS</th>
  2840. <th class="py-2.5 px-3">State</th>
  2841. <th class="py-2.5 px-3">Uptime</th>
  2842. <th class="py-2.5 px-3">Prefixes (RX/TX)</th>
  2843. <th class="py-2.5 px-3">Routing Filters</th>
  2844. <th class="py-2.5 px-3">Comment</th>
  2845. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  2846. </tr>
  2847. </thead>
  2848. <tbody id="bgp-peers-table-body" class="divide-y divide-slate-800/80"></tbody>
  2849. </table>
  2850. </div>
  2851. <div id="bgp-peers-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2852. No BGP peers configured. Click "+ Add BGP Peer" to establish an eBGP or iBGP peering session.
  2853. </div>
  2854. </div>
  2855. <!-- Sub-Tab 2: BGP Networks -->
  2856. <div id="dyn-subtab-bgp-nets" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2857. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  2858. <div class="flex items-center gap-3">
  2859. <h3 class="text-sm font-bold text-white">Advertised BGP Networks</h3>
  2860. <span id="bgp-nets-count-badge" class="text-xs font-mono text-slate-400">0 Networks</span>
  2861. </div>
  2862. <button onclick="openAddBgpNetworkModal()" class="text-xs font-semibold px-3 py-1.5 rounded-lg bg-purple-600 hover:bg-purple-500 text-white transition">
  2863. + Add Network
  2864. </button>
  2865. </div>
  2866. <div class="overflow-x-auto">
  2867. <table class="w-full text-left text-xs font-mono">
  2868. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2869. <tr>
  2870. <th class="py-2.5 px-3">Network (CIDR)</th>
  2871. <th class="py-2.5 px-3">Status</th>
  2872. <th class="py-2.5 px-3">Discard Route</th>
  2873. <th class="py-2.5 px-3">Comment</th>
  2874. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  2875. </tr>
  2876. </thead>
  2877. <tbody id="bgp-nets-table-body" class="divide-y divide-slate-800/80"></tbody>
  2878. </table>
  2879. </div>
  2880. <div id="bgp-nets-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2881. No networks advertised. Click "+ Add Network" to announce your IP prefixes to BGP peers.
  2882. </div>
  2883. </div>
  2884. <!-- Sub-Tab 3: BGP Routes -->
  2885. <div id="dyn-subtab-bgp-routes" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  2886. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  2887. <h3 class="text-sm font-bold text-white">BGP Routing Information Base (show ip bgp)</h3>
  2888. <span id="bgp-routes-count-badge" class="text-xs font-mono text-slate-400">0 Routes</span>
  2889. </div>
  2890. <div class="overflow-x-auto">
  2891. <table class="w-full text-left text-xs font-mono">
  2892. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  2893. <tr>
  2894. <th class="py-2.5 px-3">Prefix</th>
  2895. <th class="py-2.5 px-3">Next Hop</th>
  2896. <th class="py-2.5 px-3">Metric</th>
  2897. <th class="py-2.5 px-3">Weight</th>
  2898. <th class="py-2.5 px-3">AS Path</th>
  2899. <th class="py-2.5 px-3">Origin</th>
  2900. </tr>
  2901. </thead>
  2902. <tbody id="bgp-routes-table-body" class="divide-y divide-slate-800/80"></tbody>
  2903. </table>
  2904. </div>
  2905. <div id="bgp-routes-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  2906. No active routes in BGP table. Check peer connections and ensure BGP is enabled.
  2907. </div>
  2908. </div>
  2909. </section>
  2910. <!-- ================= SECTION: OSPF ROUTING ================= -->
  2911. <section id="section-ospf" class="hidden space-y-6">
  2912. <!-- Top Routing Switcher Bar -->
  2913. <div class="flex items-center gap-1.5 p-1 bg-slate-900/90 border border-slate-800 rounded-xl w-fit flex-wrap">
  2914. <button onclick="navigate('routes')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2915. <svg class="w-3.5 h-3.5 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2916. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
  2917. </svg>
  2918. <span>Routes & Policy</span>
  2919. </button>
  2920. <button onclick="navigate('bgp')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2921. <svg class="w-3.5 h-3.5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2922. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
  2923. </svg>
  2924. <span>BGP Routing</span>
  2925. </button>
  2926. <button onclick="navigate('ospf')" class="px-3.5 py-1.5 text-xs font-bold rounded-lg transition bg-indigo-600/20 text-indigo-300 border border-indigo-500/40 shadow-sm flex items-center gap-2">
  2927. <svg class="w-3.5 h-3.5 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2928. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
  2929. </svg>
  2930. <span>OSPF Routing</span>
  2931. </button>
  2932. <button onclick="navigate('rip')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2933. <svg class="w-3.5 h-3.5 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  2934. <span>RIP Routing</span>
  2935. </button>
  2936. <button onclick="navigate('bfd')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2937. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  2938. <span>BFD Sessions</span>
  2939. </button>
  2940. <button onclick="navigate('babel')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2941. <svg class="w-3.5 h-3.5 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /></svg>
  2942. <span>Babel Mesh</span>
  2943. </button>
  2944. <button onclick="navigate('radv')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2945. <svg class="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>
  2946. <span>IPv6 RAdv</span>
  2947. </button>
  2948. <button onclick="navigate('routing-filters')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  2949. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  2950. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
  2951. </svg>
  2952. <span>Policy Filters</span>
  2953. </button>
  2954. </div>
  2955. <!-- OSPF Window Container -->
  2956. <div class="bg-[#131b26] border border-[#243347] rounded-md shadow-2xl overflow-hidden text-xs flex flex-col min-h-[560px]">
  2957. <!-- Window Tab Bar Header -->
  2958. <div class="flex items-center justify-between border-b border-[#243347] bg-[#101721] px-2 pt-1 text-xs select-none">
  2959. <div class="flex items-center space-x-1 overflow-x-auto">
  2960. <div class="flex items-center gap-1.5 px-3 py-2 text-sky-400 font-bold border-r border-[#243347] mr-1">
  2961. <span>🔀</span>
  2962. <span>OSPF</span>
  2963. <span class="text-[10px]">▼</span>
  2964. </div>
  2965. <button id="ospf-tab-btn-instances" onclick="switchOspfTab('instances')" class="px-3 py-2 text-xs font-semibold border-b-2 border-sky-500 text-white transition flex items-center gap-1.5 whitespace-nowrap">
  2966. <span>Instances</span>
  2967. </button>
  2968. <button id="ospf-tab-btn-areas" onclick="switchOspfTab('areas')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  2969. <span>Areas</span>
  2970. </button>
  2971. <button id="ospf-tab-btn-area-ranges" onclick="switchOspfTab('area-ranges')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  2972. <span>Area Ranges</span>
  2973. </button>
  2974. <button id="ospf-tab-btn-templates" onclick="switchOspfTab('templates')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  2975. <span>Interface Templates</span>
  2976. </button>
  2977. <button id="ospf-tab-btn-interfaces" onclick="switchOspfTab('interfaces')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  2978. <span>Interfaces</span>
  2979. </button>
  2980. <button id="ospf-tab-btn-static-nbrs" onclick="switchOspfTab('static-nbrs')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  2981. <span>Static Neighbors</span>
  2982. </button>
  2983. <button id="ospf-tab-btn-neighbors" onclick="switchOspfTab('neighbors')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  2984. <span>Neighbors</span>
  2985. </button>
  2986. <button id="ospf-tab-btn-lsa" onclick="switchOspfTab('lsa')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  2987. <span>LSA</span>
  2988. </button>
  2989. </div>
  2990. <div class="flex items-center gap-1 text-slate-400 pr-2">
  2991. <button onclick="loadOspfAll()" title="Refresh" class="hover:text-white p-1">🔄</button>
  2992. <span class="text-slate-600">|</span>
  2993. <button title="Fullscreen" class="hover:text-white p-1">⛶</button>
  2994. <button title="Close" class="hover:text-white p-1">✕</button>
  2995. </div>
  2996. </div>
  2997. <!-- ================= TAB 1: INSTANCES ================= -->
  2998. <div id="ospf-pane-instances" class="flex-1 flex flex-col">
  2999. <!-- Toolbar -->
  3000. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3001. <div class="flex items-center space-x-2">
  3002. <button onclick="openOspfInstanceModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-sky-600 hover:text-white text-sky-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3003. <span>+</span> <span>New</span>
  3004. </button>
  3005. <button onclick="enableSelectedOspfInstance()" id="btn-ospf-inst-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3006. <span>▶</span> <span>Enable</span>
  3007. </button>
  3008. <button onclick="disableSelectedOspfInstance()" id="btn-ospf-inst-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3009. <span>⏸</span> <span>Disable</span>
  3010. </button>
  3011. <button onclick="removeSelectedOspfInstance()" id="btn-ospf-inst-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3012. <span>✕</span> <span>Remove</span>
  3013. </button>
  3014. <button onclick="promptCommentOspfInstance()" id="btn-ospf-inst-comment" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3015. <span>💬</span> <span>Comment</span>
  3016. </button>
  3017. </div>
  3018. <div class="flex items-center space-x-2">
  3019. <div class="relative">
  3020. <input type="text" id="filter-ospf-instances" oninput="renderOspfInstances()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-sky-500 w-32">
  3021. </div>
  3022. <button class="px-2 py-1 bg-[#15202e] hover:bg-[#1a2737] text-slate-300 border border-[#233346] rounded flex items-center gap-1">
  3023. <span>▼</span> <span>Filter</span>
  3024. </button>
  3025. </div>
  3026. </div>
  3027. <!-- Table -->
  3028. <div class="flex-1 overflow-x-auto">
  3029. <table class="w-full text-left text-xs font-mono">
  3030. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3031. <tr>
  3032. <th class="py-2 px-3 w-8 text-center cursor-pointer" onclick="sortOspfTable('instances', 'id')">#</th>
  3033. <th class="py-2 px-3 w-12 text-center cursor-pointer" onclick="sortOspfTable('instances', 'flags')">Flags</th>
  3034. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('instances', 'comment')">Comment</th>
  3035. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('instances', 'name')">Name <span id="sort-ospf-instances-name">▲</span></th>
  3036. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('instances', 'version')">Version</th>
  3037. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('instances', 'vrf')">VRF</th>
  3038. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('instances', 'router_id')">Router ID</th>
  3039. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('instances', 'redistribute')">Redistribute</th>
  3040. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('instances', 'originate_default')">Originate Default</th>
  3041. </tr>
  3042. </thead>
  3043. <tbody id="ospf-instances-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3044. </table>
  3045. </div>
  3046. <!-- Footer -->
  3047. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3048. <span id="ospf-instances-footer-count">0</span>
  3049. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3050. </div>
  3051. </div>
  3052. <!-- ================= TAB 2: AREAS ================= -->
  3053. <div id="ospf-pane-areas" class="hidden flex-1 flex flex-col">
  3054. <!-- Toolbar -->
  3055. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3056. <div class="flex items-center space-x-2">
  3057. <button onclick="openOspfAreaModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-sky-600 hover:text-white text-sky-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3058. <span>+</span> <span>New</span>
  3059. </button>
  3060. <button onclick="enableSelectedOspfArea()" id="btn-ospf-area-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3061. <span>▶</span> <span>Enable</span>
  3062. </button>
  3063. <button onclick="disableSelectedOspfArea()" id="btn-ospf-area-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3064. <span>⏸</span> <span>Disable</span>
  3065. </button>
  3066. <button onclick="removeSelectedOspfArea()" id="btn-ospf-area-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3067. <span>✕</span> <span>Remove</span>
  3068. </button>
  3069. <button onclick="promptCommentOspfArea()" id="btn-ospf-area-comment" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3070. <span>💬</span> <span>Comment</span>
  3071. </button>
  3072. </div>
  3073. <div class="flex items-center space-x-2">
  3074. <div class="relative">
  3075. <input type="text" id="filter-ospf-areas" oninput="renderOspfAreas()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-sky-500 w-32">
  3076. </div>
  3077. <button class="px-2 py-1 bg-[#15202e] hover:bg-[#1a2737] text-slate-300 border border-[#233346] rounded flex items-center gap-1">
  3078. <span>▼</span> <span>Filter</span>
  3079. </button>
  3080. </div>
  3081. </div>
  3082. <!-- Table -->
  3083. <div class="flex-1 overflow-x-auto">
  3084. <table class="w-full text-left text-xs font-mono">
  3085. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3086. <tr>
  3087. <th class="py-2 px-3 w-8 text-center cursor-pointer" onclick="sortOspfTable('areas', 'id')">#</th>
  3088. <th class="py-2 px-3 w-12 text-center cursor-pointer" onclick="sortOspfTable('areas', 'flags')">Flags</th>
  3089. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('areas', 'comment')">Comment</th>
  3090. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('areas', 'name')">Name <span id="sort-ospf-areas-name">▲</span></th>
  3091. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('areas', 'instance')">Instance</th>
  3092. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('areas', 'area_id')">Area ID</th>
  3093. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('areas', 'area_type')">Type</th>
  3094. </tr>
  3095. </thead>
  3096. <tbody id="ospf-areas-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3097. </table>
  3098. </div>
  3099. <!-- Footer -->
  3100. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3101. <span id="ospf-areas-footer-count">0</span>
  3102. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3103. </div>
  3104. </div>
  3105. <!-- ================= TAB 3: AREA RANGES ================= -->
  3106. <div id="ospf-pane-area-ranges" class="hidden flex-1 flex flex-col">
  3107. <!-- Toolbar -->
  3108. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3109. <div class="flex items-center space-x-2">
  3110. <button onclick="openOspfAreaRangeModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-sky-600 hover:text-white text-sky-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3111. <span>+</span> <span>New</span>
  3112. </button>
  3113. <button onclick="enableSelectedOspfAreaRange()" id="btn-ospf-range-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3114. <span>▶</span> <span>Enable</span>
  3115. </button>
  3116. <button onclick="disableSelectedOspfAreaRange()" id="btn-ospf-range-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3117. <span>⏸</span> <span>Disable</span>
  3118. </button>
  3119. <button onclick="removeSelectedOspfAreaRange()" id="btn-ospf-range-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3120. <span>✕</span> <span>Remove</span>
  3121. </button>
  3122. <button onclick="promptCommentOspfAreaRange()" id="btn-ospf-range-comment" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3123. <span>💬</span> <span>Comment</span>
  3124. </button>
  3125. </div>
  3126. <div class="flex items-center space-x-2">
  3127. <div class="relative">
  3128. <input type="text" id="filter-ospf-area-ranges" oninput="renderOspfAreaRanges()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-sky-500 w-32">
  3129. </div>
  3130. <button class="px-2 py-1 bg-[#15202e] hover:bg-[#1a2737] text-slate-300 border border-[#233346] rounded flex items-center gap-1">
  3131. <span>▼</span> <span>Filter</span>
  3132. </button>
  3133. </div>
  3134. </div>
  3135. <!-- Table -->
  3136. <div class="flex-1 overflow-x-auto">
  3137. <table class="w-full text-left text-xs font-mono">
  3138. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3139. <tr>
  3140. <th class="py-2 px-3 w-8 text-center cursor-pointer" onclick="sortOspfTable('ranges', 'id')">#</th>
  3141. <th class="py-2 px-3 w-12 text-center cursor-pointer" onclick="sortOspfTable('ranges', 'flags')">Flags</th>
  3142. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('ranges', 'comment')">Comment</th>
  3143. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('ranges', 'area')">Area <span id="sort-ospf-ranges-area">▲</span></th>
  3144. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('ranges', 'prefix')">Prefix</th>
  3145. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('ranges', 'cost')">Cost</th>
  3146. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('ranges', 'advertise')">Advertise</th>
  3147. </tr>
  3148. </thead>
  3149. <tbody id="ospf-ranges-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3150. </table>
  3151. </div>
  3152. <!-- Footer -->
  3153. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3154. <span id="ospf-ranges-footer-count">0</span>
  3155. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3156. </div>
  3157. </div>
  3158. <!-- ================= TAB 4: INTERFACE TEMPLATES ================= -->
  3159. <div id="ospf-pane-templates" class="hidden flex-1 flex flex-col">
  3160. <!-- Toolbar -->
  3161. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3162. <div class="flex items-center space-x-2">
  3163. <button onclick="openOspfTemplateModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-sky-600 hover:text-white text-sky-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3164. <span>+</span> <span>New</span>
  3165. </button>
  3166. <button onclick="enableSelectedOspfTemplate()" id="btn-ospf-tmpl-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3167. <span>▶</span> <span>Enable</span>
  3168. </button>
  3169. <button onclick="disableSelectedOspfTemplate()" id="btn-ospf-tmpl-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3170. <span>⏸</span> <span>Disable</span>
  3171. </button>
  3172. <button onclick="removeSelectedOspfTemplate()" id="btn-ospf-tmpl-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3173. <span>✕</span> <span>Remove</span>
  3174. </button>
  3175. <button onclick="promptCommentOspfTemplate()" id="btn-ospf-tmpl-comment" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3176. <span>💬</span> <span>Comment</span>
  3177. </button>
  3178. </div>
  3179. <div class="flex items-center space-x-2">
  3180. <div class="relative">
  3181. <input type="text" id="filter-ospf-templates" oninput="renderOspfTemplates()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-sky-500 w-32">
  3182. </div>
  3183. <button class="px-2 py-1 bg-[#15202e] hover:bg-[#1a2737] text-slate-300 border border-[#233346] rounded flex items-center gap-1">
  3184. <span>▼</span> <span>Filter</span>
  3185. </button>
  3186. </div>
  3187. </div>
  3188. <!-- Table -->
  3189. <div class="flex-1 overflow-x-auto">
  3190. <table class="w-full text-left text-xs font-mono">
  3191. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3192. <tr>
  3193. <th class="py-2 px-3 w-8 text-center cursor-pointer" onclick="sortOspfTable('templates', 'index')">#</th>
  3194. <th class="py-2 px-3 w-12 text-center cursor-pointer" onclick="sortOspfTable('templates', 'flags')">Flags</th>
  3195. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('templates', 'comment')">Comment</th>
  3196. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('templates', 'interfaces')">Interfaces <span id="sort-ospf-templates-interfaces">▲</span></th>
  3197. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('templates', 'area')">Area</th>
  3198. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('templates', 'networks')">Networks</th>
  3199. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('templates', 'network_type')">Network Type</th>
  3200. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('templates', 'cost')">Cost</th>
  3201. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('templates', 'priority')">Priority</th>
  3202. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('templates', 'auth')">Auth</th>
  3203. </tr>
  3204. </thead>
  3205. <tbody id="ospf-templates-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3206. </table>
  3207. </div>
  3208. <!-- Footer -->
  3209. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3210. <span id="ospf-templates-footer-count">0</span>
  3211. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3212. </div>
  3213. </div>
  3214. <!-- ================= TAB 5: INTERFACES (LIVE DYNAMIC) ================= -->
  3215. <div id="ospf-pane-interfaces" class="hidden flex-1 flex flex-col">
  3216. <!-- Toolbar -->
  3217. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3218. <div class="text-slate-400 italic font-sans text-[11px]">Dynamic live kernel interfaces registered in OSPF daemon</div>
  3219. <div class="flex items-center space-x-2">
  3220. <div class="relative">
  3221. <input type="text" id="filter-ospf-interfaces" oninput="renderOspfLiveInterfaces()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-sky-500 w-32">
  3222. </div>
  3223. <button class="px-2 py-1 bg-[#15202e] hover:bg-[#1a2737] text-slate-300 border border-[#233346] rounded flex items-center gap-1">
  3224. <span>▼</span> <span>Filter</span>
  3225. </button>
  3226. </div>
  3227. </div>
  3228. <!-- Table -->
  3229. <div class="flex-1 overflow-x-auto">
  3230. <table class="w-full text-left text-xs font-mono">
  3231. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3232. <tr>
  3233. <th class="py-2 px-3 w-12 text-center cursor-pointer" onclick="sortOspfTable('live_ifaces', 'flags')">Flags</th>
  3234. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('live_ifaces', 'interface')">Interface <span id="sort-ospf-live_ifaces-interface">▲</span></th>
  3235. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('live_ifaces', 'address')">Address</th>
  3236. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('live_ifaces', 'area')">Area</th>
  3237. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('live_ifaces', 'instance_id')">Instance ID</th>
  3238. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('live_ifaces', 'state')">State</th>
  3239. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('live_ifaces', 'cost')">Cost</th>
  3240. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('live_ifaces', 'priority')">Priority</th>
  3241. </tr>
  3242. </thead>
  3243. <tbody id="ospf-live-interfaces-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3244. </table>
  3245. </div>
  3246. <!-- Footer -->
  3247. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3248. <span id="ospf-live-interfaces-footer-count">0</span>
  3249. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3250. </div>
  3251. </div>
  3252. <!-- ================= TAB 6: STATIC NEIGHBORS ================= -->
  3253. <div id="ospf-pane-static-nbrs" class="hidden flex-1 flex flex-col">
  3254. <!-- Toolbar -->
  3255. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3256. <div class="flex items-center space-x-2">
  3257. <button onclick="openOspfStaticNbrModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-sky-600 hover:text-white text-sky-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3258. <span>+</span> <span>New</span>
  3259. </button>
  3260. <button onclick="enableSelectedOspfStaticNbr()" id="btn-ospf-snbr-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3261. <span>▶</span> <span>Enable</span>
  3262. </button>
  3263. <button onclick="disableSelectedOspfStaticNbr()" id="btn-ospf-snbr-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3264. <span>⏸</span> <span>Disable</span>
  3265. </button>
  3266. <button onclick="removeSelectedOspfStaticNbr()" id="btn-ospf-snbr-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3267. <span>✕</span> <span>Remove</span>
  3268. </button>
  3269. <button onclick="promptCommentOspfStaticNbr()" id="btn-ospf-snbr-comment" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3270. <span>💬</span> <span>Comment</span>
  3271. </button>
  3272. </div>
  3273. <div class="flex items-center space-x-2">
  3274. <div class="relative">
  3275. <input type="text" id="filter-ospf-static-nbrs" oninput="renderOspfStaticNbrs()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-sky-500 w-32">
  3276. </div>
  3277. <button class="px-2 py-1 bg-[#15202e] hover:bg-[#1a2737] text-slate-300 border border-[#233346] rounded flex items-center gap-1">
  3278. <span>▼</span> <span>Filter</span>
  3279. </button>
  3280. </div>
  3281. </div>
  3282. <!-- Table -->
  3283. <div class="flex-1 overflow-x-auto">
  3284. <table class="w-full text-left text-xs font-mono">
  3285. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3286. <tr>
  3287. <th class="py-2 px-3 w-8 text-center cursor-pointer" onclick="sortOspfTable('snbrs', 'id')">#</th>
  3288. <th class="py-2 px-3 w-12 text-center cursor-pointer" onclick="sortOspfTable('snbrs', 'flags')">Flags</th>
  3289. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('snbrs', 'comment')">Comment</th>
  3290. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('snbrs', 'address')">Address <span id="sort-ospf-snbrs-address">▲</span></th>
  3291. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('snbrs', 'area')">Area</th>
  3292. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('snbrs', 'poll_interval')">Poll Interval</th>
  3293. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('snbrs', 'cost')">Cost</th>
  3294. </tr>
  3295. </thead>
  3296. <tbody id="ospf-snbrs-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3297. </table>
  3298. </div>
  3299. <!-- Footer -->
  3300. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3301. <span id="ospf-snbrs-footer-count">0</span>
  3302. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3303. </div>
  3304. </div>
  3305. <!-- ================= TAB 7: NEIGHBORS (LIVE DYNAMIC) ================= -->
  3306. <div id="ospf-pane-neighbors" class="hidden flex-1 flex flex-col">
  3307. <!-- Toolbar -->
  3308. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3309. <div class="text-slate-400 italic font-sans text-[11px]">Discovered OSPF neighbor adjacencies</div>
  3310. <div class="flex items-center space-x-2">
  3311. <div class="relative">
  3312. <input type="text" id="filter-ospf-neighbors" oninput="renderOspfNeighbors()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-sky-500 w-32">
  3313. </div>
  3314. <button class="px-2 py-1 bg-[#15202e] hover:bg-[#1a2737] text-slate-300 border border-[#233346] rounded flex items-center gap-1">
  3315. <span>▼</span> <span>Filter</span>
  3316. </button>
  3317. </div>
  3318. </div>
  3319. <!-- Table -->
  3320. <div class="flex-1 overflow-x-auto">
  3321. <table class="w-full text-left text-xs font-mono">
  3322. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3323. <tr>
  3324. <th class="py-2 px-3 w-12 text-center cursor-pointer" onclick="sortOspfTable('neighbors', 'flags')">Flags</th>
  3325. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('neighbors', 'comment')">Comment</th>
  3326. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('neighbors', 'instance')">Instance</th>
  3327. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('neighbors', 'area')">Area</th>
  3328. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('neighbors', 'address')">Address <span id="sort-ospf-neighbors-address">▲</span></th>
  3329. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('neighbors', 'state')">State</th>
  3330. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('neighbors', 'state_changes')">State Changes</th>
  3331. </tr>
  3332. </thead>
  3333. <tbody id="ospf-neighbors-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3334. </table>
  3335. </div>
  3336. <!-- Footer -->
  3337. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3338. <span id="ospf-neighbors-footer-count">0</span>
  3339. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3340. </div>
  3341. </div>
  3342. <!-- ================= TAB 8: LSA (LINK-STATE DATABASE) ================= -->
  3343. <div id="ospf-pane-lsa" class="hidden flex-1 flex flex-col">
  3344. <!-- Toolbar -->
  3345. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3346. <div class="text-slate-400 italic font-sans text-[11px]">BIRD Link-State Database (LSADB) advertisements</div>
  3347. <div class="flex items-center space-x-2">
  3348. <div class="relative">
  3349. <input type="text" id="filter-ospf-lsa" oninput="renderOspfLSA()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-sky-500 w-32">
  3350. </div>
  3351. <button class="px-2 py-1 bg-[#15202e] hover:bg-[#1a2737] text-slate-300 border border-[#233346] rounded flex items-center gap-1">
  3352. <span>▼</span> <span>Filter</span>
  3353. </button>
  3354. </div>
  3355. </div>
  3356. <!-- Table -->
  3357. <div class="flex-1 overflow-x-auto">
  3358. <table class="w-full text-left text-xs font-mono">
  3359. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3360. <tr>
  3361. <th class="py-2 px-3 w-12 text-center cursor-pointer" onclick="sortOspfTable('lsa', 'flags')">Flags</th>
  3362. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'instance')">Instance <span id="sort-ospf-lsa-instance">▲</span></th>
  3363. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'area')">Area</th>
  3364. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'type')">Type</th>
  3365. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'originator')">Originator</th>
  3366. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'id')">ID</th>
  3367. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'link')">Link</th>
  3368. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'link_index')">Link In...</th>
  3369. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'sequence')">Sequence</th>
  3370. <th class="py-2 px-3 cursor-pointer" onclick="sortOspfTable('lsa', 'age')">Age</th>
  3371. </tr>
  3372. </thead>
  3373. <tbody id="ospf-lsa-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3374. </table>
  3375. </div>
  3376. <!-- Footer -->
  3377. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3378. <span id="ospf-lsa-footer-count">0</span>
  3379. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3380. </div>
  3381. </div>
  3382. </div>
  3383. </section>
  3384. <!-- ================= SECTION: ROUTING FILTERS ================= -->
  3385. <section id="section-routing-filters" class="hidden space-y-4">
  3386. <!-- Top Routing Switcher Bar -->
  3387. <div class="flex items-center gap-1.5 p-1 bg-slate-900/90 border border-slate-800 rounded-xl w-fit flex-wrap">
  3388. <button onclick="navigate('routes')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3389. <svg class="w-3.5 h-3.5 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  3390. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
  3391. </svg>
  3392. <span>Routes & Policy</span>
  3393. </button>
  3394. <button onclick="navigate('bgp')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3395. <svg class="w-3.5 h-3.5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  3396. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
  3397. </svg>
  3398. <span>BGP Routing</span>
  3399. </button>
  3400. <button onclick="navigate('ospf')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3401. <svg class="w-3.5 h-3.5 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  3402. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
  3403. </svg>
  3404. <span>OSPF Routing</span>
  3405. </button>
  3406. <button onclick="navigate('rip')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3407. <svg class="w-3.5 h-3.5 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  3408. <span>RIP Routing</span>
  3409. </button>
  3410. <button onclick="navigate('bfd')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3411. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  3412. <span>BFD Sessions</span>
  3413. </button>
  3414. <button onclick="navigate('babel')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3415. <svg class="w-3.5 h-3.5 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /></svg>
  3416. <span>Babel Mesh</span>
  3417. </button>
  3418. <button onclick="navigate('radv')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3419. <svg class="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>
  3420. <span>IPv6 RAdv</span>
  3421. </button>
  3422. <button onclick="navigate('routing-filters')" class="px-3.5 py-1.5 text-xs font-bold rounded-lg transition bg-emerald-600/20 text-emerald-300 border border-emerald-500/40 shadow-sm flex items-center gap-2">
  3423. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  3424. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
  3425. </svg>
  3426. <span>Policy Filters</span>
  3427. </button>
  3428. </div>
  3429. <!-- Routing Filter Window Container -->
  3430. <div class="bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl">
  3431. <!-- Window Titlebar with Filter Tabs -->
  3432. <div class="bg-slate-950 border-b border-slate-800 px-3 py-1.5 flex flex-wrap items-center justify-between gap-2">
  3433. <div class="flex items-center gap-3">
  3434. <div class="flex items-center gap-2 text-emerald-400 font-bold text-xs">
  3435. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  3436. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
  3437. </svg>
  3438. <span>Routing Filters</span>
  3439. </div>
  3440. <!-- Filter Navigation Tabs -->
  3441. <div class="flex items-center gap-1 border-l border-slate-800 pl-3">
  3442. <button id="tab-rf-rules" onclick="switchRoutingFilterTab('rules')" class="px-3 py-1 text-xs font-semibold rounded bg-emerald-600/20 text-emerald-300 border border-emerald-500/30 transition">
  3443. Rules
  3444. </button>
  3445. <button id="tab-rf-community-lists" onclick="switchRoutingFilterTab('community-lists')" class="px-3 py-1 text-xs font-semibold rounded text-slate-400 hover:text-white hover:bg-slate-800 transition">
  3446. Community Lists
  3447. </button>
  3448. <button id="tab-rf-as-lists" onclick="switchRoutingFilterTab('as-lists')" class="px-3 py-1 text-xs font-semibold rounded text-slate-400 hover:text-white hover:bg-slate-800 transition">
  3449. AS Lists
  3450. </button>
  3451. </div>
  3452. </div>
  3453. <div class="flex items-center gap-2 text-slate-400 text-xs">
  3454. <span class="px-2 py-0.5 rounded text-[10px] font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 font-semibold">BIRD 2 Engine</span>
  3455. <button onclick="loadDynamicRouting(true)" class="p-1 hover:text-white rounded" title="Reload routing">↻</button>
  3456. </div>
  3457. </div>
  3458. <!-- Routing Filter Toolbar -->
  3459. <div class="bg-slate-900/90 border-b border-slate-800 px-3 py-2 flex flex-wrap items-center justify-between gap-2 text-xs">
  3460. <div class="flex items-center gap-1.5">
  3461. <button onclick="openRoutingFilterNewModal()" class="px-2.5 py-1 rounded font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-sm flex items-center gap-1">
  3462. <span>+ New</span>
  3463. </button>
  3464. <button id="btn-rf-enable-selected" onclick="toggleSelectedRoutingFilter(false)" class="px-2.5 py-1 rounded font-medium bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition">
  3465. Enable
  3466. </button>
  3467. <button id="btn-rf-disable-selected" onclick="toggleSelectedRoutingFilter(true)" class="px-2.5 py-1 rounded font-medium bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition">
  3468. Disable
  3469. </button>
  3470. <button id="btn-rf-remove-selected" onclick="removeSelectedRoutingFilter()" class="px-2.5 py-1 rounded font-medium bg-slate-800 hover:bg-rose-950 text-rose-400 border border-slate-700 transition">
  3471. Remove
  3472. </button>
  3473. </div>
  3474. <div class="flex items-center gap-2.5">
  3475. <!-- Filter by Chain (Visible in Rules and Select Rules tabs) -->
  3476. <div id="rf-chain-filter-container" class="flex items-center gap-1.5">
  3477. <label class="text-[11px] text-slate-400 font-medium">Chain:</label>
  3478. <select id="routing-filter-chain-select" onchange="filterRoutingFiltersByChain(this.value)" class="bg-slate-800 border border-slate-700 rounded px-2 py-1 text-xs text-white font-mono">
  3479. <option value="all">All Chains</option>
  3480. </select>
  3481. </div>
  3482. <!-- Find / Search Input -->
  3483. <div class="relative">
  3484. <input type="text" id="rf-search-input" oninput="searchRoutingFilters(this.value)" placeholder="Find..." class="bg-slate-800 border border-slate-700 rounded px-2.5 py-1 text-xs text-white pl-6 w-36 focus:w-48 transition-all">
  3485. <svg class="w-3 h-3 text-slate-400 absolute left-2 top-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  3486. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
  3487. </svg>
  3488. </div>
  3489. </div>
  3490. </div>
  3491. <!-- TAB 1: RULES -->
  3492. <div id="tab-content-rf-rules" class="overflow-x-auto">
  3493. <table class="w-full text-left text-xs font-mono">
  3494. <thead class="text-slate-400 bg-slate-950/70 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans select-none">
  3495. <tr>
  3496. <th class="py-2 px-2 text-center w-10 cursor-pointer hover:text-white" onclick="sortFilterTable('rules', 'flags')">Flags</th>
  3497. <th class="py-2 px-2 w-12 cursor-pointer hover:text-white" onclick="sortFilterTable('rules', 'rule_order')"># <span id="sort-icon-rules-rule_order">▲</span></th>
  3498. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('rules', 'chain')">Chain <span id="sort-icon-rules-chain"></span></th>
  3499. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('rules', 'rule')">Rule Syntax <span id="sort-icon-rules-rule"></span></th>
  3500. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('rules', 'comment')">Comment <span id="sort-icon-rules-comment"></span></th>
  3501. <th class="py-2 px-3 text-right font-sans">Actions</th>
  3502. </tr>
  3503. </thead>
  3504. <tbody id="routing-filter-rules-tbody" class="divide-y divide-slate-800/60"></tbody>
  3505. </table>
  3506. <div id="rf-rules-empty" class="hidden p-8 text-center text-slate-500 text-xs font-sans">
  3507. No filter rules configured. Click "+ New" to add a routing filter rule.
  3508. </div>
  3509. </div>
  3510. <!-- TAB 2: COMMUNITY LISTS -->
  3511. <div id="tab-content-rf-community-lists" class="hidden overflow-x-auto">
  3512. <table class="w-full text-left text-xs font-mono">
  3513. <thead class="text-slate-400 bg-slate-950/70 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans select-none">
  3514. <tr>
  3515. <th class="py-2 px-2 text-center w-10 cursor-pointer hover:text-white" onclick="sortFilterTable('community-lists', 'flags')">Flags</th>
  3516. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('community-lists', 'name')">List Name <span id="sort-icon-community-lists-name">▲</span></th>
  3517. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('community-lists', 'communities')">Communities <span id="sort-icon-community-lists-communities"></span></th>
  3518. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('community-lists', 'regexp')">RegExp <span id="sort-icon-community-lists-regexp"></span></th>
  3519. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('community-lists', 'comment')">Comment <span id="sort-icon-community-lists-comment"></span></th>
  3520. <th class="py-2 px-3 text-right font-sans">Actions</th>
  3521. </tr>
  3522. </thead>
  3523. <tbody id="routing-filter-comm-lists-tbody" class="divide-y divide-slate-800/60"></tbody>
  3524. </table>
  3525. <div id="rf-comm-lists-empty" class="hidden p-8 text-center text-slate-500 text-xs font-sans">
  3526. No BGP community lists configured. Click "+ New" to add a named community set.
  3527. </div>
  3528. </div>
  3529. <!-- TAB 4: AS LISTS -->
  3530. <div id="tab-content-rf-as-lists" class="hidden overflow-x-auto">
  3531. <table class="w-full text-left text-xs font-mono">
  3532. <thead class="text-slate-400 bg-slate-950/70 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans select-none">
  3533. <tr>
  3534. <th class="py-2 px-2 text-center w-10 cursor-pointer hover:text-white" onclick="sortFilterTable('as-lists', 'flags')">Flags</th>
  3535. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('as-lists', 'name')">List Name <span id="sort-icon-as-lists-name">▲</span></th>
  3536. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('as-lists', 'range')">Range / ASNs <span id="sort-icon-as-lists-range"></span></th>
  3537. <th class="py-2 px-3 cursor-pointer hover:text-white" onclick="sortFilterTable('as-lists', 'comment')">Comment <span id="sort-icon-as-lists-comment"></span></th>
  3538. <th class="py-2 px-3 text-right font-sans">Actions</th>
  3539. </tr>
  3540. </thead>
  3541. <tbody id="routing-filter-as-lists-tbody" class="divide-y divide-slate-800/60"></tbody>
  3542. </table>
  3543. <div id="rf-as-lists-empty" class="hidden p-8 text-center text-slate-500 text-xs font-sans">
  3544. No AS number lists configured. Click "+ New" to define reusable AS ranges.
  3545. </div>
  3546. </div>
  3547. <!-- Live Status Footer -->
  3548. <div class="px-3 py-1.5 bg-slate-950/80 border-t border-slate-800 flex items-center justify-between text-[11px] text-slate-400">
  3549. <div class="flex items-center gap-2">
  3550. <span id="rf-status-count" class="font-bold text-white">0</span>
  3551. <span class="flex items-center gap-1 text-emerald-400">
  3552. <span class="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"></span>
  3553. <span>Live</span>
  3554. </span>
  3555. </div>
  3556. <span class="text-[10px] text-slate-500 font-mono">BIRD 2 Dynamic Routing Policy Engine</span>
  3557. </div>
  3558. </div>
  3559. </section>
  3560. <!-- ================= SECTION: RIP ROUTING ================= -->
  3561. <section id="section-rip" class="hidden space-y-6">
  3562. <!-- Top Routing Switcher Bar -->
  3563. <div class="flex items-center gap-1.5 p-1 bg-slate-900/90 border border-slate-800 rounded-xl w-fit flex-wrap">
  3564. <button onclick="navigate('routes')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3565. <svg class="w-3.5 h-3.5 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" /></svg>
  3566. <span>Routes & Policy</span>
  3567. </button>
  3568. <button onclick="navigate('bgp')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3569. <svg class="w-3.5 h-3.5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" /></svg>
  3570. <span>BGP Routing</span>
  3571. </button>
  3572. <button onclick="navigate('ospf')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3573. <svg class="w-3.5 h-3.5 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /></svg>
  3574. <span>OSPF Routing</span>
  3575. </button>
  3576. <button onclick="navigate('rip')" class="px-3.5 py-1.5 text-xs font-bold rounded-lg transition bg-rose-600/20 text-rose-300 border border-rose-500/40 shadow-sm flex items-center gap-2">
  3577. <svg class="w-3.5 h-3.5 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  3578. <span>RIP Routing</span>
  3579. </button>
  3580. <button onclick="navigate('bfd')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3581. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  3582. <span>BFD Sessions</span>
  3583. </button>
  3584. <button onclick="navigate('babel')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3585. <svg class="w-3.5 h-3.5 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /></svg>
  3586. <span>Babel Mesh</span>
  3587. </button>
  3588. <button onclick="navigate('radv')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3589. <svg class="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>
  3590. <span>IPv6 RAdv</span>
  3591. </button>
  3592. <button onclick="navigate('routing-filters')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3593. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" /></svg>
  3594. <span>Policy Filters</span>
  3595. </button>
  3596. </div>
  3597. <!-- RIP Window Container -->
  3598. <div class="bg-[#131b26] border border-[#243347] rounded-md shadow-2xl overflow-hidden text-xs flex flex-col min-h-[560px]">
  3599. <div class="flex items-center justify-between border-b border-[#243347] bg-[#101721] px-2 pt-1 text-xs select-none">
  3600. <div class="flex items-center space-x-1 overflow-x-auto">
  3601. <div class="flex items-center gap-1.5 px-3 py-2 text-rose-400 font-bold border-r border-[#243347] mr-1">
  3602. <span>⚡</span>
  3603. <span>RIP</span>
  3604. <span class="text-[10px]">▼</span>
  3605. </div>
  3606. <button id="rip-tab-btn-instances" onclick="switchRipTab('instances')" class="px-3 py-2 text-xs font-semibold border-b-2 border-rose-500 text-white transition flex items-center gap-1.5 whitespace-nowrap">
  3607. <span>Instances</span>
  3608. </button>
  3609. <button id="rip-tab-btn-interfaces" onclick="switchRipTab('interfaces')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  3610. <span>Interfaces</span>
  3611. </button>
  3612. <button id="rip-tab-btn-neighbors" onclick="switchRipTab('neighbors')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  3613. <span>Neighbors (Live)</span>
  3614. </button>
  3615. <button id="rip-tab-btn-routes" onclick="switchRipTab('routes')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  3616. <span>Routes (Live)</span>
  3617. </button>
  3618. </div>
  3619. <div class="flex items-center gap-1 text-slate-400 pr-2">
  3620. <button onclick="loadDynamicRouting(true)" title="Refresh" class="hover:text-white p-1">🔄</button>
  3621. </div>
  3622. </div>
  3623. <!-- TAB 1: RIP INSTANCES -->
  3624. <div id="rip-pane-instances" class="flex-1 flex flex-col">
  3625. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3626. <div class="flex items-center space-x-2">
  3627. <button onclick="openRipInstanceModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-rose-600 hover:text-white text-rose-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3628. <span>+</span> <span>New</span>
  3629. </button>
  3630. <button onclick="enableSelectedRipInstance()" id="btn-rip-inst-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3631. <span>▶</span> <span>Enable</span>
  3632. </button>
  3633. <button onclick="disableSelectedRipInstance()" id="btn-rip-inst-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3634. <span>⏸</span> <span>Disable</span>
  3635. </button>
  3636. <button onclick="removeSelectedRipInstance()" id="btn-rip-inst-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3637. <span>✕</span> <span>Remove</span>
  3638. </button>
  3639. </div>
  3640. <div class="flex items-center space-x-2">
  3641. <input type="text" id="filter-rip-instances" oninput="renderRipInstances()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-rose-500 w-32">
  3642. </div>
  3643. </div>
  3644. <div class="flex-1 overflow-x-auto">
  3645. <table class="w-full text-left text-xs font-mono">
  3646. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3647. <tr>
  3648. <th class="py-2 px-3 w-8 text-center">#</th>
  3649. <th class="py-2 px-3 w-12 text-center">Flags</th>
  3650. <th class="py-2 px-3">Comment</th>
  3651. <th class="py-2 px-3">Name</th>
  3652. <th class="py-2 px-3">Version</th>
  3653. <th class="py-2 px-3">Update Time</th>
  3654. <th class="py-2 px-3">Timeout</th>
  3655. <th class="py-2 px-3">Redistribute</th>
  3656. <th class="py-2 px-3">Metric</th>
  3657. </tr>
  3658. </thead>
  3659. <tbody id="rip-instances-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3660. </table>
  3661. </div>
  3662. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3663. <span id="rip-instances-footer-count">0</span>
  3664. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3665. </div>
  3666. </div>
  3667. <!-- TAB 2: RIP INTERFACES -->
  3668. <div id="rip-pane-interfaces" class="hidden flex-1 flex flex-col">
  3669. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3670. <div class="flex items-center space-x-2">
  3671. <button onclick="openRipInterfaceModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-rose-600 hover:text-white text-rose-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3672. <span>+</span> <span>New</span>
  3673. </button>
  3674. <button onclick="enableSelectedRipInterface()" id="btn-rip-iface-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3675. <span>▶</span> <span>Enable</span>
  3676. </button>
  3677. <button onclick="disableSelectedRipInterface()" id="btn-rip-iface-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3678. <span>⏸</span> <span>Disable</span>
  3679. </button>
  3680. <button onclick="removeSelectedRipInterface()" id="btn-rip-iface-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3681. <span>✕</span> <span>Remove</span>
  3682. </button>
  3683. </div>
  3684. <div class="flex items-center space-x-2">
  3685. <input type="text" id="filter-rip-interfaces" oninput="renderRipInterfaces()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-rose-500 w-32">
  3686. </div>
  3687. </div>
  3688. <div class="flex-1 overflow-x-auto">
  3689. <table class="w-full text-left text-xs font-mono">
  3690. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3691. <tr>
  3692. <th class="py-2 px-3 w-8 text-center">#</th>
  3693. <th class="py-2 px-3 w-12 text-center">Flags</th>
  3694. <th class="py-2 px-3">Comment</th>
  3695. <th class="py-2 px-3">Interface</th>
  3696. <th class="py-2 px-3">Mode</th>
  3697. <th class="py-2 px-3">Metric</th>
  3698. <th class="py-2 px-3">Passive</th>
  3699. <th class="py-2 px-3">BFD</th>
  3700. </tr>
  3701. </thead>
  3702. <tbody id="rip-interfaces-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3703. </table>
  3704. </div>
  3705. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3706. <span id="rip-interfaces-footer-count">0</span>
  3707. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3708. </div>
  3709. </div>
  3710. <!-- TAB 3: RIP NEIGHBORS (LIVE) -->
  3711. <div id="rip-pane-neighbors" class="hidden flex-1 flex flex-col">
  3712. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3713. <span class="text-slate-400 font-sans">Dynamic Neighbors detected via BIRD RIP multicasts</span>
  3714. <button onclick="loadDynamicRouting(true)" class="px-2 py-1 bg-[#1a2737] hover:bg-slate-700 text-slate-300 rounded text-xs">🔄 Refresh</button>
  3715. </div>
  3716. <div class="flex-1 overflow-x-auto">
  3717. <table class="w-full text-left text-xs font-mono">
  3718. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3719. <tr>
  3720. <th class="py-2 px-3 w-8 text-center">#</th>
  3721. <th class="py-2 px-3">IP Address</th>
  3722. <th class="py-2 px-3">Interface</th>
  3723. <th class="py-2 px-3">Last Seen</th>
  3724. <th class="py-2 px-3">Uptime</th>
  3725. </tr>
  3726. </thead>
  3727. <tbody id="rip-neighbors-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3728. </table>
  3729. </div>
  3730. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3731. <span id="rip-neighbors-footer-count">0</span>
  3732. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3733. </div>
  3734. </div>
  3735. <!-- TAB 4: RIP ROUTES (LIVE) -->
  3736. <div id="rip-pane-routes" class="hidden flex-1 flex flex-col">
  3737. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3738. <span class="text-slate-400 font-sans">Active RIP Dynamic Routes</span>
  3739. <button onclick="loadDynamicRouting(true)" class="px-2 py-1 bg-[#1a2737] hover:bg-slate-700 text-slate-300 rounded text-xs">🔄 Refresh</button>
  3740. </div>
  3741. <div class="flex-1 overflow-x-auto">
  3742. <table class="w-full text-left text-xs font-mono">
  3743. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3744. <tr>
  3745. <th class="py-2 px-3 w-8 text-center">#</th>
  3746. <th class="py-2 px-3">Prefix</th>
  3747. <th class="py-2 px-3">Next-Hop</th>
  3748. <th class="py-2 px-3">Metric</th>
  3749. <th class="py-2 px-3">Interface</th>
  3750. <th class="py-2 px-3">Age</th>
  3751. </tr>
  3752. </thead>
  3753. <tbody id="rip-routes-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3754. </table>
  3755. </div>
  3756. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3757. <span id="rip-routes-footer-count">0</span>
  3758. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3759. </div>
  3760. </div>
  3761. </div>
  3762. </section>
  3763. <!-- ================= SECTION: BFD SESSIONS ================= -->
  3764. <section id="section-bfd" class="hidden space-y-6">
  3765. <!-- Top Routing Switcher Bar -->
  3766. <div class="flex items-center gap-1.5 p-1 bg-slate-900/90 border border-slate-800 rounded-xl w-fit flex-wrap">
  3767. <button onclick="navigate('routes')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3768. <svg class="w-3.5 h-3.5 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" /></svg>
  3769. <span>Routes & Policy</span>
  3770. </button>
  3771. <button onclick="navigate('bgp')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3772. <svg class="w-3.5 h-3.5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" /></svg>
  3773. <span>BGP Routing</span>
  3774. </button>
  3775. <button onclick="navigate('ospf')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3776. <svg class="w-3.5 h-3.5 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /></svg>
  3777. <span>OSPF Routing</span>
  3778. </button>
  3779. <button onclick="navigate('rip')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3780. <svg class="w-3.5 h-3.5 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  3781. <span>RIP Routing</span>
  3782. </button>
  3783. <button onclick="navigate('bfd')" class="px-3.5 py-1.5 text-xs font-bold rounded-lg transition bg-amber-600/20 text-amber-300 border border-amber-500/40 shadow-sm flex items-center gap-2">
  3784. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  3785. <span>BFD Sessions</span>
  3786. </button>
  3787. <button onclick="navigate('babel')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3788. <svg class="w-3.5 h-3.5 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /></svg>
  3789. <span>Babel Mesh</span>
  3790. </button>
  3791. <button onclick="navigate('radv')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3792. <svg class="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>
  3793. <span>IPv6 RAdv</span>
  3794. </button>
  3795. <button onclick="navigate('routing-filters')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3796. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" /></svg>
  3797. <span>Policy Filters</span>
  3798. </button>
  3799. </div>
  3800. <!-- BFD Window Container -->
  3801. <div class="bg-[#131b26] border border-[#243347] rounded-md shadow-2xl overflow-hidden text-xs flex flex-col min-h-[560px]">
  3802. <div class="flex items-center justify-between border-b border-[#243347] bg-[#101721] px-2 pt-1 text-xs select-none">
  3803. <div class="flex items-center space-x-1 overflow-x-auto">
  3804. <div class="flex items-center gap-1.5 px-3 py-2 text-amber-400 font-bold border-r border-[#243347] mr-1">
  3805. <span>⏱</span>
  3806. <span>BFD</span>
  3807. <span class="text-[10px]">▼</span>
  3808. </div>
  3809. <button id="bfd-tab-btn-config" onclick="switchBfdTab('config')" class="px-3 py-2 text-xs font-semibold border-b-2 border-amber-500 text-white transition flex items-center gap-1.5 whitespace-nowrap">
  3810. <span>Configuration</span>
  3811. </button>
  3812. <button id="bfd-tab-btn-interfaces" onclick="switchBfdTab('interfaces')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  3813. <span>Interfaces</span>
  3814. </button>
  3815. <button id="bfd-tab-btn-peers" onclick="switchBfdTab('peers')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  3816. <span>Peers</span>
  3817. </button>
  3818. <button id="bfd-tab-btn-sessions" onclick="switchBfdTab('sessions')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  3819. <span>Live Sessions</span>
  3820. </button>
  3821. </div>
  3822. <div class="flex items-center gap-1 text-slate-400 pr-2">
  3823. <button onclick="loadDynamicRouting(true)" title="Refresh" class="hover:text-white p-1">🔄</button>
  3824. </div>
  3825. </div>
  3826. <!-- TAB 1: BFD CONFIG -->
  3827. <div id="bfd-pane-config" class="flex-1 p-6 space-y-6">
  3828. <div class="bg-slate-900 border border-slate-800 rounded-xl p-5 flex items-center justify-between">
  3829. <div>
  3830. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  3831. <span>BFD Protocol Daemon</span>
  3832. <span id="bfd-live-status-pill" class="text-[10px] px-2 py-0.5 rounded font-mono bg-slate-800 text-slate-400 border border-slate-700">INACTIVE</span>
  3833. </h3>
  3834. <p class="text-xs text-slate-400 mt-1">Bidirectional Forwarding Detection (BFD) provides sub-50ms fault detection for routing protocols.</p>
  3835. <p class="text-xs text-slate-500 mt-0.5">When enabled, BGP peers and OSPF interfaces with BFD enabled will register live sessions automatically.</p>
  3836. </div>
  3837. <button id="bfd-global-toggle-btn" onclick="toggleBfdGlobalState()" class="px-4 py-2 rounded-lg text-xs font-semibold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20 transition">
  3838. Enable BFD
  3839. </button>
  3840. </div>
  3841. <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
  3842. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  3843. <div class="text-xs text-slate-400">Total BFD Interfaces</div>
  3844. <div id="bfd-stat-interfaces" class="text-xl font-bold text-white font-mono mt-1">0</div>
  3845. </div>
  3846. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  3847. <div class="text-xs text-slate-400">Configured Static Peers</div>
  3848. <div id="bfd-stat-peers" class="text-xl font-bold text-white font-mono mt-1">0</div>
  3849. </div>
  3850. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  3851. <div class="text-xs text-slate-400">Live Active Sessions</div>
  3852. <div id="bfd-stat-sessions" class="text-xl font-bold text-emerald-400 font-mono mt-1">0</div>
  3853. </div>
  3854. </div>
  3855. </div>
  3856. <!-- TAB 2: BFD INTERFACES -->
  3857. <div id="bfd-pane-interfaces" class="hidden flex-1 flex flex-col">
  3858. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3859. <div class="flex items-center space-x-2">
  3860. <button onclick="openBfdInterfaceModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-amber-600 hover:text-white text-amber-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3861. <span>+</span> <span>New</span>
  3862. </button>
  3863. <button onclick="enableSelectedBfdInterface()" id="btn-bfd-iface-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3864. <span>▶</span> <span>Enable</span>
  3865. </button>
  3866. <button onclick="disableSelectedBfdInterface()" id="btn-bfd-iface-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3867. <span>⏸</span> <span>Disable</span>
  3868. </button>
  3869. <button onclick="removeSelectedBfdInterface()" id="btn-bfd-iface-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3870. <span>✕</span> <span>Remove</span>
  3871. </button>
  3872. </div>
  3873. <div class="flex items-center space-x-2">
  3874. <input type="text" id="filter-bfd-interfaces" oninput="renderBfdInterfaces()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-amber-500 w-32">
  3875. </div>
  3876. </div>
  3877. <div class="flex-1 overflow-x-auto">
  3878. <table class="w-full text-left text-xs font-mono">
  3879. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3880. <tr>
  3881. <th class="py-2 px-3 w-8 text-center">#</th>
  3882. <th class="py-2 px-3 w-12 text-center">Flags</th>
  3883. <th class="py-2 px-3">Comment</th>
  3884. <th class="py-2 px-3">Interface</th>
  3885. <th class="py-2 px-3">Min RX (ms)</th>
  3886. <th class="py-2 px-3">Min TX (ms)</th>
  3887. <th class="py-2 px-3">Idle TX (ms)</th>
  3888. <th class="py-2 px-3">Multiplier</th>
  3889. <th class="py-2 px-3">Passive</th>
  3890. </tr>
  3891. </thead>
  3892. <tbody id="bfd-interfaces-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3893. </table>
  3894. </div>
  3895. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3896. <span id="bfd-interfaces-footer-count">0</span>
  3897. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3898. </div>
  3899. </div>
  3900. <!-- TAB 3: BFD PEERS -->
  3901. <div id="bfd-pane-peers" class="hidden flex-1 flex flex-col">
  3902. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3903. <div class="flex items-center space-x-2">
  3904. <button onclick="openBfdPeerModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-amber-600 hover:text-white text-amber-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  3905. <span>+</span> <span>New</span>
  3906. </button>
  3907. <button onclick="enableSelectedBfdPeer()" id="btn-bfd-peer-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3908. <span>▶</span> <span>Enable</span>
  3909. </button>
  3910. <button onclick="disableSelectedBfdPeer()" id="btn-bfd-peer-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3911. <span>⏸</span> <span>Disable</span>
  3912. </button>
  3913. <button onclick="removeSelectedBfdPeer()" id="btn-bfd-peer-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  3914. <span>✕</span> <span>Remove</span>
  3915. </button>
  3916. </div>
  3917. <div class="flex items-center space-x-2">
  3918. <input type="text" id="filter-bfd-peers" oninput="renderBfdPeers()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-amber-500 w-32">
  3919. </div>
  3920. </div>
  3921. <div class="flex-1 overflow-x-auto">
  3922. <table class="w-full text-left text-xs font-mono">
  3923. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3924. <tr>
  3925. <th class="py-2 px-3 w-8 text-center">#</th>
  3926. <th class="py-2 px-3 w-12 text-center">Flags</th>
  3927. <th class="py-2 px-3">Comment</th>
  3928. <th class="py-2 px-3">Neighbor Address</th>
  3929. <th class="py-2 px-3">Interface</th>
  3930. <th class="py-2 px-3">Multihop</th>
  3931. </tr>
  3932. </thead>
  3933. <tbody id="bfd-peers-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3934. </table>
  3935. </div>
  3936. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3937. <span id="bfd-peers-footer-count">0</span>
  3938. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3939. </div>
  3940. </div>
  3941. <!-- TAB 4: BFD SESSIONS (LIVE) -->
  3942. <div id="bfd-pane-sessions" class="hidden flex-1 flex flex-col">
  3943. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  3944. <span class="text-slate-400 font-sans">Live BFD Sessions detected by BIRD (BGP, OSPF & static peers)</span>
  3945. <button onclick="loadDynamicRouting(true)" class="px-2 py-1 bg-[#1a2737] hover:bg-slate-700 text-slate-300 rounded text-xs">🔄 Refresh</button>
  3946. </div>
  3947. <div class="flex-1 overflow-x-auto">
  3948. <table class="w-full text-left text-xs font-mono">
  3949. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  3950. <tr>
  3951. <th class="py-2 px-3 w-8 text-center">#</th>
  3952. <th class="py-2 px-3">Neighbor IP</th>
  3953. <th class="py-2 px-3">Interface</th>
  3954. <th class="py-2 px-3">State</th>
  3955. <th class="py-2 px-3">Interval</th>
  3956. <th class="py-2 px-3">Timeout</th>
  3957. <th class="py-2 px-3">Since</th>
  3958. </tr>
  3959. </thead>
  3960. <tbody id="bfd-sessions-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  3961. </table>
  3962. </div>
  3963. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  3964. <span id="bfd-sessions-footer-count">0</span>
  3965. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  3966. </div>
  3967. </div>
  3968. </div>
  3969. </section>
  3970. <!-- ================= SECTION: BABEL ROUTING ================= -->
  3971. <section id="section-babel" class="hidden space-y-6">
  3972. <!-- Top Routing Switcher Bar -->
  3973. <div class="flex items-center gap-1.5 p-1 bg-slate-900/90 border border-slate-800 rounded-xl w-fit flex-wrap">
  3974. <button onclick="navigate('routes')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3975. <svg class="w-3.5 h-3.5 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" /></svg>
  3976. <span>Routes & Policy</span>
  3977. </button>
  3978. <button onclick="navigate('bgp')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3979. <svg class="w-3.5 h-3.5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" /></svg>
  3980. <span>BGP Routing</span>
  3981. </button>
  3982. <button onclick="navigate('ospf')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3983. <svg class="w-3.5 h-3.5 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /></svg>
  3984. <span>OSPF Routing</span>
  3985. </button>
  3986. <button onclick="navigate('rip')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3987. <svg class="w-3.5 h-3.5 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  3988. <span>RIP Routing</span>
  3989. </button>
  3990. <button onclick="navigate('bfd')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3991. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  3992. <span>BFD Sessions</span>
  3993. </button>
  3994. <button onclick="navigate('babel')" class="px-3.5 py-1.5 text-xs font-bold rounded-lg transition bg-teal-600/20 text-teal-300 border border-teal-500/40 shadow-sm flex items-center gap-2">
  3995. <svg class="w-3.5 h-3.5 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /></svg>
  3996. <span>Babel Mesh</span>
  3997. </button>
  3998. <button onclick="navigate('radv')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  3999. <svg class="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>
  4000. <span>IPv6 RAdv</span>
  4001. </button>
  4002. <button onclick="navigate('routing-filters')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  4003. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" /></svg>
  4004. <span>Policy Filters</span>
  4005. </button>
  4006. </div>
  4007. <!-- Babel Window Container -->
  4008. <div class="bg-[#131b26] border border-[#243347] rounded-md shadow-2xl overflow-hidden text-xs flex flex-col min-h-[560px]">
  4009. <div class="flex items-center justify-between border-b border-[#243347] bg-[#101721] px-2 pt-1 text-xs select-none">
  4010. <div class="flex items-center space-x-1 overflow-x-auto">
  4011. <div class="flex items-center gap-1.5 px-3 py-2 text-teal-400 font-bold border-r border-[#243347] mr-1">
  4012. <span>🌐</span>
  4013. <span>Babel</span>
  4014. <span class="text-[10px]">▼</span>
  4015. </div>
  4016. <button id="babel-tab-btn-instances" onclick="switchBabelTab('instances')" class="px-3 py-2 text-xs font-semibold border-b-2 border-teal-500 text-white transition flex items-center gap-1.5 whitespace-nowrap">
  4017. <span>Instances</span>
  4018. </button>
  4019. <button id="babel-tab-btn-interfaces" onclick="switchBabelTab('interfaces')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  4020. <span>Interfaces</span>
  4021. </button>
  4022. <button id="babel-tab-btn-neighbors" onclick="switchBabelTab('neighbors')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  4023. <span>Neighbors (Live)</span>
  4024. </button>
  4025. <button id="babel-tab-btn-routes" onclick="switchBabelTab('routes')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  4026. <span>Routes (Live)</span>
  4027. </button>
  4028. </div>
  4029. <div class="flex items-center gap-1 text-slate-400 pr-2">
  4030. <button onclick="loadDynamicRouting(true)" title="Refresh" class="hover:text-white p-1">🔄</button>
  4031. </div>
  4032. </div>
  4033. <!-- TAB 1: BABEL INSTANCES -->
  4034. <div id="babel-pane-instances" class="flex-1 flex flex-col">
  4035. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  4036. <div class="flex items-center space-x-2">
  4037. <button onclick="openBabelInstanceModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-teal-600 hover:text-white text-teal-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  4038. <span>+</span> <span>New</span>
  4039. </button>
  4040. <button onclick="enableSelectedBabelInstance()" id="btn-babel-inst-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4041. <span>▶</span> <span>Enable</span>
  4042. </button>
  4043. <button onclick="disableSelectedBabelInstance()" id="btn-babel-inst-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4044. <span>⏸</span> <span>Disable</span>
  4045. </button>
  4046. <button onclick="removeSelectedBabelInstance()" id="btn-babel-inst-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4047. <span>✕</span> <span>Remove</span>
  4048. </button>
  4049. </div>
  4050. <div class="flex items-center space-x-2">
  4051. <input type="text" id="filter-babel-instances" oninput="renderBabelInstances()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-teal-500 w-32">
  4052. </div>
  4053. </div>
  4054. <div class="flex-1 overflow-x-auto">
  4055. <table class="w-full text-left text-xs font-mono">
  4056. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  4057. <tr>
  4058. <th class="py-2 px-3 w-8 text-center">#</th>
  4059. <th class="py-2 px-3 w-12 text-center">Flags</th>
  4060. <th class="py-2 px-3">Comment</th>
  4061. <th class="py-2 px-3">Name</th>
  4062. <th class="py-2 px-3">Router ID</th>
  4063. <th class="py-2 px-3">Dual-Stack</th>
  4064. <th class="py-2 px-3">Redistribute</th>
  4065. <th class="py-2 px-3">Metric</th>
  4066. </tr>
  4067. </thead>
  4068. <tbody id="babel-instances-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  4069. </table>
  4070. </div>
  4071. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  4072. <span id="babel-instances-footer-count">0</span>
  4073. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  4074. </div>
  4075. </div>
  4076. <!-- TAB 2: BABEL INTERFACES -->
  4077. <div id="babel-pane-interfaces" class="hidden flex-1 flex flex-col">
  4078. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  4079. <div class="flex items-center space-x-2">
  4080. <button onclick="openBabelInterfaceModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-teal-600 hover:text-white text-teal-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  4081. <span>+</span> <span>New</span>
  4082. </button>
  4083. <button onclick="enableSelectedBabelInterface()" id="btn-babel-iface-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4084. <span>▶</span> <span>Enable</span>
  4085. </button>
  4086. <button onclick="disableSelectedBabelInterface()" id="btn-babel-iface-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4087. <span>⏸</span> <span>Disable</span>
  4088. </button>
  4089. <button onclick="removeSelectedBabelInterface()" id="btn-babel-iface-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4090. <span>✕</span> <span>Remove</span>
  4091. </button>
  4092. </div>
  4093. <div class="flex items-center space-x-2">
  4094. <input type="text" id="filter-babel-interfaces" oninput="renderBabelInterfaces()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-teal-500 w-32">
  4095. </div>
  4096. </div>
  4097. <div class="flex-1 overflow-x-auto">
  4098. <table class="w-full text-left text-xs font-mono">
  4099. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  4100. <tr>
  4101. <th class="py-2 px-3 w-8 text-center">#</th>
  4102. <th class="py-2 px-3 w-12 text-center">Flags</th>
  4103. <th class="py-2 px-3">Comment</th>
  4104. <th class="py-2 px-3">Interface</th>
  4105. <th class="py-2 px-3">Type</th>
  4106. <th class="py-2 px-3">RX Cost</th>
  4107. <th class="py-2 px-3">Hello Interval</th>
  4108. <th class="py-2 px-3">Update Interval</th>
  4109. </tr>
  4110. </thead>
  4111. <tbody id="babel-interfaces-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  4112. </table>
  4113. </div>
  4114. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  4115. <span id="babel-interfaces-footer-count">0</span>
  4116. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  4117. </div>
  4118. </div>
  4119. <!-- TAB 3: BABEL NEIGHBORS (LIVE) -->
  4120. <div id="babel-pane-neighbors" class="hidden flex-1 flex flex-col">
  4121. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  4122. <span class="text-slate-400 font-sans">Live Babel Mesh Neighbors</span>
  4123. <button onclick="loadDynamicRouting(true)" class="px-2 py-1 bg-[#1a2737] hover:bg-slate-700 text-slate-300 rounded text-xs">🔄 Refresh</button>
  4124. </div>
  4125. <div class="flex-1 overflow-x-auto">
  4126. <table class="w-full text-left text-xs font-mono">
  4127. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  4128. <tr>
  4129. <th class="py-2 px-3 w-8 text-center">#</th>
  4130. <th class="py-2 px-3">Neighbor IP</th>
  4131. <th class="py-2 px-3">Interface</th>
  4132. <th class="py-2 px-3">Metric</th>
  4133. <th class="py-2 px-3">Cost</th>
  4134. <th class="py-2 px-3">Uptime</th>
  4135. </tr>
  4136. </thead>
  4137. <tbody id="babel-neighbors-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  4138. </table>
  4139. </div>
  4140. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  4141. <span id="babel-neighbors-footer-count">0</span>
  4142. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  4143. </div>
  4144. </div>
  4145. <!-- TAB 4: BABEL ROUTES (LIVE) -->
  4146. <div id="babel-pane-routes" class="hidden flex-1 flex flex-col">
  4147. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  4148. <span class="text-slate-400 font-sans">Active Babel Mesh Routes</span>
  4149. <button onclick="loadDynamicRouting(true)" class="px-2 py-1 bg-[#1a2737] hover:bg-slate-700 text-slate-300 rounded text-xs">🔄 Refresh</button>
  4150. </div>
  4151. <div class="flex-1 overflow-x-auto">
  4152. <table class="w-full text-left text-xs font-mono">
  4153. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  4154. <tr>
  4155. <th class="py-2 px-3 w-8 text-center">#</th>
  4156. <th class="py-2 px-3">Prefix</th>
  4157. <th class="py-2 px-3">Next-Hop</th>
  4158. <th class="py-2 px-3">Metric</th>
  4159. <th class="py-2 px-3">Interface</th>
  4160. </tr>
  4161. </thead>
  4162. <tbody id="babel-routes-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  4163. </table>
  4164. </div>
  4165. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  4166. <span id="babel-routes-footer-count">0</span>
  4167. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  4168. </div>
  4169. </div>
  4170. </div>
  4171. </section>
  4172. <!-- ================= SECTION: RADV (ROUTER ADVERTISEMENTS) ================= -->
  4173. <section id="section-radv" class="hidden space-y-6">
  4174. <!-- Top Routing Switcher Bar -->
  4175. <div class="flex items-center gap-1.5 p-1 bg-slate-900/90 border border-slate-800 rounded-xl w-fit flex-wrap">
  4176. <button onclick="navigate('routes')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  4177. <svg class="w-3.5 h-3.5 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" /></svg>
  4178. <span>Routes & Policy</span>
  4179. </button>
  4180. <button onclick="navigate('bgp')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  4181. <svg class="w-3.5 h-3.5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" /></svg>
  4182. <span>BGP Routing</span>
  4183. </button>
  4184. <button onclick="navigate('ospf')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  4185. <svg class="w-3.5 h-3.5 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /></svg>
  4186. <span>OSPF Routing</span>
  4187. </button>
  4188. <button onclick="navigate('rip')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  4189. <svg class="w-3.5 h-3.5 text-rose-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  4190. <span>RIP Routing</span>
  4191. </button>
  4192. <button onclick="navigate('bfd')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  4193. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  4194. <span>BFD Sessions</span>
  4195. </button>
  4196. <button onclick="navigate('babel')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  4197. <svg class="w-3.5 h-3.5 text-teal-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /></svg>
  4198. <span>Babel Mesh</span>
  4199. </button>
  4200. <button onclick="navigate('radv')" class="px-3.5 py-1.5 text-xs font-bold rounded-lg transition bg-blue-600/20 text-blue-300 border border-blue-500/40 shadow-sm flex items-center gap-2">
  4201. <svg class="w-3.5 h-3.5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /></svg>
  4202. <span>IPv6 RAdv</span>
  4203. </button>
  4204. <button onclick="navigate('routing-filters')" class="px-3.5 py-1.5 text-xs font-medium rounded-lg text-slate-400 hover:text-white hover:bg-slate-800/60 transition flex items-center gap-2">
  4205. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" /></svg>
  4206. <span>Policy Filters</span>
  4207. </button>
  4208. </div>
  4209. <!-- RAdv Window Container -->
  4210. <div class="bg-[#131b26] border border-[#243347] rounded-md shadow-2xl overflow-hidden text-xs flex flex-col min-h-[560px]">
  4211. <div class="flex items-center justify-between border-b border-[#243347] bg-[#101721] px-2 pt-1 text-xs select-none">
  4212. <div class="flex items-center space-x-1 overflow-x-auto">
  4213. <div class="flex items-center gap-1.5 px-3 py-2 text-blue-400 font-bold border-r border-[#243347] mr-1">
  4214. <span>📢</span>
  4215. <span>RAdv</span>
  4216. <span class="text-[10px]">▼</span>
  4217. </div>
  4218. <button id="radv-tab-btn-instances" onclick="switchRadvTab('instances')" class="px-3 py-2 text-xs font-semibold border-b-2 border-blue-500 text-white transition flex items-center gap-1.5 whitespace-nowrap">
  4219. <span>Instances</span>
  4220. </button>
  4221. <button id="radv-tab-btn-interfaces" onclick="switchRadvTab('interfaces')" class="px-3 py-2 text-xs font-semibold border-b-2 border-transparent text-slate-400 hover:text-white transition flex items-center gap-1.5 whitespace-nowrap">
  4222. <span>Interfaces</span>
  4223. </button>
  4224. </div>
  4225. <div class="flex items-center gap-1 text-slate-400 pr-2">
  4226. <button onclick="loadDynamicRouting(true)" title="Refresh" class="hover:text-white p-1">🔄</button>
  4227. </div>
  4228. </div>
  4229. <!-- TAB 1: RADV INSTANCES -->
  4230. <div id="radv-pane-instances" class="flex-1 flex flex-col">
  4231. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  4232. <div class="flex items-center space-x-2">
  4233. <button onclick="openRAdvInstanceModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-blue-600 hover:text-white text-blue-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  4234. <span>+</span> <span>New</span>
  4235. </button>
  4236. <button onclick="enableSelectedRadvInstance()" id="btn-radv-inst-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4237. <span>▶</span> <span>Enable</span>
  4238. </button>
  4239. <button onclick="disableSelectedRadvInstance()" id="btn-radv-inst-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4240. <span>⏸</span> <span>Disable</span>
  4241. </button>
  4242. <button onclick="removeSelectedRadvInstance()" id="btn-radv-inst-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4243. <span>✕</span> <span>Remove</span>
  4244. </button>
  4245. </div>
  4246. <div class="flex items-center space-x-2">
  4247. <input type="text" id="filter-radv-instances" oninput="renderRAdvInstances()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-blue-500 w-32">
  4248. </div>
  4249. </div>
  4250. <div class="flex-1 overflow-x-auto">
  4251. <table class="w-full text-left text-xs font-mono">
  4252. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  4253. <tr>
  4254. <th class="py-2 px-3 w-8 text-center">#</th>
  4255. <th class="py-2 px-3 w-12 text-center">Flags</th>
  4256. <th class="py-2 px-3">Comment</th>
  4257. <th class="py-2 px-3">Name</th>
  4258. <th class="py-2 px-3">Status</th>
  4259. </tr>
  4260. </thead>
  4261. <tbody id="radv-instances-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  4262. </table>
  4263. </div>
  4264. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  4265. <span id="radv-instances-footer-count">0</span>
  4266. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  4267. </div>
  4268. </div>
  4269. <!-- TAB 2: RADV INTERFACES -->
  4270. <div id="radv-pane-interfaces" class="hidden flex-1 flex flex-col">
  4271. <div class="bg-[#101721] border-b border-[#243347] px-3 py-1.5 flex items-center justify-between text-xs select-none">
  4272. <div class="flex items-center space-x-2">
  4273. <button onclick="openRAdvInterfaceModal()" class="px-2.5 py-1 bg-[#1a2737] hover:bg-blue-600 hover:text-white text-blue-400 border border-[#2d425d] rounded transition flex items-center gap-1 font-medium">
  4274. <span>+</span> <span>New</span>
  4275. </button>
  4276. <button onclick="enableSelectedRadvInterface()" id="btn-radv-iface-enable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4277. <span>▶</span> <span>Enable</span>
  4278. </button>
  4279. <button onclick="disableSelectedRadvInterface()" id="btn-radv-iface-disable" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4280. <span>⏸</span> <span>Disable</span>
  4281. </button>
  4282. <button onclick="removeSelectedRadvInterface()" id="btn-radv-iface-remove" disabled class="px-2.5 py-1 bg-[#15202e] text-slate-500 border border-[#233346] rounded flex items-center gap-1 font-medium disabled:opacity-50">
  4283. <span>✕</span> <span>Remove</span>
  4284. </button>
  4285. </div>
  4286. <div class="flex items-center space-x-2">
  4287. <input type="text" id="filter-radv-interfaces" oninput="renderRAdvInterfaces()" placeholder="Find..." class="bg-[#15202e] border border-[#26374d] rounded px-2 py-0.5 text-xs text-white placeholder-slate-500 focus:outline-none focus:border-blue-500 w-32">
  4288. </div>
  4289. </div>
  4290. <div class="flex-1 overflow-x-auto">
  4291. <table class="w-full text-left text-xs font-mono">
  4292. <thead class="text-slate-400 bg-[#0e1622] border-b border-[#243347] text-[11px] font-sans select-none">
  4293. <tr>
  4294. <th class="py-2 px-3 w-8 text-center">#</th>
  4295. <th class="py-2 px-3 w-12 text-center">Flags</th>
  4296. <th class="py-2 px-3">Comment</th>
  4297. <th class="py-2 px-3">Interface</th>
  4298. <th class="py-2 px-3">Min/Max RA (s)</th>
  4299. <th class="py-2 px-3">Lifetime</th>
  4300. <th class="py-2 px-3">Prefixes</th>
  4301. <th class="py-2 px-3">RDNSS (DNS)</th>
  4302. </tr>
  4303. </thead>
  4304. <tbody id="radv-interfaces-table-body" class="divide-y divide-[#1e2c3d]"></tbody>
  4305. </table>
  4306. </div>
  4307. <div class="bg-[#101721] border-t border-[#243347] px-3 py-1.5 flex items-center justify-between text-[11px] font-mono text-slate-400 select-none">
  4308. <span id="radv-interfaces-footer-count">0</span>
  4309. <span class="flex items-center gap-1.5 text-emerald-400"><span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span> ▶ Live</span>
  4310. </div>
  4311. </div>
  4312. </div>
  4313. </section>
  4314. <!-- ================= SECTION: VRRP HIGH AVAILABILITY ================= -->
  4315. <section id="section-vrrp" class="hidden space-y-6">
  4316. <div class="bg-gradient-to-r from-emerald-950/40 via-slate-900 to-slate-900 border border-emerald-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  4317. <div>
  4318. <div class="flex items-center gap-2 mb-1">
  4319. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 tracking-wide uppercase">High Availability</span>
  4320. <h2 class="text-base font-bold text-white flex items-center gap-2">
  4321. <span>VRRP Virtual Routers</span>
  4322. <span class="px-2 py-0.5 text-[10px] rounded-full bg-slate-800 text-slate-300 border border-slate-700 font-mono font-normal">Active / Backup Failover</span>
  4323. </h2>
  4324. </div>
  4325. <p class="text-xs text-slate-400">Virtual Router Redundancy Protocol (VRRPv3/v2) with automated Virtual IP (VIP) failover, preemptive election, priority ranking, and virtual MAC routing.</p>
  4326. </div>
  4327. <div class="flex items-center gap-2">
  4328. <button onclick="openAddVRRPModal()" class="text-xs font-semibold px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20 transition flex items-center gap-1.5">
  4329. <span>+ Add VRRP Instance</span>
  4330. </button>
  4331. <button onclick="loadVRRP(true)" class="text-xs font-semibold px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition" title="Refresh">
  4332. <span>🔄</span>
  4333. </button>
  4334. </div>
  4335. </div>
  4336. <!-- KPI Summary Cards -->
  4337. <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
  4338. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  4339. <div class="text-xs text-slate-400">Total VRRP Routers</div>
  4340. <div id="vrrp-stat-total" class="text-xl font-bold text-white font-mono mt-1">0</div>
  4341. </div>
  4342. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  4343. <div class="text-xs text-slate-400">Master Instances (Active VIP)</div>
  4344. <div id="vrrp-stat-master" class="text-xl font-bold text-emerald-400 font-mono mt-1">0</div>
  4345. <div class="text-[10px] text-slate-500 mt-0.5">Holding virtual IP gateways</div>
  4346. </div>
  4347. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  4348. <div class="text-xs text-slate-400">Backup Instances (Standby)</div>
  4349. <div id="vrrp-stat-backup" class="text-xl font-bold text-sky-400 font-mono mt-1">0</div>
  4350. <div class="text-[10px] text-slate-500 mt-0.5">Listening for heartbeats</div>
  4351. </div>
  4352. <div class="bg-slate-900 border border-slate-800 rounded-xl p-4">
  4353. <div class="text-xs text-slate-400">Redundancy Protocol</div>
  4354. <div class="text-xl font-bold text-amber-300 font-mono mt-1">VRRPv3</div>
  4355. <div class="text-[10px] text-slate-500 mt-0.5">keepalived HA engine</div>
  4356. </div>
  4357. </div>
  4358. <!-- VRRP Table View -->
  4359. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  4360. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  4361. <div>
  4362. <h3 class="text-xs font-bold text-white">Configured VRRP Instances</h3>
  4363. <p class="text-[11px] text-slate-400 mt-0.5">Virtual router instances, priority weights, virtual IP addresses and live election states</p>
  4364. </div>
  4365. <div class="text-xs text-slate-400 font-mono">
  4366. <span class="text-emerald-400 font-bold">M</span> = Master &nbsp;|&nbsp; <span class="text-sky-400 font-bold">B</span> = Backup &nbsp;|&nbsp; <span class="text-rose-400 font-bold">X</span> = Disabled
  4367. </div>
  4368. </div>
  4369. <div class="overflow-x-auto">
  4370. <table class="w-full text-left text-xs font-mono">
  4371. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  4372. <tr>
  4373. <th class="py-2.5 px-3 w-12 text-center">Flag</th>
  4374. <th class="py-2.5 px-3">Name</th>
  4375. <th class="py-2.5 px-3">Interface</th>
  4376. <th class="py-2.5 px-3 text-center">VRID</th>
  4377. <th class="py-2.5 px-3 text-center">Priority</th>
  4378. <th class="py-2.5 px-3">Virtual IP (VIP)</th>
  4379. <th class="py-2.5 px-3 text-center">Preempt</th>
  4380. <th class="py-2.5 px-3">Virtual MAC</th>
  4381. <th class="py-2.5 px-3 text-center">Live State</th>
  4382. <th class="py-2.5 px-3">Comment</th>
  4383. <th class="py-2.5 px-3 text-right font-sans">Actions</th>
  4384. </tr>
  4385. </thead>
  4386. <tbody id="vrrp-table-tbody" class="divide-y divide-slate-800/80"></tbody>
  4387. </table>
  4388. </div>
  4389. <div id="vrrp-table-empty" class="hidden p-10 text-center text-slate-500 text-xs font-sans">
  4390. No VRRP instances configured. Click "+ Add VRRP Instance" to configure high availability redundancy.
  4391. </div>
  4392. </div>
  4393. </section>
  4394. <!-- ================= SECTION: TROUBLESHOOT TOOLS ================= -->
  4395. <section id="section-tools" class="hidden space-y-6">
  4396. <!-- Top Banner -->
  4397. <div class="bg-gradient-to-r from-amber-950/40 via-slate-900 to-slate-900 border border-amber-900/30 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  4398. <div>
  4399. <div class="flex items-center gap-2 mb-1">
  4400. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-amber-500/20 text-amber-300 border border-amber-500/30 tracking-wide uppercase">Diagnostics</span>
  4401. <h2 class="text-base font-bold text-white">Troubleshoot & Diagnostic Tools</h2>
  4402. </div>
  4403. <p class="text-xs text-slate-400">Comprehensive network diagnostic suite: ICMP echo latency, path route tracing, continuous MTR telemetry, DNS resolution, and TCP port probing.</p>
  4404. </div>
  4405. </div>
  4406. <!-- Tools Subtabs Navigation -->
  4407. <div class="flex items-center gap-1 border-b border-slate-800 pb-0 overflow-x-auto">
  4408. <button id="tools-subtab-btn-ping" onclick="switchToolsSubTab('ping')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-amber-400 text-amber-300 transition flex items-center gap-2 whitespace-nowrap">
  4409. <span>Ping (ICMP)</span>
  4410. </button>
  4411. <button id="tools-subtab-btn-traceroute" onclick="switchToolsSubTab('traceroute')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  4412. <span>Traceroute</span>
  4413. </button>
  4414. <button id="tools-subtab-btn-mtr" onclick="switchToolsSubTab('mtr')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  4415. <span>MTR Telemetry</span>
  4416. </button>
  4417. <button id="tools-subtab-btn-dns" onclick="switchToolsSubTab('dns')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  4418. <span>DNS Resolution</span>
  4419. </button>
  4420. <button id="tools-subtab-btn-port" onclick="switchToolsSubTab('port')" class="px-4 py-2.5 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition flex items-center gap-2 whitespace-nowrap">
  4421. <span>Port Probe (TCP Check)</span>
  4422. </button>
  4423. </div>
  4424. <!-- ================= SUBTAB: PING ================= -->
  4425. <div id="tools-view-ping" class="space-y-5">
  4426. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5">
  4427. <form id="tools-ping-form" onsubmit="executePing(event)" class="space-y-4">
  4428. <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
  4429. <div class="md:col-span-2">
  4430. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Target Host or IP *</label>
  4431. <input type="text" id="tools-ping-target" required placeholder="e.g. 8.8.8.8, 1.1.1.1, google.com" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4432. </div>
  4433. <div>
  4434. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Count</label>
  4435. <select id="tools-ping-count" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4436. <option value="0">Continuous (Until Stopped)</option>
  4437. <option value="4" selected>4 packets</option>
  4438. <option value="5">5 packets</option>
  4439. <option value="10">10 packets</option>
  4440. <option value="20">20 packets</option>
  4441. <option value="50">50 packets</option>
  4442. <option value="100">100 packets</option>
  4443. </select>
  4444. </div>
  4445. <div>
  4446. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Source Interface</label>
  4447. <select id="tools-ping-iface" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4448. <option value="">Auto (Default Route)</option>
  4449. </select>
  4450. </div>
  4451. </div>
  4452. <div class="flex flex-wrap items-center justify-between gap-4 pt-2">
  4453. <div class="flex items-center space-x-6 text-xs">
  4454. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  4455. <input type="checkbox" id="tools-ping-df" class="rounded text-amber-500 bg-slate-800 border-slate-700">
  4456. <span>Don't Fragment (DF bit)</span>
  4457. </label>
  4458. <div class="flex items-center space-x-2">
  4459. <span class="text-slate-400">Size (bytes):</span>
  4460. <input type="number" id="tools-ping-size" value="56" min="1" max="65500" class="w-20 bg-slate-800 border border-slate-700 rounded px-2 py-1 text-xs text-white font-mono">
  4461. </div>
  4462. </div>
  4463. <button type="submit" id="tools-ping-btn" class="px-5 py-2 rounded-lg font-semibold text-xs bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-lg shadow-amber-500/20 transition flex items-center gap-2">
  4464. <span id="tools-ping-spinner" class="hidden animate-spin">↻</span>
  4465. <span id="tools-ping-btn-text">Start Ping</span>
  4466. </button>
  4467. </div>
  4468. </form>
  4469. </div>
  4470. <!-- Ping Results Container -->
  4471. <div id="tools-ping-results" class="hidden space-y-4">
  4472. <!-- KPI Summary Cards -->
  4473. <div class="grid grid-cols-2 md:grid-cols-5 gap-3">
  4474. <div class="bg-slate-900 border border-slate-800 rounded-xl p-3 text-center">
  4475. <div class="text-[10px] text-slate-400 uppercase">Packets Sent</div>
  4476. <div id="tools-ping-stat-sent" class="text-base font-bold text-white font-mono mt-1">0</div>
  4477. </div>
  4478. <div class="bg-slate-900 border border-slate-800 rounded-xl p-3 text-center">
  4479. <div class="text-[10px] text-slate-400 uppercase">Received</div>
  4480. <div id="tools-ping-stat-recv" class="text-base font-bold text-emerald-400 font-mono mt-1">0</div>
  4481. </div>
  4482. <div class="bg-slate-900 border border-slate-800 rounded-xl p-3 text-center">
  4483. <div class="text-[10px] text-slate-400 uppercase">Packet Loss</div>
  4484. <div id="tools-ping-stat-loss" class="text-base font-bold font-mono mt-1">0%</div>
  4485. </div>
  4486. <div class="bg-slate-900 border border-slate-800 rounded-xl p-3 text-center">
  4487. <div class="text-[10px] text-slate-400 uppercase">Avg RTT</div>
  4488. <div id="tools-ping-stat-avg" class="text-base font-bold text-cyan-400 font-mono mt-1">0 ms</div>
  4489. </div>
  4490. <div class="bg-slate-900 border border-slate-800 rounded-xl p-3 text-center">
  4491. <div class="text-[10px] text-slate-400 uppercase">Min / Max</div>
  4492. <div id="tools-ping-stat-minmax" class="text-xs font-bold text-purple-300 font-mono mt-1.5">-</div>
  4493. </div>
  4494. </div>
  4495. <!-- Replies Table -->
  4496. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  4497. <div class="p-3 border-b border-slate-800 flex items-center justify-between">
  4498. <div class="flex items-center gap-2.5">
  4499. <span class="text-xs font-bold text-white">ICMP Echo Replies</span>
  4500. <span id="tools-ping-status-badge" class="px-2 py-0.5 rounded text-[10px] font-mono font-semibold"></span>
  4501. </div>
  4502. <button type="button" onclick="clearPingTable()" class="text-[11px] text-slate-400 hover:text-white px-2 py-0.5 rounded bg-slate-800/60 hover:bg-slate-800 border border-slate-700/60 transition">Clear</button>
  4503. </div>
  4504. <div class="overflow-x-auto">
  4505. <table class="w-full text-left text-xs font-mono">
  4506. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  4507. <tr>
  4508. <th class="py-2 px-3"># Seq</th>
  4509. <th class="py-2 px-3">Responder</th>
  4510. <th class="py-2 px-3">Bytes</th>
  4511. <th class="py-2 px-3">TTL</th>
  4512. <th class="py-2 px-3">Latency (RTT)</th>
  4513. <th class="py-2 px-3 text-right">Status</th>
  4514. </tr>
  4515. </thead>
  4516. <tbody id="tools-ping-tbody" class="divide-y divide-slate-800/80"></tbody>
  4517. </table>
  4518. </div>
  4519. </div>
  4520. <!-- Raw CLI Drawer -->
  4521. <details class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3 text-xs">
  4522. <summary class="cursor-pointer text-slate-400 font-medium hover:text-white">Raw CLI Console Output</summary>
  4523. <pre id="tools-ping-raw" class="mt-2 bg-slate-950 p-3 rounded font-mono text-[11px] text-slate-300 overflow-x-auto border border-slate-800 leading-relaxed"></pre>
  4524. </details>
  4525. </div>
  4526. </div>
  4527. <!-- ================= SUBTAB: TRACEROUTE ================= -->
  4528. <div id="tools-view-traceroute" class="hidden space-y-5">
  4529. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5">
  4530. <form id="tools-traceroute-form" onsubmit="executeTraceroute(event)" class="space-y-4">
  4531. <div class="grid grid-cols-1 md:grid-cols-5 gap-4">
  4532. <div class="md:col-span-2">
  4533. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Target Host or IP *</label>
  4534. <input type="text" id="tools-tr-target" required placeholder="e.g. 1.1.1.1 or google.com" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4535. </div>
  4536. <div>
  4537. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Max Hops</label>
  4538. <input type="number" id="tools-tr-maxhops" value="15" min="1" max="30" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4539. </div>
  4540. <div>
  4541. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Protocol</label>
  4542. <select id="tools-tr-proto" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4543. <option value="icmp" selected>ICMP Echo (-I)</option>
  4544. <option value="udp">UDP Probe</option>
  4545. </select>
  4546. </div>
  4547. <div>
  4548. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Source Interface</label>
  4549. <select id="tools-tr-iface" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4550. <option value="">Auto (Default Route)</option>
  4551. </select>
  4552. </div>
  4553. </div>
  4554. <div class="flex items-center justify-between pt-2">
  4555. <label class="flex items-center space-x-2 text-xs text-slate-300 cursor-pointer">
  4556. <input type="checkbox" id="tools-tr-resolve" class="rounded text-amber-500 bg-slate-800 border-slate-700">
  4557. <span>Resolve Hostnames (Reverse DNS)</span>
  4558. </label>
  4559. <button type="submit" id="tools-tr-btn" class="px-5 py-2 rounded-lg font-semibold text-xs bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-lg shadow-amber-500/20 transition flex items-center gap-2">
  4560. <span id="tools-tr-spinner" class="hidden animate-spin">↻</span>
  4561. <span id="tools-tr-btn-text">Start Traceroute</span>
  4562. </button>
  4563. </div>
  4564. </form>
  4565. </div>
  4566. <div id="tools-tr-results" class="hidden space-y-4">
  4567. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  4568. <div class="p-3 border-b border-slate-800 flex items-center justify-between">
  4569. <div class="flex items-center gap-2.5">
  4570. <span class="text-xs font-bold text-white">Route Hops</span>
  4571. <span id="tools-tr-hops-count" class="text-xs font-mono text-slate-400">0 Hops</span>
  4572. </div>
  4573. <button type="button" onclick="clearTracerouteTable()" class="text-[11px] text-slate-400 hover:text-white px-2 py-0.5 rounded bg-slate-800/60 hover:bg-slate-800 border border-slate-700/60 transition">Clear</button>
  4574. </div>
  4575. <div class="overflow-x-auto">
  4576. <table class="w-full text-left text-xs font-mono">
  4577. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  4578. <tr>
  4579. <th class="py-2.5 px-3 w-16">Hop #</th>
  4580. <th class="py-2.5 px-3">Gateway Host / IP</th>
  4581. <th class="py-2.5 px-3">RTT 1</th>
  4582. <th class="py-2.5 px-3">RTT 2</th>
  4583. <th class="py-2.5 px-3 text-right">Status</th>
  4584. </tr>
  4585. </thead>
  4586. <tbody id="tools-tr-tbody" class="divide-y divide-slate-800/80"></tbody>
  4587. </table>
  4588. </div>
  4589. </div>
  4590. <details class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3 text-xs">
  4591. <summary class="cursor-pointer text-slate-400 font-medium hover:text-white">Raw CLI Console Output</summary>
  4592. <pre id="tools-tr-raw" class="mt-2 bg-slate-950 p-3 rounded font-mono text-[11px] text-slate-300 overflow-x-auto border border-slate-800 leading-relaxed"></pre>
  4593. </details>
  4594. </div>
  4595. </div>
  4596. <!-- ================= SUBTAB: MTR ================= -->
  4597. <div id="tools-view-mtr" class="hidden space-y-5">
  4598. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5">
  4599. <form id="tools-mtr-form" onsubmit="executeMTR(event)" class="space-y-4">
  4600. <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
  4601. <div class="md:col-span-2">
  4602. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Target Host or IP *</label>
  4603. <input type="text" id="tools-mtr-target" required placeholder="e.g. 1.1.1.1 or 8.8.8.8" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4604. </div>
  4605. <div>
  4606. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Cycle Count</label>
  4607. <select id="tools-mtr-count" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4608. <option value="2">2 cycles (Fast)</option>
  4609. <option value="3" selected>3 cycles</option>
  4610. <option value="5">5 cycles</option>
  4611. <option value="10">10 cycles (Deep)</option>
  4612. </select>
  4613. </div>
  4614. </div>
  4615. <div class="flex justify-end pt-2">
  4616. <button type="submit" id="tools-mtr-btn" class="px-5 py-2 rounded-lg font-semibold text-xs bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-lg shadow-amber-500/20 transition flex items-center gap-2">
  4617. <span id="tools-mtr-spinner" class="hidden">↻</span>
  4618. <span>Start MTR Report</span>
  4619. </button>
  4620. </div>
  4621. </form>
  4622. </div>
  4623. <div id="tools-mtr-results" class="hidden space-y-4">
  4624. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  4625. <div class="p-3 border-b border-slate-800 flex items-center justify-between">
  4626. <span class="text-xs font-bold text-white">MTR Route Telemetry</span>
  4627. <span id="tools-mtr-hops-count" class="text-xs font-mono text-slate-400">0 Hops</span>
  4628. </div>
  4629. <div class="overflow-x-auto">
  4630. <table class="w-full text-left text-xs font-mono">
  4631. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  4632. <tr>
  4633. <th class="py-2.5 px-3 w-16">Hop #</th>
  4634. <th class="py-2.5 px-3">Host / Router</th>
  4635. <th class="py-2.5 px-3">Loss %</th>
  4636. <th class="py-2.5 px-3">Sent</th>
  4637. <th class="py-2.5 px-3">Last (ms)</th>
  4638. <th class="py-2.5 px-3">Avg (ms)</th>
  4639. <th class="py-2.5 px-3">Best (ms)</th>
  4640. <th class="py-2.5 px-3">Worst (ms)</th>
  4641. <th class="py-2.5 px-3">StDev</th>
  4642. </tr>
  4643. </thead>
  4644. <tbody id="tools-mtr-tbody" class="divide-y divide-slate-800/80"></tbody>
  4645. </table>
  4646. </div>
  4647. </div>
  4648. <details class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3 text-xs">
  4649. <summary class="cursor-pointer text-slate-400 font-medium hover:text-white">Raw CLI Console Output</summary>
  4650. <pre id="tools-mtr-raw" class="mt-2 bg-slate-950 p-3 rounded font-mono text-[11px] text-slate-300 overflow-x-auto border border-slate-800 leading-relaxed"></pre>
  4651. </details>
  4652. </div>
  4653. </div>
  4654. <!-- ================= SUBTAB: DNS LOOKUP ================= -->
  4655. <div id="tools-view-dns" class="hidden space-y-5">
  4656. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5">
  4657. <form id="tools-dns-form" onsubmit="executeDNS(event)" class="space-y-4">
  4658. <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
  4659. <div class="md:col-span-2">
  4660. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Domain Name *</label>
  4661. <input type="text" id="tools-dns-domain" required placeholder="e.g. cloudflare.com, google.com" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4662. </div>
  4663. <div>
  4664. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Record Type</label>
  4665. <select id="tools-dns-type" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4666. <option value="A" selected>A (IPv4)</option>
  4667. <option value="AAAA">AAAA (IPv6)</option>
  4668. <option value="CNAME">CNAME</option>
  4669. <option value="MX">MX (Mail)</option>
  4670. <option value="TXT">TXT</option>
  4671. <option value="NS">NS (Nameserver)</option>
  4672. <option value="SOA">SOA</option>
  4673. <option value="ANY">ANY</option>
  4674. </select>
  4675. </div>
  4676. <div>
  4677. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Custom DNS Server (Optional)</label>
  4678. <input type="text" id="tools-dns-server" placeholder="e.g. 1.1.1.1, 8.8.8.8" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4679. </div>
  4680. </div>
  4681. <div class="flex justify-end pt-2">
  4682. <button type="submit" id="tools-dns-btn" class="px-5 py-2 rounded-lg font-semibold text-xs bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-lg shadow-amber-500/20 transition flex items-center gap-2">
  4683. <span id="tools-dns-spinner" class="hidden">↻</span>
  4684. <span>Resolve Domain</span>
  4685. </button>
  4686. </div>
  4687. </form>
  4688. </div>
  4689. <div id="tools-dns-results" class="hidden space-y-4">
  4690. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  4691. <div class="p-3 border-b border-slate-800 flex items-center justify-between">
  4692. <div class="flex items-center gap-2">
  4693. <span class="text-xs font-bold text-white">DNS Answer Records</span>
  4694. <span id="tools-dns-meta" class="text-[11px] font-mono text-cyan-300"></span>
  4695. </div>
  4696. </div>
  4697. <div class="overflow-x-auto">
  4698. <table class="w-full text-left text-xs font-mono">
  4699. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  4700. <tr>
  4701. <th class="py-2.5 px-3">Name</th>
  4702. <th class="py-2.5 px-3">TTL (s)</th>
  4703. <th class="py-2.5 px-3">Class</th>
  4704. <th class="py-2.5 px-3">Type</th>
  4705. <th class="py-2.5 px-3">Value / Target</th>
  4706. </tr>
  4707. </thead>
  4708. <tbody id="tools-dns-tbody" class="divide-y divide-slate-800/80"></tbody>
  4709. </table>
  4710. </div>
  4711. </div>
  4712. <details class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3 text-xs">
  4713. <summary class="cursor-pointer text-slate-400 font-medium hover:text-white">Raw CLI Console Output</summary>
  4714. <pre id="tools-dns-raw" class="mt-2 bg-slate-950 p-3 rounded font-mono text-[11px] text-slate-300 overflow-x-auto border border-slate-800 leading-relaxed"></pre>
  4715. </details>
  4716. </div>
  4717. </div>
  4718. <!-- ================= SUBTAB: PORT PROBE ================= -->
  4719. <div id="tools-view-port" class="hidden space-y-5">
  4720. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5">
  4721. <form id="tools-port-form" onsubmit="executePortProbe(event)" class="space-y-4">
  4722. <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
  4723. <div class="md:col-span-2">
  4724. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Target Host or IP *</label>
  4725. <input type="text" id="tools-port-target" required placeholder="e.g. 1.1.1.1, 192.168.131.1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4726. </div>
  4727. <div>
  4728. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Port (TCP) *</label>
  4729. <input type="number" id="tools-port-num" required min="1" max="65535" value="80" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4730. </div>
  4731. <div>
  4732. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Timeout (s)</label>
  4733. <input type="number" id="tools-port-timeout" value="3" min="1" max="10" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  4734. </div>
  4735. </div>
  4736. <!-- Quick Presets -->
  4737. <div class="flex flex-wrap items-center gap-2 pt-1">
  4738. <span class="text-[11px] text-slate-400">Quick Ports:</span>
  4739. <button type="button" onclick="setPortPreset(80)" class="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-[11px] font-mono text-cyan-300 border border-slate-700">HTTP: 80</button>
  4740. <button type="button" onclick="setPortPreset(443)" class="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-[11px] font-mono text-emerald-300 border border-slate-700">HTTPS: 443</button>
  4741. <button type="button" onclick="setPortPreset(22)" class="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-[11px] font-mono text-purple-300 border border-slate-700">SSH: 22</button>
  4742. <button type="button" onclick="setPortPreset(53)" class="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-[11px] font-mono text-indigo-300 border border-slate-700">DNS: 53</button>
  4743. <button type="button" onclick="setPortPreset(179)" class="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-[11px] font-mono text-amber-300 border border-slate-700">BGP: 179</button>
  4744. <button type="button" onclick="setPortPreset(8291)" class="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-[11px] font-mono text-pink-300 border border-slate-700">Remote Mgmt: 8291</button>
  4745. <button type="button" onclick="setPortPreset(8006)" class="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-[11px] font-mono text-orange-300 border border-slate-700">PVE: 8006</button>
  4746. </div>
  4747. <div class="flex justify-end pt-2">
  4748. <button type="submit" id="tools-port-btn" class="px-5 py-2 rounded-lg font-semibold text-xs bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-lg shadow-amber-500/20 transition flex items-center gap-2">
  4749. <span id="tools-port-spinner" class="hidden">↻</span>
  4750. <span>Probe Port</span>
  4751. </button>
  4752. </div>
  4753. </form>
  4754. </div>
  4755. <div id="tools-port-results" class="hidden space-y-4">
  4756. <div id="tools-port-banner" class="rounded-xl p-5 border flex items-center justify-between">
  4757. <div class="flex items-center gap-3">
  4758. <span id="tools-port-icon" class="w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm"></span>
  4759. <div>
  4760. <h3 id="tools-port-title" class="text-sm font-bold"></h3>
  4761. <p id="tools-port-sub" class="text-xs mt-0.5 font-mono"></p>
  4762. </div>
  4763. </div>
  4764. <div class="text-right">
  4765. <div class="text-[10px] uppercase text-slate-400">Handshake Latency</div>
  4766. <div id="tools-port-latency" class="text-base font-bold font-mono text-white"></div>
  4767. </div>
  4768. </div>
  4769. <details class="bg-slate-900/60 border border-slate-800/80 rounded-xl p-3 text-xs">
  4770. <summary class="cursor-pointer text-slate-400 font-medium hover:text-white">Handshake Details</summary>
  4771. <pre id="tools-port-raw" class="mt-2 bg-slate-950 p-3 rounded font-mono text-[11px] text-slate-300 overflow-x-auto border border-slate-800 leading-relaxed"></pre>
  4772. </details>
  4773. </div>
  4774. </div>
  4775. </section>
  4776. <!-- ================= SECTION: BANDWIDTH TEST (BwTest) ================= -->
  4777. <section id="section-bwtest" class="hidden space-y-6">
  4778. <!-- Top Banner -->
  4779. <div class="bg-gradient-to-r from-emerald-950/50 via-slate-900 to-slate-900 border border-emerald-800/40 rounded-xl p-5 flex flex-wrap items-center justify-between gap-4">
  4780. <div>
  4781. <div class="flex items-center gap-2 mb-1">
  4782. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 tracking-wide uppercase">Throughput Benchmark</span>
  4783. <h2 class="text-base font-bold text-white flex items-center gap-2">
  4784. <span>Bandwidth Test Engine</span>
  4785. <span class="px-2 py-0.5 text-[10px] rounded-full bg-emerald-500/20 text-emerald-300 border border-emerald-500/40 font-mono font-normal">UDP & TCP Line-Rate</span>
  4786. </h2>
  4787. </div>
  4788. <p class="text-xs text-slate-400">High-performance network throughput benchmark with line-rate packet generation, token-bucket pacing, sliding-window RFC 3550 jitter telemetry, and bidirectional stream testing.</p>
  4789. </div>
  4790. <!-- Server Status & Controls -->
  4791. <div class="flex items-center gap-3">
  4792. <div class="flex items-center gap-2 bg-slate-950/80 px-3 py-1.5 rounded-lg border border-slate-800 text-xs font-mono">
  4793. <span id="bwtest-server-indicator" class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span>
  4794. <span class="text-slate-400">Server:</span>
  4795. <span id="bwtest-server-badge" class="text-emerald-400 font-bold">Port 2000 (Active)</span>
  4796. </div>
  4797. <button onclick="openBwTestServerModal()" class="text-xs font-semibold px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition flex items-center gap-1.5">
  4798. <svg class="w-3.5 h-3.5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/></svg>
  4799. <span>Server Settings</span>
  4800. </button>
  4801. </div>
  4802. </div>
  4803. <!-- 4 Top KPI Cards (Live Gauges) -->
  4804. <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
  4805. <!-- TX Card -->
  4806. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  4807. <div>
  4808. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider flex items-center gap-1.5">
  4809. <span class="w-2 h-2 rounded-full bg-cyan-400"></span>
  4810. <span>Upload (TX)</span>
  4811. </div>
  4812. <div class="flex items-baseline gap-1 mt-1 font-mono">
  4813. <span id="bwtest-stat-tx-mbps" title="Throughput confirmed by the receiving peer" class="text-2xl font-bold text-cyan-400">0.00</span>
  4814. <span class="text-xs text-slate-400">Mbps</span>
  4815. </div>
  4816. <div class="text-[10px] text-slate-500 mt-0.5 font-mono">
  4817. Peak: <span id="bwtest-stat-tx-peak" class="text-cyan-300 font-semibold">0.00 Mbps</span> • <span id="bwtest-stat-tx-pkts">0</span> pkts
  4818. </div>
  4819. </div>
  4820. <div class="w-10 h-10 rounded-xl bg-cyan-500/10 border border-cyan-500/20 flex items-center justify-center text-cyan-400">
  4821. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"/></svg>
  4822. </div>
  4823. </div>
  4824. <!-- RX Card -->
  4825. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  4826. <div>
  4827. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider flex items-center gap-1.5">
  4828. <span class="w-2 h-2 rounded-full bg-purple-400"></span>
  4829. <span>Download (RX)</span>
  4830. </div>
  4831. <div class="flex items-baseline gap-1 mt-1 font-mono">
  4832. <span id="bwtest-stat-rx-mbps" class="text-2xl font-bold text-purple-400">0.00</span>
  4833. <span class="text-xs text-slate-400">Mbps</span>
  4834. </div>
  4835. <div class="text-[10px] text-slate-500 mt-0.5 font-mono">
  4836. Peak: <span id="bwtest-stat-rx-peak" class="text-purple-300 font-semibold">0.00 Mbps</span> • <span id="bwtest-stat-rx-pkts">0</span> pkts
  4837. </div>
  4838. </div>
  4839. <div class="w-10 h-10 rounded-xl bg-purple-500/10 border border-purple-500/20 flex items-center justify-center text-purple-400">
  4840. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"/></svg>
  4841. </div>
  4842. </div>
  4843. <!-- Packet Loss & Jitter -->
  4844. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  4845. <div>
  4846. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Loss & Jitter</div>
  4847. <div class="flex items-baseline gap-2 mt-1 font-mono">
  4848. <span id="bwtest-stat-loss" class="text-2xl font-bold text-emerald-400">0.0%</span>
  4849. <span class="text-xs text-slate-400">(0 lost)</span>
  4850. </div>
  4851. <div class="text-[10px] text-slate-500 mt-0.5 font-mono">
  4852. Jitter: <span id="bwtest-stat-jitter" class="text-slate-300 font-semibold">0.00 ms</span>
  4853. </div>
  4854. </div>
  4855. <div class="w-10 h-10 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400">
  4856. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
  4857. </div>
  4858. </div>
  4859. <!-- Time & Transferred Data -->
  4860. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  4861. <div>
  4862. <div class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Elapsed & Volume</div>
  4863. <div class="flex items-baseline gap-1 mt-1 font-mono">
  4864. <span id="bwtest-stat-time" class="text-2xl font-bold text-white">0.0s</span>
  4865. <span id="bwtest-stat-target-dur" class="text-xs text-slate-400">/ 10s</span>
  4866. </div>
  4867. <div class="text-[10px] text-slate-500 mt-0.5 font-mono">
  4868. Data: <span id="bwtest-stat-data" class="text-slate-300 font-semibold">0.0 MB</span>
  4869. </div>
  4870. </div>
  4871. <div class="w-10 h-10 rounded-xl bg-slate-800 border border-slate-700 flex items-center justify-center text-slate-300">
  4872. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
  4873. </div>
  4874. </div>
  4875. </div>
  4876. <!-- Main Grid: Benchmark Controls + Live Telemetry Graph -->
  4877. <div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
  4878. <!-- Left Panel: Test Setup Form (5 cols) -->
  4879. <div class="lg:col-span-5 bg-slate-900/90 border border-slate-800 rounded-xl p-5 space-y-4">
  4880. <div class="border-b border-slate-800 pb-3 flex items-center justify-between">
  4881. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  4882. <span>Test Parameters</span>
  4883. </h3>
  4884. <span id="bwtest-run-status-badge" class="px-2 py-0.5 rounded text-[10px] font-mono bg-slate-800 text-slate-400 border border-slate-700 font-semibold">IDLE</span>
  4885. </div>
  4886. <form id="bwtest-form" onsubmit="startBwTest(event)" class="space-y-4">
  4887. <!-- Target Host & Port -->
  4888. <div>
  4889. <div class="flex justify-between items-center mb-1">
  4890. <label class="block text-xs font-semibold text-slate-300">Target IP or Hostname</label>
  4891. <div class="flex gap-1 text-[10px] font-mono">
  4892. <button type="button" onclick="setBwTestTarget('127.0.0.1')" class="px-1.5 py-0.5 bg-slate-800 hover:bg-slate-700 text-cyan-400 rounded transition border border-slate-700">127.0.0.1</button>
  4893. <button type="button" onclick="setBwTestTarget('192.168.131.56')" class="px-1.5 py-0.5 bg-slate-800 hover:bg-slate-700 text-emerald-400 rounded transition border border-slate-700">eth0</button>
  4894. </div>
  4895. </div>
  4896. <div class="grid grid-cols-3 gap-2">
  4897. <div class="col-span-2">
  4898. <input type="text" id="bwtest-input-target" required value="127.0.0.1" placeholder="e.g. 192.168.88.1 or router.lan" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-emerald-500">
  4899. </div>
  4900. <div>
  4901. <input type="number" id="bwtest-input-port" required value="2000" min="1" max="65535" placeholder="Port" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-emerald-500" title="Control Port (default 2000)">
  4902. </div>
  4903. </div>
  4904. </div>
  4905. <!-- Protocol Selection (Segmented Selector) -->
  4906. <div>
  4907. <div class="flex justify-between items-center mb-1.5">
  4908. <label class="block text-xs font-semibold text-white flex items-center gap-1.5">
  4909. <svg class="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
  4910. <span>Transport Protocol</span>
  4911. </label>
  4912. <span id="bwtest-proto-badge" class="text-[10px] font-mono text-emerald-400 bg-emerald-500/10 px-2 py-0.5 rounded border border-emerald-500/20">UDP Mode (RFC 3550 Jitter + Loss %)</span>
  4913. </div>
  4914. <div class="grid grid-cols-2 gap-2">
  4915. <button type="button" id="bwtest-proto-udp" onclick="setBwTestProtocol('udp')" class="py-2 px-3 rounded-lg text-xs font-bold border transition text-center bg-emerald-600/20 border-emerald-500/50 text-emerald-300 shadow-sm flex items-center justify-center gap-2">
  4916. <span class="w-2 h-2 rounded-full bg-emerald-400"></span>
  4917. <span>UDP (Loss & Jitter)</span>
  4918. </button>
  4919. <button type="button" id="bwtest-proto-tcp" onclick="setBwTestProtocol('tcp')" class="py-2 px-3 rounded-lg text-xs font-medium border border-slate-800 bg-slate-950 text-slate-400 hover:text-white transition text-center flex items-center justify-center gap-2">
  4920. <span class="w-2 h-2 rounded-full bg-slate-600"></span>
  4921. <span>TCP (Stream Throughput)</span>
  4922. </button>
  4923. </div>
  4924. <input type="hidden" id="bwtest-input-protocol" value="udp">
  4925. </div>
  4926. <!-- Direction (Both / TX / RX) -->
  4927. <div>
  4928. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Traffic Direction</label>
  4929. <div class="grid grid-cols-3 gap-2">
  4930. <button type="button" id="bwtest-dir-both" onclick="setBwTestDirection('both')" class="py-2 px-2 rounded-lg text-xs font-bold border transition text-center bg-emerald-600/20 border-emerald-500/50 text-emerald-300 shadow-sm">
  4931. Both (Full Duplex)
  4932. </button>
  4933. <button type="button" id="bwtest-dir-tx" onclick="setBwTestDirection('tx')" class="py-2 px-2 rounded-lg text-xs font-medium border border-slate-800 bg-slate-950 text-slate-400 hover:text-white transition text-center">
  4934. Transmit (TX)
  4935. </button>
  4936. <button type="button" id="bwtest-dir-rx" onclick="setBwTestDirection('rx')" class="py-2 px-2 rounded-lg text-xs font-medium border border-slate-800 bg-slate-950 text-slate-400 hover:text-white transition text-center">
  4937. Receive (RX)
  4938. </button>
  4939. </div>
  4940. <input type="hidden" id="bwtest-input-direction" value="both">
  4941. </div>
  4942. <!-- Protocol Options: Packet Size (UDP) & Parallel Streams (TCP) -->
  4943. <div class="grid grid-cols-2 gap-3">
  4944. <div id="bwtest-field-pktsize">
  4945. <label class="block text-xs font-semibold text-slate-300 mb-1">Maximum UDP Datagram Size</label>
  4946. <div id="bwtest-effective-payload" class="text-[10px] text-slate-400 mb-1">Negotiated to fit the route MTU</div>
  4947. <select id="bwtest-input-pktsize" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-emerald-500">
  4948. <option value="1472" selected>1472 bytes (1500-byte IP packet)</option>
  4949. <option value="8972">8972 bytes (9000-byte IP packet)</option>
  4950. <option value="512">512 (Small Payload)</option>
  4951. <option value="64">64 (Min VoIP / Small)</option>
  4952. </select>
  4953. </div>
  4954. <div id="bwtest-field-streams">
  4955. <label class="block text-xs font-semibold text-slate-300 mb-1">Parallel Streams (TCP/UDP)</label>
  4956. <select id="bwtest-input-streams" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-emerald-500">
  4957. <option value="1" selected>1 Stream</option>
  4958. <option value="2">2 Streams</option>
  4959. <option value="4">4 Streams</option>
  4960. <option value="8">8 Streams</option>
  4961. </select>
  4962. </div>
  4963. </div>
  4964. <!-- Bandwidth Limit -->
  4965. <div>
  4966. <div class="flex justify-between items-center mb-1">
  4967. <label class="block text-xs font-semibold text-slate-300">Bandwidth Target Limit</label>
  4968. <div class="flex gap-1 text-[10px] font-mono">
  4969. <button type="button" onclick="setBwTestLimit(0)" class="px-1.5 py-0.5 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded transition border border-slate-700">Auto</button>
  4970. <button type="button" onclick="setBwTestLimit(50000000)" class="px-1.5 py-0.5 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded transition border border-slate-700">50M</button>
  4971. <button type="button" onclick="setBwTestLimit(100000000)" class="px-1.5 py-0.5 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded transition border border-slate-700">100M</button>
  4972. <button type="button" onclick="setBwTestLimit(1000000000)" class="px-1.5 py-0.5 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded transition border border-slate-700">1G</button>
  4973. </div>
  4974. </div>
  4975. <select id="bwtest-input-limit" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-emerald-500">
  4976. <option value="0" selected>Auto (UDP uses receiver feedback)</option>
  4977. <option value="10000000">10 Mbps</option>
  4978. <option value="50000000">50 Mbps</option>
  4979. <option value="100000000">100 Mbps</option>
  4980. <option value="250000000">250 Mbps</option>
  4981. <option value="500000000">500 Mbps</option>
  4982. <option value="1000000000">1 Gbps</option>
  4983. </select>
  4984. </div>
  4985. <!-- Duration -->
  4986. <div>
  4987. <div>
  4988. <label class="block text-xs font-semibold text-slate-300 mb-1">Duration</label>
  4989. <select id="bwtest-input-duration" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-emerald-500">
  4990. <option value="10" selected>10 Seconds</option>
  4991. <option value="30">30 Seconds</option>
  4992. <option value="60">60 Seconds</option>
  4993. <option value="0">Continuous (Until Stop)</option>
  4994. </select>
  4995. </div>
  4996. </div>
  4997. <!-- Action Buttons -->
  4998. <div class="pt-2 flex items-center gap-3">
  4999. <button type="submit" id="bwtest-btn-start" class="flex-1 py-2.5 px-4 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs shadow-lg shadow-emerald-500/20 transition flex items-center justify-center gap-2">
  5000. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
  5001. <span>Run Bandwidth Test</span>
  5002. </button>
  5003. <button type="button" id="bwtest-btn-stop" onclick="stopBwTest()" disabled class="py-2.5 px-4 rounded-lg bg-slate-800 hover:bg-rose-600 text-slate-400 hover:text-white font-bold text-xs transition border border-slate-700 disabled:opacity-40 disabled:hover:bg-slate-800 disabled:hover:text-slate-400 flex items-center justify-center gap-2">
  5004. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z"/></svg>
  5005. <span>Stop</span>
  5006. </button>
  5007. </div>
  5008. </form>
  5009. </div>
  5010. <!-- Right Panel: Real-Time Dynamic Graph & Interval Log (7 cols) -->
  5011. <div class="lg:col-span-7 space-y-4">
  5012. <!-- Live Chart Card -->
  5013. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5">
  5014. <div class="flex items-center justify-between mb-3">
  5015. <div class="flex items-center gap-3">
  5016. <h3 class="text-sm font-bold text-white">Live Real-Time Throughput</h3>
  5017. <div class="flex items-center gap-3 text-xs font-mono">
  5018. <span class="flex items-center gap-1.5 text-cyan-400"><span class="w-2.5 h-1 bg-cyan-400 rounded-full"></span> TX (Upload)</span>
  5019. <span class="flex items-center gap-1.5 text-purple-400"><span class="w-2.5 h-1 bg-purple-400 rounded-full"></span> RX (Download)</span>
  5020. </div>
  5021. </div>
  5022. <span id="bwtest-graph-rate" class="text-xs font-mono text-slate-400">0.00 Mbps total</span>
  5023. </div>
  5024. <div class="relative w-full h-52 bg-slate-950 rounded-lg border border-slate-800/80 overflow-hidden">
  5025. <canvas id="bwtest-chart" class="w-full h-full block"></canvas>
  5026. <div id="bwtest-chart-placeholder" class="absolute inset-0 flex items-center justify-center text-xs text-slate-500 font-sans pointer-events-none">
  5027. Start a test to stream live throughput waveform in real time
  5028. </div>
  5029. </div>
  5030. </div>
  5031. <!-- Second-by-Second Breakdown Table -->
  5032. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  5033. <div class="p-3 border-b border-slate-800 flex items-center justify-between">
  5034. <h4 class="text-xs font-bold text-white uppercase tracking-wider">Interval Telemetry Log</h4>
  5035. <span id="bwtest-sample-count" class="text-[10px] font-mono text-slate-400">0 samples</span>
  5036. </div>
  5037. <div class="overflow-x-auto max-h-48 overflow-y-auto">
  5038. <table class="w-full text-left text-xs font-mono">
  5039. <thead class="text-slate-400 bg-slate-950/70 border-b border-slate-800 text-[10px] uppercase font-sans sticky top-0">
  5040. <tr>
  5041. <th class="py-1.5 px-3">Interval</th>
  5042. <th class="py-1.5 px-3 text-cyan-400">TX Rate</th>
  5043. <th class="py-1.5 px-3 text-purple-400">RX Rate</th>
  5044. <th class="py-1.5 px-3">Loss %</th>
  5045. <th class="py-1.5 px-3">Jitter</th>
  5046. <th class="py-1.5 px-3">Total Volume</th>
  5047. </tr>
  5048. </thead>
  5049. <tbody id="bwtest-samples-tbody" class="divide-y divide-slate-800/60">
  5050. <tr>
  5051. <td colspan="6" class="p-4 text-center text-slate-500 text-xs font-sans">No test active. Click "Run Bandwidth Test" to benchmark link.</td>
  5052. </tr>
  5053. </tbody>
  5054. </table>
  5055. </div>
  5056. </div>
  5057. </div>
  5058. </div>
  5059. <!-- Benchmark History Table -->
  5060. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
  5061. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  5062. <div>
  5063. <h3 class="text-sm font-bold text-white">Recent Benchmark History</h3>
  5064. <p class="text-[11px] text-slate-400 mt-0.5">Summary of previous bandwidth test runs recorded on this router.</p>
  5065. </div>
  5066. <button onclick="loadBwTestHistory()" class="text-xs font-semibold px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 transition">
  5067. <span>🔄 Refresh</span>
  5068. </button>
  5069. </div>
  5070. <div class="overflow-x-auto">
  5071. <table class="w-full text-left text-xs font-mono">
  5072. <thead class="text-slate-400 bg-slate-950/70 border-b border-slate-800 text-[10px] uppercase font-sans">
  5073. <tr>
  5074. <th class="py-2.5 px-4">Session ID</th>
  5075. <th class="py-2.5 px-4">Target Server</th>
  5076. <th class="py-2.5 px-4">Protocol</th>
  5077. <th class="py-2.5 px-4">Direction</th>
  5078. <th class="py-2.5 px-4">Duration</th>
  5079. <th class="py-2.5 px-4 text-cyan-400">Avg / Peak TX</th>
  5080. <th class="py-2.5 px-4 text-purple-400">Avg / Peak RX</th>
  5081. <th class="py-2.5 px-4">Packet Loss</th>
  5082. <th class="py-2.5 px-4">Avg Jitter</th>
  5083. <th class="py-2.5 px-4">Date / Time</th>
  5084. </tr>
  5085. </thead>
  5086. <tbody id="bwtest-history-tbody" class="divide-y divide-slate-800/80">
  5087. <tr>
  5088. <td colspan="10" class="p-6 text-center text-slate-500 text-xs font-sans">No past benchmark runs recorded yet.</td>
  5089. </tr>
  5090. </tbody>
  5091. </table>
  5092. </div>
  5093. </div>
  5094. </section>
  5095. <!-- ================= SECTION: SETTINGS ================= -->
  5096. <section id="section-settings" class="hidden space-y-6">
  5097. <!-- Settings Sub-Navigation Tabs -->
  5098. <div class="flex items-center justify-between border-b border-slate-800 pb-3 flex-wrap gap-3">
  5099. <div class="flex space-x-2">
  5100. <button onclick="switchSettingsSubTab('dns')" id="btn-settings-subtab-dns"
  5101. class="px-3.5 py-1.5 rounded-lg text-xs font-semibold bg-cyan-600 text-white transition flex items-center gap-1.5">
  5102. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/></svg>
  5103. <span>DNS Settings</span>
  5104. </button>
  5105. <button onclick="switchSettingsSubTab('system')" id="btn-settings-subtab-system"
  5106. class="px-3.5 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 text-slate-300 hover:text-white transition flex items-center gap-1.5">
  5107. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
  5108. <span>System & Hostname</span>
  5109. </button>
  5110. <button onclick="switchSettingsSubTab('backup')" id="btn-settings-subtab-backup"
  5111. class="px-3.5 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 text-slate-300 hover:text-white transition flex items-center gap-1.5 cursor-pointer">
  5112. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 4H6a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-2m-4-1v8m0 0l3-3m-3 3L9 8m-5 5h2.586a1 1 0 01.707.293l2.414 2.414a1 1 0 00.707.293h3.172a1 1 0 00.707-.293l2.414-2.414a1 1 0 01.707-.293H20"/></svg>
  5113. <span>Backup & Restore</span>
  5114. </button>
  5115. </div>
  5116. <div class="flex items-center gap-2">
  5117. <span id="dns-live-status-pill" class="px-2.5 py-1 rounded-full text-[11px] font-mono bg-emerald-500/10 text-emerald-400 border border-emerald-500/30 flex items-center gap-1.5">
  5118. <span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span>
  5119. <span id="dns-live-status-text">DNS Service Active</span>
  5120. </span>
  5121. </div>
  5122. </div>
  5123. <!-- Sub-Tab 1: DNS Settings -->
  5124. <div id="settings-subtab-dns" class="space-y-6">
  5125. <!-- DNS Forwarder Config Card -->
  5126. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5 shadow-lg">
  5127. <div class="flex items-center justify-between pb-4 mb-4 border-b border-slate-800 flex-wrap gap-2">
  5128. <div>
  5129. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  5130. <span>Router DNS & Upstream Resolvers</span>
  5131. </h3>
  5132. <p class="text-xs text-slate-400 mt-0.5">Configure system recursive DNS forwarders, caching, and LAN client request resolution.</p>
  5133. </div>
  5134. <div class="flex items-center gap-2">
  5135. <button type="button" onclick="flushDNSCache()" class="px-3 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white border border-slate-700 transition flex items-center gap-1.5">
  5136. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
  5137. <span>Flush Cache</span>
  5138. </button>
  5139. </div>
  5140. </div>
  5141. <form id="form-dns-settings" onsubmit="saveDNSSettings(event)" oninput="this.dataset.dirty='true'" class="space-y-4">
  5142. <!-- Upstream DNS Servers -->
  5143. <div>
  5144. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Upstream DNS Servers (Comma or space separated) *</label>
  5145. <div class="flex gap-2">
  5146. <input type="text" id="dns-servers-input" required placeholder="e.g. 1.1.1.1, 8.8.8.8, 9.9.9.9" class="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5147. </div>
  5148. <!-- Quick Preset Tags -->
  5149. <div class="flex items-center gap-1.5 mt-2 flex-wrap">
  5150. <span class="text-[11px] text-slate-500 font-sans">Quick Presets:</span>
  5151. <button type="button" onclick="appendDNSPreset('1.1.1.1, 1.0.0.1')" class="px-2 py-0.5 rounded text-[10px] font-mono bg-slate-800 hover:bg-cyan-950 hover:text-cyan-400 text-slate-400 border border-slate-700/80 transition">+ Cloudflare (1.1.1.1)</button>
  5152. <button type="button" onclick="appendDNSPreset('8.8.8.8, 8.8.4.4')" class="px-2 py-0.5 rounded text-[10px] font-mono bg-slate-800 hover:bg-cyan-950 hover:text-cyan-400 text-slate-400 border border-slate-700/80 transition">+ Google (8.8.8.8)</button>
  5153. <button type="button" onclick="appendDNSPreset('9.9.9.9, 149.112.112.112')" class="px-2 py-0.5 rounded text-[10px] font-mono bg-slate-800 hover:bg-cyan-950 hover:text-cyan-400 text-slate-400 border border-slate-700/80 transition">+ Quad9 (9.9.9.9)</button>
  5154. <button type="button" onclick="appendDNSPreset('208.67.222.222, 208.67.220.220')" class="px-2 py-0.5 rounded text-[10px] font-mono bg-slate-800 hover:bg-cyan-950 hover:text-cyan-400 text-slate-400 border border-slate-700/80 transition">+ OpenDNS</button>
  5155. </div>
  5156. </div>
  5157. <!-- Dynamic Servers badge display -->
  5158. <div id="dns-dynamic-servers-container" class="p-3 bg-slate-950/60 rounded-xl border border-slate-800 flex items-center justify-between flex-wrap gap-2">
  5159. <div class="flex items-center gap-2">
  5160. <span class="text-xs font-semibold text-slate-400 font-sans">Dynamic Servers (Learned from DHCP Client):</span>
  5161. <span id="dns-dynamic-servers-list" class="text-xs font-mono text-cyan-400 font-bold">None</span>
  5162. </div>
  5163. <span class="text-[10px] text-slate-500">Auto-injected into resolver forwarders</span>
  5164. </div>
  5165. <div class="grid grid-cols-1 md:grid-cols-3 gap-4 pt-2">
  5166. <div>
  5167. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Cache Size (Entries)</label>
  5168. <input type="number" id="dns-cache-size-input" min="150" max="50000" value="2048" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5169. <span class="text-[10px] text-slate-500 mt-1 block">Maximum cached records (Default: 2048)</span>
  5170. </div>
  5171. <div>
  5172. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Local Domain</label>
  5173. <input type="text" id="dns-domain-input" placeholder="e.g. home.lan" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5174. <span class="text-[10px] text-slate-500 mt-1 block">Appended to unqualified host queries</span>
  5175. </div>
  5176. <div>
  5177. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Allow Remote Requests</label>
  5178. <div class="flex items-center space-x-2 mt-2">
  5179. <input type="checkbox" id="dns-allow-remote-input" checked class="rounded bg-slate-800 border-slate-700 text-cyan-600 focus:ring-0">
  5180. <label for="dns-allow-remote-input" class="text-xs text-slate-300 cursor-pointer">Serve DNS to LAN clients (Port 53)</label>
  5181. </div>
  5182. <span class="text-[10px] text-slate-500 mt-1 block">When enabled, clients use this router as primary DNS</span>
  5183. </div>
  5184. </div>
  5185. <div class="pt-3 border-t border-slate-800 flex justify-end">
  5186. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg transition flex items-center gap-1.5">
  5187. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
  5188. <span>Apply DNS Settings</span>
  5189. </button>
  5190. </div>
  5191. </form>
  5192. </div>
  5193. <!-- Static DNS Table -->
  5194. <div class="bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden shadow-lg">
  5195. <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-2">
  5196. <div>
  5197. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  5198. <span>Static DNS Records</span>
  5199. <span id="dns-static-count-badge" class="text-xs font-mono text-cyan-400 bg-cyan-950 border border-cyan-800/60 px-2 py-0.5 rounded-full">0 Records</span>
  5200. </h3>
  5201. <p class="text-xs text-slate-400 mt-0.5">Authoritative local hostname-to-IP overrides and local CNAME aliases.</p>
  5202. </div>
  5203. <button type="button" onclick="openStaticDNSModal()" class="px-3 py-1.5 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white transition flex items-center gap-1.5">
  5204. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/></svg>
  5205. <span>Add Static DNS</span>
  5206. </button>
  5207. </div>
  5208. <div class="overflow-x-auto">
  5209. <table class="w-full text-left text-xs font-mono">
  5210. <thead class="text-slate-400 bg-slate-950/60 border-b border-slate-800 uppercase tracking-wider text-[10px] font-sans">
  5211. <tr>
  5212. <th class="py-2.5 px-4">Name (Hostname)</th>
  5213. <th class="py-2.5 px-4">Type</th>
  5214. <th class="py-2.5 px-4">Address / Target</th>
  5215. <th class="py-2.5 px-4">TTL</th>
  5216. <th class="py-2.5 px-4">Comment</th>
  5217. <th class="py-2.5 px-4">Status</th>
  5218. <th class="py-2.5 px-4 text-right font-sans">Actions</th>
  5219. </tr>
  5220. </thead>
  5221. <tbody id="dns-static-tbody" class="divide-y divide-slate-800/80"></tbody>
  5222. </table>
  5223. </div>
  5224. <div id="dns-static-empty" class="hidden p-8 text-center text-slate-500 text-xs font-sans">
  5225. No static DNS records configured yet. Click "+ Add Static DNS" to add local hostname mappings.
  5226. </div>
  5227. </div>
  5228. <!-- System Resolv.conf Preview -->
  5229. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-4 shadow">
  5230. <div class="flex items-center justify-between mb-2">
  5231. <span class="text-xs font-bold text-slate-300 font-sans">Active Host Resolver Configuration (/etc/resolv.conf)</span>
  5232. <span class="text-[10px] text-slate-500 font-mono">System Host View</span>
  5233. </div>
  5234. <pre id="dns-resolv-preview" class="p-3 bg-slate-950 rounded-lg text-slate-400 font-mono text-[11px] overflow-x-auto">Loading...</pre>
  5235. </div>
  5236. </div>
  5237. <!-- Sub-Tab 2: System & Hostname -->
  5238. <div id="settings-subtab-system" class="hidden space-y-6">
  5239. <div class="bg-slate-900/90 border border-slate-800 rounded-xl p-5 shadow-lg">
  5240. <div class="pb-4 mb-4 border-b border-slate-800">
  5241. <h3 class="text-sm font-bold text-white">System Identity & Hostname</h3>
  5242. <p class="text-xs text-slate-400 mt-0.5">Set the router appliance identity and timezone.</p>
  5243. </div>
  5244. <form id="form-system-settings" onsubmit="saveSystemSettings(event)" oninput="this.dataset.dirty='true'" class="space-y-4 max-w-xl">
  5245. <div>
  5246. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Router Identity / Hostname *</label>
  5247. <input type="text" id="system-hostname-input" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5248. </div>
  5249. <div>
  5250. <label class="block text-xs font-semibold text-slate-300 mb-1.5">System Timezone</label>
  5251. <select id="system-timezone-input" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5252. <option value="Europe/Athens">Europe/Athens (UTC+2 / UTC+3 EEST)</option>
  5253. <option value="Etc/UTC">Etc/UTC (Coordinated Universal Time)</option>
  5254. <option value="Europe/London">Europe/London (GMT/BST)</option>
  5255. <option value="Europe/Berlin">Europe/Berlin (CET/CEST)</option>
  5256. <option value="America/New_York">America/New_York (EST/EDT)</option>
  5257. <option value="America/Los_Angeles">America/Los_Angeles (PST/PDT)</option>
  5258. <option value="Asia/Tokyo">Asia/Tokyo (JST)</option>
  5259. </select>
  5260. </div>
  5261. <div class="pt-3">
  5262. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white transition">
  5263. Save System Settings
  5264. </button>
  5265. </div>
  5266. </form>
  5267. </div>
  5268. </div>
  5269. <!-- Sub-Tab 3: Backup & Restore -->
  5270. <div id="settings-subtab-backup" class="hidden space-y-6">
  5271. <!-- Header Explanatory Card -->
  5272. <div class="bg-slate-900/90 border border-slate-800 rounded-2xl p-5 shadow-lg flex flex-col md:flex-row md:items-center justify-between gap-4">
  5273. <div>
  5274. <h3 class="text-base font-bold text-white flex items-center gap-2">
  5275. <span class="w-2.5 h-2.5 rounded-full bg-cyan-400"></span>
  5276. <span>Full Configuration Backup & Disaster Recovery</span>
  5277. </h3>
  5278. <p class="text-xs text-slate-400 mt-1 max-w-3xl leading-relaxed">
  5279. Export the complete DebTik router appliance configuration—including routing policies, firewall rules, NAT, WireGuard & AeroTUN tunnels, SSL/TLS certificates & private keys, Reverse Proxy routes, DHCP, DNS, and user credentials—into a single downloadable <span class="font-mono text-cyan-400">.zip</span> archive. Restorations automatically generate a pre-restore fallback safety copy.
  5280. </p>
  5281. </div>
  5282. <div class="flex items-center gap-2">
  5283. <button type="button" onclick="loadBackupList()" class="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-xs text-slate-300 transition flex items-center gap-1.5 cursor-pointer">
  5284. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
  5285. <span>Refresh</span>
  5286. </button>
  5287. </div>
  5288. </div>
  5289. <!-- Two-Column Grid: Export & Import -->
  5290. <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
  5291. <!-- Card 1: Export Backup -->
  5292. <div class="bg-slate-900/90 border border-slate-800 rounded-2xl p-6 shadow-xl flex flex-col justify-between">
  5293. <div class="space-y-4">
  5294. <div class="flex items-center justify-between pb-3 border-b border-slate-800">
  5295. <div class="flex items-center gap-2.5">
  5296. <div class="w-9 h-9 rounded-xl bg-cyan-500/10 border border-cyan-500/20 text-cyan-400 flex items-center justify-center">
  5297. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
  5298. </div>
  5299. <div>
  5300. <h4 class="text-sm font-bold text-white">Export Configuration Backup</h4>
  5301. <p class="text-[11px] text-slate-400">Generate and download complete router archive (.zip)</p>
  5302. </div>
  5303. </div>
  5304. <span class="px-2 py-0.5 rounded text-[10px] font-mono bg-cyan-500/10 text-cyan-300 border border-cyan-500/20">LIVE SNAPSHOT</span>
  5305. </div>
  5306. <div>
  5307. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Backup Note / Comment (Optional)</label>
  5308. <input type="text" id="backup-export-comment" placeholder="e.g. Pre-maintenance backup or stable baseline"
  5309. class="w-full bg-slate-950 border border-slate-800 rounded-xl px-3.5 py-2.5 text-xs text-white placeholder-slate-600 focus:outline-none focus:border-cyan-500 font-sans">
  5310. </div>
  5311. <div class="rounded-xl bg-slate-950/60 border border-slate-800/80 p-3.5 space-y-2 text-xs">
  5312. <div class="text-[11px] font-semibold text-slate-300 uppercase tracking-wider">Archive Contents:</div>
  5313. <ul class="text-[11px] text-slate-400 space-y-1 font-mono">
  5314. <li class="flex items-center gap-2"><span class="text-emerald-400">✔</span> /etc/debtik/* (Firewall, Routing, VPN, Certs, DNS, Users)</li>
  5315. <li class="flex items-center gap-2"><span class="text-emerald-400">✔</span> /etc/network/interfaces.d/* (Interfaces, VLANs)</li>
  5316. <li class="flex items-center gap-2"><span class="text-emerald-400">✔</span> manifest.json (SHA-256 integrity verification)</li>
  5317. </ul>
  5318. </div>
  5319. </div>
  5320. <div class="pt-5 mt-4 border-t border-slate-800 flex justify-end">
  5321. <button type="button" id="btn-export-backup" onclick="exportBackupDownload()"
  5322. class="px-5 py-2.5 rounded-xl text-xs font-bold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-600/25 transition flex items-center gap-2 cursor-pointer">
  5323. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
  5324. <span>Download Backup (.zip)</span>
  5325. </button>
  5326. </div>
  5327. </div>
  5328. <!-- Card 2: Restore Backup -->
  5329. <div class="bg-slate-900/90 border border-slate-800 rounded-2xl p-6 shadow-xl flex flex-col justify-between">
  5330. <div class="space-y-4">
  5331. <div class="flex items-center justify-between pb-3 border-b border-slate-800">
  5332. <div class="flex items-center gap-2.5">
  5333. <div class="w-9 h-9 rounded-xl bg-amber-500/10 border border-amber-500/20 text-amber-400 flex items-center justify-center">
  5334. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/></svg>
  5335. </div>
  5336. <div>
  5337. <h4 class="text-sm font-bold text-white">Restore Configuration</h4>
  5338. <p class="text-[11px] text-slate-400">Upload a backup archive to restore router state</p>
  5339. </div>
  5340. </div>
  5341. <span class="px-2 py-0.5 rounded text-[10px] font-mono bg-amber-500/10 text-amber-300 border border-amber-500/20">REPLACE & RELOAD</span>
  5342. </div>
  5343. <!-- File Drop / Select Area -->
  5344. <div>
  5345. <input type="file" id="backup-file-input" accept=".zip" class="hidden" onchange="onBackupFileSelected(event)">
  5346. <div onclick="document.getElementById('backup-file-input').click()"
  5347. class="border-2 border-dashed border-slate-700 hover:border-cyan-500 rounded-xl p-5 text-center cursor-pointer transition bg-slate-950/40 hover:bg-slate-950/80">
  5348. <svg class="w-8 h-8 text-slate-500 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/></svg>
  5349. <div class="text-xs font-semibold text-slate-200" id="backup-file-label">Click to choose or drag & drop a .zip archive</div>
  5350. <div class="text-[10px] text-slate-500 mt-1 font-mono">Max archive size: 100 MB</div>
  5351. </div>
  5352. </div>
  5353. <!-- Inspection details preview card (hidden initially) -->
  5354. <div id="backup-inspect-preview" class="hidden rounded-xl bg-slate-950/90 border border-cyan-500/30 p-3.5 space-y-2 text-xs">
  5355. <div class="flex items-center justify-between">
  5356. <span class="font-bold text-cyan-400 flex items-center gap-1.5">
  5357. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
  5358. Archive Verified
  5359. </span>
  5360. <span id="backup-inspect-version" class="text-[10px] font-mono text-slate-400"></span>
  5361. </div>
  5362. <div class="grid grid-cols-2 gap-2 text-[11px] font-mono text-slate-300">
  5363. <div>Hostname: <span id="backup-inspect-hostname" class="text-white font-bold"></span></div>
  5364. <div>Files: <span id="backup-inspect-files" class="text-white font-bold"></span></div>
  5365. <div class="col-span-2">Created: <span id="backup-inspect-date" class="text-slate-400"></span></div>
  5366. <div class="col-span-2 text-slate-400 italic" id="backup-inspect-comment"></div>
  5367. </div>
  5368. </div>
  5369. <!-- Warning banner -->
  5370. <div class="p-3 rounded-xl bg-amber-500/10 border border-amber-500/20 text-amber-300/90 text-[11px] flex items-start gap-2">
  5371. <svg class="w-4 h-4 text-amber-400 shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>
  5372. <span>Restoring replaces active configuration. DebTik will take an automated fallback safety backup and reload services.</span>
  5373. </div>
  5374. </div>
  5375. <div class="pt-5 mt-4 border-t border-slate-800 flex justify-end gap-2">
  5376. <button type="button" id="btn-restore-backup" onclick="confirmRestoreBackup()" disabled
  5377. class="px-5 py-2.5 rounded-xl text-xs font-bold bg-amber-600 hover:bg-amber-500 disabled:bg-slate-800 disabled:text-slate-600 disabled:cursor-not-allowed text-white shadow-lg shadow-amber-600/25 transition flex items-center gap-2 cursor-pointer">
  5378. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
  5379. <span>Restore Configuration</span>
  5380. </button>
  5381. </div>
  5382. </div>
  5383. </div>
  5384. <!-- Card 3: Stored Safety Fallback Backups History -->
  5385. <div class="bg-slate-900/90 border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
  5386. <div class="p-4 border-b border-slate-800 bg-slate-950/40 flex items-center justify-between">
  5387. <div>
  5388. <h4 class="text-sm font-bold text-white flex items-center gap-2">
  5389. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
  5390. <span>Stored Safety Fallback Backups</span>
  5391. </h4>
  5392. <p class="text-[11px] text-slate-400">Automated pre-restore backups retained in <span class="font-mono text-slate-300">/var/lib/debtik/backups</span></p>
  5393. </div>
  5394. <button type="button" onclick="loadBackupList()" class="px-2.5 py-1 rounded-lg text-xs bg-slate-800 hover:bg-slate-700 text-slate-300 transition cursor-pointer">Refresh List</button>
  5395. </div>
  5396. <div class="overflow-x-auto">
  5397. <table class="w-full text-left text-xs">
  5398. <thead>
  5399. <tr class="text-slate-400 border-b border-slate-800/80 bg-slate-950/60 font-semibold uppercase tracking-wider text-[10px]">
  5400. <th class="py-3 px-4">Filename</th>
  5401. <th class="py-3 px-4">DebTik Version</th>
  5402. <th class="py-3 px-4">Hostname</th>
  5403. <th class="py-3 px-4">Created</th>
  5404. <th class="py-3 px-4">Size</th>
  5405. <th class="py-3 px-4">Comment</th>
  5406. <th class="py-3 px-4 text-right">Actions</th>
  5407. </tr>
  5408. </thead>
  5409. <tbody id="backups-table-tbody" class="divide-y divide-slate-800/60 font-mono">
  5410. <!-- Rendered dynamically -->
  5411. </tbody>
  5412. </table>
  5413. <div id="backups-empty-state" class="hidden p-8 text-center text-slate-500 text-xs">
  5414. No local backup archives currently stored on disk.
  5415. </div>
  5416. </div>
  5417. </div>
  5418. </div>
  5419. </section>
  5420. <!-- ================= SECTION: USERS & SECURITY ================= -->
  5421. <section id="section-users" class="hidden space-y-6">
  5422. <!-- Top KPI Stat Cards -->
  5423. <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
  5424. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 flex items-center justify-between shadow-lg">
  5425. <div>
  5426. <div class="text-[11px] text-slate-400 font-medium uppercase tracking-wider">Total System Users</div>
  5427. <div class="text-2xl font-bold font-mono text-white mt-1" id="stat-total-users">0</div>
  5428. </div>
  5429. <div class="w-10 h-10 rounded-xl bg-cyan-500/10 border border-cyan-500/20 text-cyan-400 flex items-center justify-center">
  5430. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /></svg>
  5431. </div>
  5432. </div>
  5433. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 flex items-center justify-between shadow-lg">
  5434. <div>
  5435. <div class="text-[11px] text-slate-400 font-medium uppercase tracking-wider">Super Administrators</div>
  5436. <div class="text-2xl font-bold font-mono text-emerald-400 mt-1" id="stat-admin-users">0</div>
  5437. </div>
  5438. <div class="w-10 h-10 rounded-xl bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 flex items-center justify-center">
  5439. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
  5440. </div>
  5441. </div>
  5442. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 flex items-center justify-between shadow-lg">
  5443. <div>
  5444. <div class="text-[11px] text-slate-400 font-medium uppercase tracking-wider">Active Session</div>
  5445. <div class="text-sm font-bold font-mono text-white mt-1 flex items-center gap-2">
  5446. <span id="stat-current-user">admin</span>
  5447. <span id="stat-current-role" class="text-[9px] bg-brand-500/20 text-brand-300 border border-brand-500/30 px-1.5 py-0.2 rounded font-mono uppercase font-bold">FULL</span>
  5448. </div>
  5449. </div>
  5450. <div class="w-10 h-10 rounded-xl bg-purple-500/10 border border-purple-500/20 text-purple-400 flex items-center justify-center">
  5451. <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
  5452. </div>
  5453. </div>
  5454. </div>
  5455. <!-- Main Table Card -->
  5456. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
  5457. <!-- Table Header Controls -->
  5458. <div class="p-4 border-b border-slate-800/80 bg-slate-950/40 flex flex-wrap items-center justify-between gap-3">
  5459. <div class="flex items-center gap-3">
  5460. <div class="relative">
  5461. <span class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-slate-500">
  5462. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
  5463. </span>
  5464. <input type="text" id="users-search-input" oninput="renderUsersTable()" placeholder="Search users by name or role..."
  5465. class="bg-slate-900 border border-slate-800 rounded-xl pl-9 pr-3 py-1.5 text-xs text-white placeholder-slate-500 font-mono focus:outline-none focus:border-cyan-500 w-64">
  5466. </div>
  5467. <button onclick="loadUsers(true)" class="p-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-slate-800 transition" title="Refresh Users">
  5468. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
  5469. </button>
  5470. </div>
  5471. <div class="flex items-center gap-2">
  5472. <button onclick="openSelfPasswordModal()" class="px-3 py-1.5 rounded-xl text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700 transition flex items-center gap-1.5 cursor-pointer">
  5473. <svg class="w-3.5 h-3.5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg>
  5474. <span>My Password</span>
  5475. </button>
  5476. <button onclick="openAddUserModal()" class="px-3.5 py-1.5 rounded-xl text-xs font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-600/20 transition flex items-center gap-1.5 cursor-pointer">
  5477. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/></svg>
  5478. <span>+ Add User</span>
  5479. </button>
  5480. </div>
  5481. </div>
  5482. <!-- Table -->
  5483. <div class="overflow-x-auto">
  5484. <table class="w-full text-left text-xs">
  5485. <thead>
  5486. <tr class="text-slate-400 border-b border-slate-800/80 bg-slate-950/60 font-semibold uppercase tracking-wider text-[10px]">
  5487. <th class="py-3 px-4">User</th>
  5488. <th class="py-3 px-4">Permissions (Role)</th>
  5489. <th class="py-3 px-4">Status</th>
  5490. <th class="py-3 px-4">Comment</th>
  5491. <th class="py-3 px-4">Last Login</th>
  5492. <th class="py-3 px-4 text-right">Actions</th>
  5493. </tr>
  5494. </thead>
  5495. <tbody id="users-table-tbody" class="divide-y divide-slate-800/60 font-mono">
  5496. <!-- Rendered dynamically -->
  5497. </tbody>
  5498. </table>
  5499. <div id="users-empty-state" class="hidden p-8 text-center text-slate-500 text-xs">
  5500. No users match the search criteria.
  5501. </div>
  5502. </div>
  5503. </div>
  5504. </section>
  5505. <!-- ================= SECTION: CERTIFICATES ================= -->
  5506. <section id="section-certificates" class="hidden space-y-6">
  5507. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 shadow-lg flex flex-wrap items-center justify-between gap-4">
  5508. <div>
  5509. <h3 class="text-base font-bold text-white flex items-center gap-2">
  5510. <svg class="w-5 h-5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
  5511. <span>Certificates & SSL/TLS</span>
  5512. </h3>
  5513. <p class="text-xs text-slate-400 mt-1">Manage Root Certificate Authorities (CA), issue Server/Client certificates, and request automated Let's Encrypt certificates.</p>
  5514. </div>
  5515. <div class="flex items-center gap-2 flex-wrap">
  5516. <button onclick="openCreateCAModal()" class="px-3 py-1.5 rounded-lg bg-blue-600 hover:bg-blue-500 text-xs font-semibold text-white flex items-center gap-1.5 transition shadow">
  5517. <span>+ Create CA</span>
  5518. </button>
  5519. <button onclick="openCreateCertModal()" class="px-3 py-1.5 rounded-lg bg-cyan-600 hover:bg-cyan-500 text-xs font-semibold text-white flex items-center gap-1.5 transition shadow">
  5520. <span>+ Issue Cert</span>
  5521. </button>
  5522. <button onclick="openLetsEncryptModal()" class="px-3 py-1.5 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-xs font-semibold text-white flex items-center gap-1.5 transition shadow">
  5523. <span>🔒 Let's Encrypt</span>
  5524. </button>
  5525. <button onclick="openImportCertModal()" class="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">
  5526. <span>📥 Import</span>
  5527. </button>
  5528. <button onclick="loadCertificates(true)" class="px-2.5 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs text-slate-400 hover:text-white transition">
  5529. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
  5530. </button>
  5531. </div>
  5532. </div>
  5533. <!-- Cards Summary -->
  5534. <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
  5535. <div class="bg-slate-900/60 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  5536. <div>
  5537. <span class="text-[11px] font-mono uppercase text-slate-400">Total Certificates</span>
  5538. <div class="text-xl font-bold font-mono text-white mt-1" id="kpi-certs-total">0</div>
  5539. </div>
  5540. <div class="w-9 h-9 rounded-lg bg-amber-500/10 border border-amber-500/30 flex items-center justify-center text-amber-400 font-bold">📜</div>
  5541. </div>
  5542. <div class="bg-slate-900/60 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  5543. <div>
  5544. <span class="text-[11px] font-mono uppercase text-slate-400">Certificate Authorities</span>
  5545. <div class="text-xl font-bold font-mono text-cyan-400 mt-1" id="kpi-certs-ca">0</div>
  5546. </div>
  5547. <div class="w-9 h-9 rounded-lg bg-cyan-500/10 border border-cyan-500/30 flex items-center justify-center text-cyan-400 font-bold">🏛</div>
  5548. </div>
  5549. <div class="bg-slate-900/60 border border-slate-800 rounded-xl p-4 flex items-center justify-between">
  5550. <div>
  5551. <span class="text-[11px] font-mono uppercase text-slate-400">HTTPS Web SSL (www-ssl)</span>
  5552. <div class="text-xs font-mono font-bold mt-1" id="kpi-https-status">
  5553. <span class="text-rose-400">Disabled (Port 443)</span>
  5554. </div>
  5555. </div>
  5556. <a href="#services" onclick="navigate('services')" title="Configure in Services" class="text-xs text-slate-400 hover:text-cyan-400 underline">Configure</a>
  5557. </div>
  5558. </div>
  5559. <!-- Certificates Table -->
  5560. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
  5561. <div class="overflow-x-auto">
  5562. <table class="w-full text-left text-xs">
  5563. <thead class="bg-slate-950/60 text-slate-400 uppercase font-mono text-[10px] tracking-wider border-b border-slate-800">
  5564. <tr>
  5565. <th class="py-3 px-4">Type</th>
  5566. <th class="py-3 px-4">Name / Common Name</th>
  5567. <th class="py-3 px-4">Issuer</th>
  5568. <th class="py-3 px-4">SANs</th>
  5569. <th class="py-3 px-4">Valid Until</th>
  5570. <th class="py-3 px-4 text-right">Actions</th>
  5571. </tr>
  5572. </thead>
  5573. <tbody id="certs-table-body" class="divide-y divide-slate-800 font-mono">
  5574. <!-- Rendered dynamically -->
  5575. </tbody>
  5576. </table>
  5577. </div>
  5578. </div>
  5579. </section>
  5580. <!-- ================= SECTION: REVERSE PROXY ================= -->
  5581. <section id="section-reverse_proxy" class="hidden space-y-6">
  5582. <!-- Header and Stats Banner -->
  5583. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 shadow-lg flex flex-wrap items-center justify-between gap-4">
  5584. <div>
  5585. <h3 class="text-base font-bold text-white flex items-center gap-2">
  5586. <svg class="w-5 h-5 text-violet-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  5587. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
  5588. </svg>
  5589. <span>Reverse Proxy & Application Gateway</span>
  5590. </h3>
  5591. <p class="text-xs text-slate-400 mt-1">Route HTTP/HTTPS domains and IP path prefixes to backend targets with subfolder forwarding and SSL/TLS termination.</p>
  5592. </div>
  5593. <div class="flex items-center gap-2 flex-wrap">
  5594. <button onclick="openProxyConfigModal()" class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700 text-xs font-semibold flex items-center gap-1.5 transition">
  5595. <svg class="w-3.5 h-3.5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
  5596. <span>Global Settings</span>
  5597. </button>
  5598. <button onclick="loadReverseProxy(true)" class="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700 text-xs font-semibold flex items-center gap-1.5 transition">
  5599. <svg class="w-3.5 h-3.5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /></svg>
  5600. <span>Refresh</span>
  5601. </button>
  5602. <button onclick="openAddProxyRuleModal()" class="px-3.5 py-1.5 rounded-xl bg-violet-600 hover:bg-violet-500 text-white text-xs font-semibold flex items-center gap-1.5 shadow-lg shadow-violet-600/20 transition">
  5603. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" /></svg>
  5604. <span>Add Proxy Rule</span>
  5605. </button>
  5606. </div>
  5607. </div>
  5608. <!-- Quick Metrics Cards -->
  5609. <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
  5610. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-sm">
  5611. <div class="text-[11px] font-mono text-slate-400 uppercase tracking-wider">Engine Status</div>
  5612. <div class="text-xl font-bold mt-1.5 flex items-center gap-2" id="proxy-metric-status">
  5613. <span class="w-2.5 h-2.5 rounded-full bg-emerald-500 animate-pulse"></span>
  5614. <span class="text-white">Active</span>
  5615. </div>
  5616. <div class="text-[10px] text-slate-500 mt-1" id="proxy-metric-ports">HTTP: 80 | HTTPS: 443</div>
  5617. </div>
  5618. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-sm">
  5619. <div class="text-[11px] font-mono text-slate-400 uppercase tracking-wider">Configured Rules</div>
  5620. <div class="text-2xl font-bold text-violet-400 mt-1" id="proxy-metric-rules">0</div>
  5621. <div class="text-[10px] text-slate-500 mt-1">Active proxy routes</div>
  5622. </div>
  5623. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-sm">
  5624. <div class="text-[11px] font-mono text-slate-400 uppercase tracking-wider">SSL Certificates</div>
  5625. <div class="text-2xl font-bold text-amber-400 mt-1" id="proxy-metric-certs">0</div>
  5626. <div class="text-[10px] text-slate-500 mt-1">Bound to proxy routes</div>
  5627. </div>
  5628. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-sm">
  5629. <div class="text-[11px] font-mono text-slate-400 uppercase tracking-wider">Total Requests Proxied</div>
  5630. <div class="text-2xl font-bold text-emerald-400 font-mono mt-1" id="proxy-metric-requests">0</div>
  5631. <div class="text-[10px] text-slate-500 mt-1">Cumulative HTTP/S hits</div>
  5632. </div>
  5633. </div>
  5634. <!-- Proxy Rules Table -->
  5635. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
  5636. <div class="p-4 border-b border-slate-800 flex items-center justify-between">
  5637. <h4 class="text-xs font-bold text-slate-300 uppercase tracking-wider font-mono">Proxy Routes & Rules</h4>
  5638. <span class="text-[11px] text-slate-500">Supports wildcards, subpath nesting and SSL termination</span>
  5639. </div>
  5640. <div class="overflow-x-auto">
  5641. <table class="w-full text-left text-xs">
  5642. <thead class="bg-slate-950/60 text-slate-400 uppercase font-mono text-[10px] tracking-wider border-b border-slate-800">
  5643. <tr>
  5644. <th class="py-3 px-4">State</th>
  5645. <th class="py-3 px-4">Name / Comment</th>
  5646. <th class="py-3 px-4">Inbound Domain / IP</th>
  5647. <th class="py-3 px-4">Inbound Path</th>
  5648. <th class="py-3 px-4">Target Backend</th>
  5649. <th class="py-3 px-4">Protocol & SSL</th>
  5650. <th class="py-3 px-4">Strip Path</th>
  5651. <th class="py-3 px-4">Requests</th>
  5652. <th class="py-3 px-4 text-right">Actions</th>
  5653. </tr>
  5654. </thead>
  5655. <tbody id="proxy-rules-table-body" class="divide-y divide-slate-800 font-mono">
  5656. <tr>
  5657. <td colspan="9" class="py-8 text-center text-slate-500 text-xs font-sans">Loading proxy rules...</td>
  5658. </tr>
  5659. </tbody>
  5660. </table>
  5661. </div>
  5662. </div>
  5663. </section>
  5664. <!-- ================= SECTION: SERVICES ================= -->
  5665. <section id="section-services" class="hidden space-y-6">
  5666. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-5 shadow-lg flex items-center justify-between">
  5667. <div>
  5668. <h3 class="text-base font-bold text-white flex items-center gap-2">
  5669. <svg class="w-5 h-5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" /></svg>
  5670. Management Services
  5671. </h3>
  5672. <p class="text-xs text-slate-400 mt-1">Control management service listeners and listening ports across Web, SSH, and Telnet.</p>
  5673. </div>
  5674. <button onclick="loadServices(true)" class="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs text-white flex items-center gap-1.5 transition">
  5675. <svg class="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
  5676. <span>Refresh</span>
  5677. </button>
  5678. </div>
  5679. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
  5680. <div class="overflow-x-auto">
  5681. <table class="w-full text-left text-xs">
  5682. <thead class="bg-slate-950/60 text-slate-400 uppercase font-mono text-[10px] tracking-wider border-b border-slate-800">
  5683. <tr>
  5684. <th class="py-3 px-4">Service</th>
  5685. <th class="py-3 px-4">Port</th>
  5686. <th class="py-3 px-4">Status</th>
  5687. <th class="py-3 px-4">Description</th>
  5688. <th class="py-3 px-4 text-right">Actions</th>
  5689. </tr>
  5690. </thead>
  5691. <tbody id="services-table-body" class="divide-y divide-slate-800 font-mono">
  5692. <!-- Rendered dynamically -->
  5693. </tbody>
  5694. </table>
  5695. </div>
  5696. </div>
  5697. </section>
  5698. <!-- ================= SECTION: SYSTEM LOGS ================= -->
  5699. <section id="section-updates" class="hidden space-y-6">
  5700. <div class="bg-slate-900 border border-slate-800 rounded-2xl p-6 space-y-4 max-w-3xl">
  5701. <h2 class="text-lg font-bold text-white">Git Updates</h2>
  5702. <p class="text-xs text-slate-400">Set the source repository for this appliance. Only a higher stable release version (vMAJOR.MINOR.PATCH tag) can be installed. Installation rebuilds DebTik, restarts the service and keeps the previous binaries for rollback.</p>
  5703. <label class="block text-xs text-slate-300">Repository URL<input id="update-repository" type="url" placeholder="https://git.example.com/team/DebTik.git" class="mt-1 w-full bg-slate-950 border border-slate-700 rounded-lg p-2 text-white font-mono"></label>
  5704. <label class="block text-xs text-slate-300">TLS public key pin (optional for private Git servers)<input id="update-tls-pin" placeholder="sha256//..." class="mt-1 w-full bg-slate-950 border border-slate-700 rounded-lg p-2 text-white font-mono"></label>
  5705. <div class="flex flex-wrap gap-2">
  5706. <button onclick="saveUpdateConfig()" class="bg-sky-700 text-white px-3 py-2 rounded-lg text-xs">Save source</button>
  5707. <button onclick="checkUpdate()" class="bg-slate-700 text-white px-3 py-2 rounded-lg text-xs">Check for updates</button>
  5708. <button id="update-install-button" disabled onclick="startUpdate('install')" class="bg-emerald-700 text-white px-3 py-2 rounded-lg text-xs">Install update</button>
  5709. <button onclick="startUpdate('rollback')" class="bg-amber-700 text-white px-3 py-2 rounded-lg text-xs">Restore previous version</button>
  5710. </div>
  5711. <div class="text-xs font-mono text-slate-300">Installed: <span id="update-installed">—</span><br>Available: <span id="update-available">—</span><br>Status: <span id="update-state">idle</span></div>
  5712. <pre id="update-log" class="bg-slate-950 rounded-lg p-3 text-[11px] text-slate-300 whitespace-pre-wrap max-h-72 overflow-auto"></pre>
  5713. </div>
  5714. </section>
  5715. <section id="section-logs" class="hidden space-y-6">
  5716. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl p-4 shadow-lg flex flex-wrap items-center justify-between gap-4">
  5717. <div class="flex items-center gap-3">
  5718. <div class="relative">
  5719. <span class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-slate-500">
  5720. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
  5721. </span>
  5722. <input type="text" id="logs-search-input" oninput="renderLogsTable()" placeholder="Search logs..."
  5723. class="bg-slate-950/80 border border-slate-800 rounded-xl pl-9 pr-3 py-1.5 text-xs text-white placeholder-slate-500 font-mono focus:outline-none focus:border-cyan-500 w-64">
  5724. </div>
  5725. <select id="logs-topic-filter" onchange="renderLogsTable()" class="bg-slate-950/80 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5726. <option value="">All Topics</option>
  5727. <option value="system">System</option>
  5728. <option value="auth">Auth & Security</option>
  5729. <option value="dhcp">DHCP</option>
  5730. <option value="route">Routing</option>
  5731. <option value="firewall">Firewall</option>
  5732. <option value="error">Errors</option>
  5733. </select>
  5734. <select id="logs-limit-select" onchange="loadLogs(true)" class="bg-slate-950/80 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5735. <option value="50">50 entries</option>
  5736. <option value="100" selected>100 entries</option>
  5737. <option value="250">250 entries</option>
  5738. <option value="500">500 entries</option>
  5739. </select>
  5740. </div>
  5741. <div class="flex items-center gap-3">
  5742. <label class="flex items-center gap-2 text-xs text-slate-400 cursor-pointer">
  5743. <input type="checkbox" id="logs-auto-refresh" checked class="rounded bg-slate-800 border-slate-700 text-cyan-500 focus:ring-0">
  5744. <span>Auto-refresh</span>
  5745. </label>
  5746. <button onclick="loadLogs(true)" class="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs text-white flex items-center gap-1.5 transition">
  5747. <svg class="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
  5748. <span>Refresh</span>
  5749. </button>
  5750. </div>
  5751. </div>
  5752. <div class="bg-slate-900/80 border border-slate-800 rounded-2xl overflow-hidden shadow-xl">
  5753. <div class="overflow-x-auto max-h-[650px] overflow-y-auto">
  5754. <table class="w-full text-left text-xs">
  5755. <thead class="sticky top-0 bg-slate-950 text-slate-400 uppercase font-mono text-[10px] tracking-wider border-b border-slate-800 z-10">
  5756. <tr>
  5757. <th class="py-2.5 px-3 w-16">#</th>
  5758. <th class="py-2.5 px-3 w-44">Time</th>
  5759. <th class="py-2.5 px-3 w-36">Topics</th>
  5760. <th class="py-2.5 px-3">Message</th>
  5761. </tr>
  5762. </thead>
  5763. <tbody id="logs-table-body" class="divide-y divide-slate-800/60 font-mono text-[11px]">
  5764. <!-- Rendered dynamically -->
  5765. </tbody>
  5766. </table>
  5767. </div>
  5768. </div>
  5769. </section>
  5770. </main>
  5771. </div>
  5772. </div>
  5773. <!-- ================= MODAL: STATIC DNS RECORD ================= -->
  5774. <div id="modal-static-dns" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  5775. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  5776. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  5777. <h3 id="modal-static-dns-title" class="text-sm font-bold text-white">Add Static DNS Record</h3>
  5778. <button onclick="closeStaticDNSModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  5779. </div>
  5780. <form id="form-static-dns" onsubmit="submitSaveStaticDNS(event)" class="p-6 space-y-4">
  5781. <input type="hidden" id="modal-static-dns-id">
  5782. <div>
  5783. <label class="block text-xs font-semibold text-slate-300 mb-1.5">DNS Name (FQDN or Hostname) *</label>
  5784. <input type="text" id="modal-static-dns-name" placeholder="e.g. router.lan or nas.home" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5785. </div>
  5786. <div class="grid grid-cols-2 gap-4">
  5787. <div>
  5788. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Record Type</label>
  5789. <select id="modal-static-dns-type" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5790. <option value="A">A (IPv4 Address)</option>
  5791. <option value="AAAA">AAAA (IPv6 Address)</option>
  5792. <option value="CNAME">CNAME (Alias)</option>
  5793. <option value="TXT">TXT (Text record)</option>
  5794. </select>
  5795. </div>
  5796. <div>
  5797. <label class="block text-xs font-semibold text-slate-300 mb-1.5">TTL (Seconds)</label>
  5798. <input type="number" id="modal-static-dns-ttl" min="0" value="3600" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5799. </div>
  5800. </div>
  5801. <div>
  5802. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Target IP / Canonical Name *</label>
  5803. <input type="text" id="modal-static-dns-addr" placeholder="e.g. 192.168.130.1 or router.lan" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5804. </div>
  5805. <div>
  5806. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Comment</label>
  5807. <input type="text" id="modal-static-dns-comment" placeholder="Optional description" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white focus:outline-none focus:border-cyan-500">
  5808. </div>
  5809. <div class="flex items-center space-x-2 pt-1">
  5810. <input type="checkbox" id="modal-static-dns-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500 focus:ring-0">
  5811. <label for="modal-static-dns-disabled" class="text-xs text-slate-300 cursor-pointer">Disabled (Commented out)</label>
  5812. </div>
  5813. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  5814. <button type="button" onclick="closeStaticDNSModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800">Cancel</button>
  5815. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white">Save Record</button>
  5816. </div>
  5817. </form>
  5818. </div>
  5819. </div>
  5820. <!-- ================= MODAL: ADD USER ================= -->
  5821. <div id="modal-add-user" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  5822. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  5823. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  5824. <div class="flex items-center gap-2">
  5825. <div class="w-7 h-7 rounded-lg bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 flex items-center justify-center">
  5826. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"/></svg>
  5827. </div>
  5828. <h3 class="text-sm font-bold text-white">Add New User</h3>
  5829. </div>
  5830. <button onclick="closeAddUserModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  5831. </div>
  5832. <form id="form-add-user" onsubmit="submitAddUser(event)" class="p-6 space-y-4">
  5833. <div>
  5834. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Username *</label>
  5835. <input type="text" id="user-add-username" required placeholder="e.g. operator"
  5836. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5837. </div>
  5838. <div class="grid grid-cols-2 gap-3">
  5839. <div>
  5840. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Password *</label>
  5841. <input type="password" id="user-add-password" required placeholder="••••••••"
  5842. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5843. </div>
  5844. <div>
  5845. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Confirm Password *</label>
  5846. <input type="password" id="user-add-confirm" required placeholder="••••••••"
  5847. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5848. </div>
  5849. </div>
  5850. <div>
  5851. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Permissions / Role *</label>
  5852. <select id="user-add-role" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5853. <option value="full">full (Super Administrator - All Capabilities)</option>
  5854. <option value="write">write (Operator - Router & Network Management)</option>
  5855. <option value="read">read (Observer - View Only Access)</option>
  5856. </select>
  5857. </div>
  5858. <div>
  5859. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Comment / Description</label>
  5860. <input type="text" id="user-add-comment" placeholder="e.g. NOC shift operator"
  5861. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white focus:outline-none focus:border-cyan-500">
  5862. </div>
  5863. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  5864. <button type="button" onclick="closeAddUserModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800 transition">Cancel</button>
  5865. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-emerald-600 hover:bg-emerald-500 text-white transition shadow-lg shadow-emerald-600/20">Create User</button>
  5866. </div>
  5867. </form>
  5868. </div>
  5869. </div>
  5870. <!-- ================= MODAL: EDIT USER ================= -->
  5871. <div id="modal-edit-user" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  5872. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  5873. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  5874. <div class="flex items-center gap-2">
  5875. <div class="w-7 h-7 rounded-lg bg-cyan-500/10 border border-cyan-500/20 text-cyan-400 flex items-center justify-center">
  5876. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
  5877. </div>
  5878. <h3 class="text-sm font-bold text-white">Edit User <span id="user-edit-username-title" class="font-mono text-cyan-400"></span></h3>
  5879. </div>
  5880. <button onclick="closeEditUserModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  5881. </div>
  5882. <form id="form-edit-user" onsubmit="submitEditUser(event)" class="p-6 space-y-4">
  5883. <input type="hidden" id="user-edit-username">
  5884. <div>
  5885. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Permissions / Role *</label>
  5886. <select id="user-edit-role" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5887. <option value="full">full (Super Administrator - All Capabilities)</option>
  5888. <option value="write">write (Operator - Router & Network Management)</option>
  5889. <option value="read">read (Observer - View Only Access)</option>
  5890. </select>
  5891. </div>
  5892. <div>
  5893. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Comment / Description</label>
  5894. <input type="text" id="user-edit-comment" placeholder="Optional description"
  5895. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white focus:outline-none focus:border-cyan-500">
  5896. </div>
  5897. <div class="flex items-center space-x-2 pt-1">
  5898. <input type="checkbox" id="user-edit-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500 focus:ring-0">
  5899. <label for="user-edit-disabled" class="text-xs text-slate-300 cursor-pointer">Account Disabled (Prevent login)</label>
  5900. </div>
  5901. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  5902. <button type="button" onclick="closeEditUserModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800 transition">Cancel</button>
  5903. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white transition shadow-lg shadow-cyan-600/20">Save Changes</button>
  5904. </div>
  5905. </form>
  5906. </div>
  5907. </div>
  5908. <!-- ================= MODAL: ADMIN RESET PASSWORD ================= -->
  5909. <div id="modal-admin-reset-pw" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  5910. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  5911. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  5912. <div class="flex items-center gap-2">
  5913. <div class="w-7 h-7 rounded-lg bg-amber-500/10 border border-amber-500/20 text-amber-400 flex items-center justify-center">
  5914. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg>
  5915. </div>
  5916. <h3 class="text-sm font-bold text-white">Reset Password for <span id="admin-reset-username-title" class="font-mono text-amber-400"></span></h3>
  5917. </div>
  5918. <button onclick="closeAdminResetPasswordModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  5919. </div>
  5920. <form id="form-admin-reset-pw" onsubmit="submitAdminResetPassword(event)" class="p-6 space-y-4">
  5921. <input type="hidden" id="admin-reset-username">
  5922. <div>
  5923. <label class="block text-xs font-semibold text-slate-300 mb-1.5">New Password *</label>
  5924. <input type="password" id="admin-reset-new-pw" required placeholder="Minimum 4 characters"
  5925. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5926. </div>
  5927. <div>
  5928. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Confirm New Password *</label>
  5929. <input type="password" id="admin-reset-confirm-pw" required placeholder="Re-enter password"
  5930. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5931. </div>
  5932. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  5933. <button type="button" onclick="closeAdminResetPasswordModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800 transition">Cancel</button>
  5934. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-amber-600 hover:bg-amber-500 text-white transition shadow-lg shadow-amber-600/20">Set Password</button>
  5935. </div>
  5936. </form>
  5937. </div>
  5938. </div>
  5939. <!-- ================= MODAL: CHANGE OWN PASSWORD ================= -->
  5940. <div id="modal-self-password" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  5941. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  5942. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  5943. <div class="flex items-center gap-2">
  5944. <div class="w-7 h-7 rounded-lg bg-cyan-500/10 border border-cyan-500/20 text-cyan-400 flex items-center justify-center">
  5945. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg>
  5946. </div>
  5947. <h3 class="text-sm font-bold text-white">Change My Password</h3>
  5948. </div>
  5949. <button onclick="closeSelfPasswordModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  5950. </div>
  5951. <form id="form-self-password" onsubmit="submitSelfPassword(event)" class="p-6 space-y-4">
  5952. <div>
  5953. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Current Password *</label>
  5954. <input type="password" id="self-current-password" required placeholder="Current password"
  5955. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5956. </div>
  5957. <div>
  5958. <label class="block text-xs font-semibold text-slate-300 mb-1.5">New Password *</label>
  5959. <input type="password" id="self-new-password" required placeholder="Minimum 4 characters"
  5960. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5961. </div>
  5962. <div>
  5963. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Confirm New Password *</label>
  5964. <input type="password" id="self-confirm-password" required placeholder="Re-enter new password"
  5965. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  5966. </div>
  5967. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  5968. <button type="button" onclick="closeSelfPasswordModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800 transition">Cancel</button>
  5969. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white transition shadow-lg shadow-cyan-600/20">Update Password</button>
  5970. </div>
  5971. </form>
  5972. </div>
  5973. </div>
  5974. <!-- ================= MODAL: CREATE VLAN ================= -->
  5975. <div id="vlan-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  5976. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  5977. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  5978. <h3 class="text-sm font-bold text-white">Create New VLAN Interface</h3>
  5979. <button onclick="closeVlanModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  5980. </div>
  5981. <form id="vlan-form" onsubmit="submitCreateVlan(event)" class="p-6 space-y-4">
  5982. <div>
  5983. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Parent Interface *</label>
  5984. <select id="vlan-parent" onchange="autoSuggestVlanName()" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white focus:outline-none focus:border-cyan-500 font-mono"></select>
  5985. </div>
  5986. <div class="grid grid-cols-2 gap-4">
  5987. <div>
  5988. <label class="block text-xs font-semibold text-slate-300 mb-1.5">VLAN ID (1-4094) *</label>
  5989. <input type="number" id="vlan-id" min="1" max="4094" placeholder="10" required oninput="autoSuggestVlanName()" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  5990. </div>
  5991. <div>
  5992. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Interface Name</label>
  5993. <input type="text" id="vlan-name" placeholder="eth0.10" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  5994. </div>
  5995. </div>
  5996. <div>
  5997. <div class="flex items-center justify-between mb-1.5">
  5998. <label class="text-xs font-semibold text-slate-300">Router IP Address & CIDR</label>
  5999. <div class="flex space-x-1">
  6000. <button type="button" onclick="setSubnetPreset('/24')" class="text-[10px] bg-slate-800 text-slate-400 px-1.5 py-0.5 rounded">/24</button>
  6001. <button type="button" onclick="setSubnetPreset('/23')" class="text-[10px] bg-slate-800 text-slate-400 px-1.5 py-0.5 rounded">/23</button>
  6002. <button type="button" onclick="setSubnetPreset('/16')" class="text-[10px] bg-slate-800 text-slate-400 px-1.5 py-0.5 rounded">/16</button>
  6003. </div>
  6004. </div>
  6005. <input type="text" id="vlan-ip" placeholder="192.168.10.1/24" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  6006. </div>
  6007. <div class="grid grid-cols-2 gap-4">
  6008. <div>
  6009. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Role Tag</label>
  6010. <select id="vlan-role" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white">
  6011. <option value="LAN">LAN</option>
  6012. <option value="GUEST">Guest Network</option>
  6013. <option value="IOT">IoT / Smart Home</option>
  6014. <option value="DMZ">DMZ / Isolated</option>
  6015. <option value="MGMT">Management</option>
  6016. <option value="WAN">WAN / Uplink</option>
  6017. </select>
  6018. </div>
  6019. <div>
  6020. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Description / Alias</label>
  6021. <input type="text" id="vlan-desc" placeholder="Guest WiFi Subnet" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white">
  6022. </div>
  6023. </div>
  6024. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  6025. <button type="button" onclick="closeVlanModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800">Cancel</button>
  6026. <button type="submit" id="vlan-submit-btn" class="px-5 py-2 rounded-lg text-xs font-semibold bg-gradient-to-r from-brand-600 to-cyan-600 text-white">Create VLAN</button>
  6027. </div>
  6028. </form>
  6029. </div>
  6030. </div>
  6031. <!-- ================= MODAL: EDIT INTERFACE ================= -->
  6032. <div id="iface-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  6033. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  6034. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  6035. <div>
  6036. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  6037. <span id="modal-iface-name">eth0</span>
  6038. <span id="modal-iface-badge" class="text-[10px] font-mono px-2 py-0.5 rounded bg-slate-800 text-slate-300">Ethernet</span>
  6039. </h3>
  6040. </div>
  6041. <button onclick="closeIfaceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  6042. </div>
  6043. <div class="p-6 space-y-5">
  6044. <!-- Interface Name & Rename -->
  6045. <div class="bg-slate-950/60 rounded-xl border border-slate-800/80 p-3.5 space-y-2">
  6046. <div class="flex items-center justify-between">
  6047. <label class="block text-xs font-semibold text-slate-300">Interface Name</label>
  6048. <span id="modal-iface-rename-hint" class="text-[10px] text-slate-400 font-mono">Kernel net_device</span>
  6049. </div>
  6050. <div class="flex items-center space-x-2">
  6051. <input type="text" id="modal-iface-new-name" maxlength="15" placeholder="e.g. wan1, eth0, HomeNet" class="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  6052. <button type="button" id="modal-btn-rename" onclick="renameInterface()" class="px-3.5 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white flex items-center gap-1.5 transition">
  6053. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>
  6054. <span>Rename</span>
  6055. </button>
  6056. </div>
  6057. <p id="modal-iface-rename-help" class="text-[10px] text-slate-500">Device name in Linux (max 15 characters). Automatically preserves operational link state and metadata.</p>
  6058. </div>
  6059. <div class="grid grid-cols-2 gap-4 p-3 bg-slate-950/60 rounded-xl border border-slate-800/80">
  6060. <div>
  6061. <span class="block text-[11px] text-slate-400 mb-1">Administrative Link</span>
  6062. <div class="flex items-center space-x-2">
  6063. <button id="modal-btn-up" onclick="setIfaceState(state.currentModalIface, 'up')" class="px-3 py-1 rounded text-xs font-semibold bg-emerald-600/30 text-emerald-400 border border-emerald-500/40">UP</button>
  6064. <button id="modal-btn-down" onclick="setIfaceState(state.currentModalIface, 'down')" class="px-3 py-1 rounded text-xs font-semibold bg-slate-800 text-slate-400 hover:bg-rose-600/30 hover:text-rose-400">DOWN</button>
  6065. </div>
  6066. </div>
  6067. <div>
  6068. <span class="block text-[11px] text-slate-400 mb-1">MTU Size</span>
  6069. <div class="flex items-center space-x-2">
  6070. <input type="number" id="modal-iface-mtu" min="68" max="9000" class="w-24 bg-slate-800 border border-slate-700 rounded px-2 py-1 text-xs text-white font-mono">
  6071. <button onclick="applyMTU()" class="text-xs bg-slate-800 hover:bg-slate-700 text-white px-2.5 py-1 rounded border border-slate-700">Set</button>
  6072. </div>
  6073. </div>
  6074. </div>
  6075. <div>
  6076. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Assigned IP Addresses</label>
  6077. <div id="modal-ip-list" class="space-y-1.5 mb-3 font-mono text-xs"></div>
  6078. <div class="flex space-x-2">
  6079. <input type="text" id="modal-new-ip" placeholder="192.168.100.1/24" class="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  6080. <button onclick="addInterfaceIP()" class="bg-brand-600 hover:bg-brand-500 text-white px-4 py-2 rounded-lg text-xs font-semibold">+ Add IP</button>
  6081. </div>
  6082. </div>
  6083. <div class="grid grid-cols-2 gap-4">
  6084. <div>
  6085. <div class="flex items-center justify-between mb-1.5">
  6086. <label class="block text-xs font-semibold text-slate-300">Role Tag</label>
  6087. <button type="button" onclick="closeIfaceModal(); switchIfaceSubTab('roles')" class="text-[10px] text-cyan-400 hover:underline">Manage Tags</button>
  6088. </div>
  6089. <select id="modal-iface-role" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white">
  6090. </select>
  6091. </div>
  6092. <div>
  6093. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Description</label>
  6094. <input type="text" id="modal-iface-desc" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white">
  6095. </div>
  6096. </div>
  6097. <div class="pt-4 border-t border-slate-800 flex justify-between items-center">
  6098. <button id="modal-delete-vlan-btn" onclick="confirmDeleteVlan(state.currentModalIface)" class="hidden text-xs text-rose-400 hover:text-rose-300">Delete VLAN</button>
  6099. <div class="flex space-x-3">
  6100. <button onclick="closeIfaceModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800">Close</button>
  6101. <button onclick="saveInterfaceMetadata()" class="px-5 py-2 rounded-lg text-xs font-semibold bg-brand-600 hover:bg-brand-500 text-white">Save Changes</button>
  6102. </div>
  6103. </div>
  6104. </div>
  6105. </div>
  6106. </div>
  6107. <!-- ================= MODAL: ROLE TAG ================= -->
  6108. <div id="role-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  6109. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  6110. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  6111. <h3 id="role-modal-title" class="text-sm font-bold text-white">Add Role Tag</h3>
  6112. <button onclick="closeRoleModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  6113. </div>
  6114. <form onsubmit="submitSaveRole(event)" class="p-6 space-y-4 text-xs font-sans">
  6115. <input type="hidden" id="role-modal-id">
  6116. <div>
  6117. <label class="block font-semibold text-slate-300 mb-1.5">Role Tag Name *</label>
  6118. <input type="text" id="role-modal-name" required placeholder="e.g. VoIP, Cameras, Trunk, Backbone, WAN2"
  6119. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500"
  6120. oninput="updateRoleModalPreview()">
  6121. </div>
  6122. <div class="grid grid-cols-2 gap-4">
  6123. <div>
  6124. <label class="block font-semibold text-slate-300 mb-1.5">Color Theme *</label>
  6125. <select id="role-modal-color" onchange="updateRoleModalPreview()"
  6126. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  6127. <option value="amber">Amber (Yellow/Orange)</option>
  6128. <option value="blue" selected>Blue</option>
  6129. <option value="cyan">Cyan (Sky Blue)</option>
  6130. <option value="emerald">Emerald (Green)</option>
  6131. <option value="purple">Purple</option>
  6132. <option value="rose">Rose (Red)</option>
  6133. <option value="indigo">Indigo</option>
  6134. <option value="sky">Sky</option>
  6135. <option value="slate">Slate (Gray)</option>
  6136. </select>
  6137. </div>
  6138. <div>
  6139. <label class="block font-semibold text-slate-300 mb-1.5">Badge Preview</label>
  6140. <div class="h-9 flex items-center">
  6141. <span id="role-modal-preview" class="px-2.5 py-1 rounded text-xs font-semibold bg-blue-500/20 text-blue-300 border border-blue-500/30">Preview</span>
  6142. </div>
  6143. </div>
  6144. </div>
  6145. <div>
  6146. <label class="block font-semibold text-slate-300 mb-1.5">Description (Optional)</label>
  6147. <input type="text" id="role-modal-desc" placeholder="e.g. Dedicated security & surveillance devices"
  6148. class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-cyan-500">
  6149. </div>
  6150. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  6151. <button type="button" onclick="closeRoleModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800">Cancel</button>
  6152. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-brand-600 hover:bg-brand-500 text-white shadow-lg shadow-brand-500/20">Save Role Tag</button>
  6153. </div>
  6154. </form>
  6155. </div>
  6156. </div>
  6157. <!-- ================= MODAL: PPPoE CLIENT ================= -->
  6158. <div id="pppoe-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  6159. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  6160. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  6161. <h3 id="pppoe-modal-title" class="text-sm font-bold text-white">Add PPPoE Client Interface</h3>
  6162. <button onclick="closePPPoEModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  6163. </div>
  6164. <form onsubmit="submitSavePPPoE(event)" class="p-6 space-y-4">
  6165. <input type="hidden" id="pppoe-modal-edit-orig">
  6166. <div class="grid grid-cols-2 gap-4">
  6167. <div>
  6168. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Name *</label>
  6169. <input type="text" id="pppoe-modal-name" placeholder="pppoe-out1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  6170. </div>
  6171. <div>
  6172. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Interface *</label>
  6173. <select id="pppoe-modal-iface" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500"></select>
  6174. </div>
  6175. </div>
  6176. <div class="grid grid-cols-2 gap-4">
  6177. <div>
  6178. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Username *</label>
  6179. <input type="text" id="pppoe-modal-user" placeholder="user@isp" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  6180. </div>
  6181. <div>
  6182. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Password *</label>
  6183. <input type="password" id="pppoe-modal-pass" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  6184. </div>
  6185. </div>
  6186. <div class="grid grid-cols-2 gap-4">
  6187. <div>
  6188. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Service Name (Optional)</label>
  6189. <input type="text" id="pppoe-modal-service" placeholder="ISP Service" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white">
  6190. </div>
  6191. <div>
  6192. <label class="block text-xs font-semibold text-slate-300 mb-1.5">PPP Profile</label>
  6193. <select id="pppoe-modal-profile" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  6194. <option value="default" selected>default</option>
  6195. <option value="default-encryption">default-encryption</option>
  6196. </select>
  6197. </div>
  6198. </div>
  6199. <div>
  6200. <label class="block text-xs font-semibold text-slate-300 mb-1.5">MTU</label>
  6201. <input type="number" id="pppoe-modal-mtu" value="1492" min="576" max="1500" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  6202. </div>
  6203. <div class="grid grid-cols-3 gap-2 pt-2 border-t border-slate-800/80">
  6204. <label class="flex items-center space-x-2 cursor-pointer">
  6205. <input type="checkbox" id="pppoe-modal-defroute" checked class="rounded bg-slate-800 border-slate-700 text-blue-500">
  6206. <span class="text-xs text-slate-300 font-medium">Default Route</span>
  6207. </label>
  6208. <label class="flex items-center space-x-2 cursor-pointer">
  6209. <input type="checkbox" id="pppoe-modal-reconnect" checked class="rounded bg-slate-800 border-slate-700 text-blue-500">
  6210. <span class="text-xs text-slate-300 font-medium">Auto-Reconnect</span>
  6211. </label>
  6212. <label class="flex items-center space-x-2 cursor-pointer">
  6213. <input type="checkbox" id="pppoe-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  6214. <span class="text-xs text-slate-300 font-medium">Disabled (X)</span>
  6215. </label>
  6216. </div>
  6217. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  6218. <button type="button" onclick="closePPPoEModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800">Cancel</button>
  6219. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-blue-600 hover:bg-blue-500 text-white shadow-lg shadow-blue-500/20">Save PPPoE</button>
  6220. </div>
  6221. </form>
  6222. </div>
  6223. </div>
  6224. <!-- ================= MODAL: WIREGUARD INTERFACE ================= -->
  6225. <!-- ================= MODAL: ADD / EDIT WIREGUARD INTERFACE ================= -->
  6226. <div id="wg-iface-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  6227. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  6228. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  6229. <div class="flex items-center gap-2">
  6230. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-rose-500/20 text-rose-300 border border-rose-500/30 tracking-wide uppercase">Cryptographic VPN</span>
  6231. <h3 id="wg-iface-modal-title" class="text-sm font-bold text-white">Add WireGuard Interface</h3>
  6232. </div>
  6233. <button onclick="closeAddWGInterfaceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  6234. </div>
  6235. <form onsubmit="submitSaveWGInterface(event)" class="p-6 space-y-4 text-xs">
  6236. <input type="hidden" id="wg-iface-modal-edit-orig">
  6237. <div class="grid grid-cols-2 gap-4">
  6238. <div>
  6239. <label class="block font-semibold text-slate-300 mb-1.5">Interface Name *</label>
  6240. <input type="text" id="wg-iface-modal-name" placeholder="wg0" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-rose-500">
  6241. <p class="text-[10px] text-slate-500 mt-1">e.g. wg0, wg-office, wgx-Space</p>
  6242. </div>
  6243. <div>
  6244. <label class="block font-semibold text-slate-300 mb-1.5">Listen Port *</label>
  6245. <input type="number" id="wg-iface-modal-port" value="51820" min="1" max="65535" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-rose-500">
  6246. </div>
  6247. </div>
  6248. <div class="grid grid-cols-2 gap-4">
  6249. <div>
  6250. <label class="block font-semibold text-slate-300 mb-1.5">Tunnel IP (CIDR)</label>
  6251. <input type="text" id="wg-iface-modal-addr" placeholder="10.10.0.1/24" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-rose-500">
  6252. <p class="text-[10px] text-slate-500 mt-1">Assigned IP/mask, or leave blank</p>
  6253. </div>
  6254. <div>
  6255. <label class="block font-semibold text-slate-300 mb-1.5">MTU</label>
  6256. <input type="number" id="wg-iface-modal-mtu" value="1420" min="1280" max="1500" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  6257. </div>
  6258. </div>
  6259. <div>
  6260. <label class="block font-semibold text-slate-300 mb-1.5">Comment / Alias</label>
  6261. <input type="text" id="wg-iface-modal-comment" placeholder="e.g. Main Roadwarrior Server, Site-to-Site tunnel" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-rose-500">
  6262. </div>
  6263. <div class="bg-slate-950/60 border border-slate-800/80 rounded-xl p-3.5 space-y-3">
  6264. <div class="flex items-center justify-between">
  6265. <span class="text-[11px] font-bold text-slate-400 uppercase tracking-wider font-mono">Cryptographic Keypair</span>
  6266. <button type="button" onclick="generateWGInterfaceKeys()" class="text-[11px] text-rose-400 hover:text-rose-300 font-semibold flex items-center gap-1">
  6267. <span>⚡ Generate New Keypair</span>
  6268. </button>
  6269. </div>
  6270. <div>
  6271. <div class="flex items-center justify-between mb-1">
  6272. <label class="text-[11px] font-semibold text-slate-300">Public Key</label>
  6273. <button type="button" onclick="copyWGInterfacePubKey()" class="text-[10px] text-cyan-400 hover:text-cyan-300">📋 Copy Public Key</button>
  6274. </div>
  6275. <input type="text" id="wg-iface-modal-pubkey" readonly class="w-full bg-slate-900 border border-slate-800 rounded-lg px-3 py-2 text-slate-300 font-mono text-[11px]">
  6276. </div>
  6277. <div>
  6278. <div class="flex items-center justify-between mb-1">
  6279. <label class="text-[11px] font-semibold text-slate-300">Private Key (Secret) *</label>
  6280. <button type="button" onclick="toggleWGPrivKeyVisibility()" id="btn-toggle-wg-privkey" class="text-[10px] text-slate-400 hover:text-white">👁️ Show</button>
  6281. </div>
  6282. <input type="password" id="wg-iface-modal-privkey" required oninput="onWGInterfacePrivKeyInput()" class="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono text-[11px] focus:outline-none focus:border-rose-500">
  6283. <p class="text-[10px] text-slate-500 mt-1">You can paste any existing base64 private key; the public key will update automatically.</p>
  6284. </div>
  6285. </div>
  6286. <div class="pt-1">
  6287. <label class="flex items-center space-x-2.5 cursor-pointer">
  6288. <input type="checkbox" id="wg-iface-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500 focus:ring-rose-500">
  6289. <span class="font-medium text-slate-300">Disabled (X)</span>
  6290. </label>
  6291. </div>
  6292. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  6293. <button type="button" onclick="closeAddWGInterfaceModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800 transition">Cancel</button>
  6294. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-rose-600 hover:bg-rose-500 text-white shadow-lg shadow-rose-500/20 transition">Save Interface</button>
  6295. </div>
  6296. </form>
  6297. </div>
  6298. </div>
  6299. <!-- ================= MODAL: ADD / EDIT WIREGUARD PEER ================= -->
  6300. <div id="wg-peer-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  6301. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  6302. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  6303. <div class="flex items-center gap-2">
  6304. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-rose-500/20 text-rose-300 border border-rose-500/30 tracking-wide uppercase">Peer Connection</span>
  6305. <h3 id="wg-peer-modal-title" class="text-sm font-bold text-white">Add WireGuard Peer</h3>
  6306. </div>
  6307. <button onclick="closeAddWGPeerModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  6308. </div>
  6309. <form onsubmit="submitSaveWGPeer(event)" class="p-6 space-y-4 text-xs">
  6310. <input type="hidden" id="wg-peer-modal-edit-orig-iface">
  6311. <input type="hidden" id="wg-peer-modal-edit-orig-pubkey">
  6312. <div class="grid grid-cols-2 gap-4">
  6313. <div>
  6314. <label class="block font-semibold text-slate-300 mb-1.5">WireGuard Interface *</label>
  6315. <select id="wg-peer-modal-iface" onchange="checkWGPeerKeyValidity()" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-rose-500"></select>
  6316. </div>
  6317. <div>
  6318. <label class="block font-semibold text-slate-300 mb-1.5">Peer Name / Alias *</label>
  6319. <input type="text" id="wg-peer-modal-name" placeholder="Laptop, iPhone, Branch-Athens" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-rose-500">
  6320. </div>
  6321. </div>
  6322. <div>
  6323. <div class="flex items-center justify-between mb-1.5">
  6324. <label class="font-semibold text-slate-300">Peer Public Key *</label>
  6325. <button type="button" onclick="generatePeerKeypairHelper()" class="text-[11px] text-cyan-400 hover:text-cyan-300 font-semibold flex items-center gap-1">
  6326. <span>⚡ Generate Client Keypair</span>
  6327. </button>
  6328. </div>
  6329. <input type="text" id="wg-peer-modal-pubkey" oninput="checkWGPeerKeyValidity()" placeholder="Base64 44-character public key" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-rose-500">
  6330. <!-- Warning if user tries to paste interface's own public key -->
  6331. <div id="wg-peer-key-warning" class="hidden text-rose-400 text-[11px] font-sans mt-1.5 bg-rose-950/50 p-2.5 rounded-lg border border-rose-800/70">
  6332. ⚠️ <strong>Invalid Key:</strong> The peer public key cannot be the same as the interface's own public key. A WireGuard node cannot connect to itself!
  6333. </div>
  6334. <!-- Box showing generated client secret key if generated here -->
  6335. <div id="wg-peer-gen-key-box" class="hidden mt-2.5 bg-slate-950/80 p-3 rounded-xl border border-cyan-800/60 text-xs font-mono space-y-1.5">
  6336. <div class="flex items-center justify-between text-[11px] text-cyan-300 font-bold font-sans">
  6337. <span>Generated Client Private Key (Secret):</span>
  6338. <button type="button" onclick="copyGeneratedPeerPrivKey()" class="text-white hover:text-cyan-300 font-semibold text-[10px] bg-slate-800 px-2 py-0.5 rounded border border-slate-700">📋 Copy Secret</button>
  6339. </div>
  6340. <input type="text" id="wg-peer-gen-privkey" readonly class="w-full bg-slate-900 border border-slate-800 rounded px-2.5 py-1.5 text-slate-300 text-xs font-mono">
  6341. <p class="text-[10px] text-slate-400 font-sans">Copy and save this private key to configure the client device (e.g. phone or remote router).</p>
  6342. </div>
  6343. </div>
  6344. <div>
  6345. <label class="block font-semibold text-slate-300 mb-1.5">Allowed IPs (CIDR) *</label>
  6346. <input type="text" id="wg-peer-modal-allowedips" placeholder="e.g. 10.10.0.2/32, 192.168.20.0/24 or 0.0.0.0/0" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-rose-500">
  6347. <p class="text-[10px] text-slate-500 mt-1">IP ranges routed to/from this peer (comma-separated)</p>
  6348. </div>
  6349. <div class="grid grid-cols-3 gap-3">
  6350. <div class="col-span-2">
  6351. <label class="block font-semibold text-slate-300 mb-1.5">Endpoint Host/IP (Optional)</label>
  6352. <input type="text" id="wg-peer-modal-endpoint" placeholder="e.g. 10.2.145.253 or vpn.example.com" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-rose-500">
  6353. </div>
  6354. <div>
  6355. <label class="block font-semibold text-slate-300 mb-1.5">Endpoint Port</label>
  6356. <input type="number" id="wg-peer-modal-port" placeholder="51820" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-rose-500">
  6357. </div>
  6358. </div>
  6359. <div class="grid grid-cols-2 gap-4">
  6360. <div>
  6361. <label class="block font-semibold text-slate-300 mb-1.5">Keepalive (seconds)</label>
  6362. <input type="number" id="wg-peer-modal-keepalive" value="25" min="0" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-rose-500">
  6363. <p class="text-[10px] text-slate-500 mt-1">25s recommended for NAT traversal</p>
  6364. </div>
  6365. <div>
  6366. <div class="flex items-center justify-between mb-1.5">
  6367. <label class="font-semibold text-slate-300">Preshared Key</label>
  6368. <button type="button" onclick="generatePeerPSK()" class="text-[10px] text-purple-400 hover:text-purple-300 font-semibold">Generate</button>
  6369. </div>
  6370. <input type="password" id="wg-peer-modal-preshared" placeholder="Optional PSK" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-rose-500">
  6371. </div>
  6372. </div>
  6373. <div class="pt-1">
  6374. <label class="flex items-center space-x-2.5 cursor-pointer">
  6375. <input type="checkbox" id="wg-peer-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500 focus:ring-rose-500">
  6376. <span class="font-medium text-slate-300">Disabled (X)</span>
  6377. </label>
  6378. </div>
  6379. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  6380. <button type="button" onclick="closeAddWGPeerModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800 transition">Cancel</button>
  6381. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-rose-600 hover:bg-rose-500 text-white shadow-lg shadow-rose-500/20 transition">Save Peer</button>
  6382. </div>
  6383. </form>
  6384. </div>
  6385. </div>
  6386. <!-- ================= MODAL: IPSEC TUNNEL (FORTIGATE PHASE 1 & PHASE 2 STYLE) ================= -->
  6387. <div id="ipsec-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  6388. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-2xl shadow-2xl overflow-hidden animate-in">
  6389. <!-- Modal Header -->
  6390. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  6391. <div class="flex items-center gap-2.5">
  6392. <div class="w-8 h-8 rounded-lg bg-amber-500/10 border border-amber-500/30 flex items-center justify-center text-amber-400">
  6393. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  6394. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
  6395. </svg>
  6396. </div>
  6397. <div>
  6398. <h3 id="ipsec-modal-title" class="text-sm font-bold text-white">IPsec Tunnel (FortiGate Style)</h3>
  6399. <p class="text-[11px] text-slate-400">Phase 1 (IKE Gateway) &amp; Phase 2 (Traffic Selectors / Multi-Subnet)</p>
  6400. </div>
  6401. </div>
  6402. <button onclick="closeAddIPSecModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  6403. </div>
  6404. <!-- FortiGate Phase Navigation Tabs -->
  6405. <div class="flex border-b border-slate-800 bg-slate-950/40 px-6 pt-2 gap-2 text-xs">
  6406. <button type="button" id="tab-btn-ipsec-p1" onclick="switchIpsecModalTab('p1')" class="px-4 py-2 font-bold rounded-t-lg transition border-b-2 border-amber-500 text-amber-300 bg-slate-900/80">
  6407. Phase 1 (Network &amp; IKE)
  6408. </button>
  6409. <button type="button" id="tab-btn-ipsec-p2" onclick="switchIpsecModalTab('p2')" class="px-4 py-2 font-medium rounded-t-lg transition text-slate-400 hover:text-white hover:bg-slate-800/60">
  6410. Phase 2 (Selectors &amp; Subnets)
  6411. </button>
  6412. </div>
  6413. <form onsubmit="submitSaveIPSec(event)" class="p-6 space-y-4 max-h-[75vh] overflow-y-auto text-xs">
  6414. <!-- PHASE 1 TAB CONTENT -->
  6415. <div id="ipsec-tab-content-p1" class="space-y-4">
  6416. <!-- General / Network -->
  6417. <div class="p-4 bg-slate-950/40 border border-slate-800 rounded-xl space-y-3">
  6418. <div class="text-[11px] font-bold text-amber-400 uppercase tracking-wider">Network &amp; Gateway</div>
  6419. <div class="grid grid-cols-2 gap-3">
  6420. <div>
  6421. <label class="block font-semibold text-slate-300 mb-1">Tunnel Name *</label>
  6422. <input type="text" id="ipsec-modal-name" placeholder="e.g. to_branch_office" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono focus:border-amber-500">
  6423. </div>
  6424. <div>
  6425. <label class="block font-semibold text-slate-300 mb-1">Remote Gateway IP / FQDN *</label>
  6426. <input type="text" id="ipsec-modal-remotegw" placeholder="e.g. 198.51.100.2 or vpn.remote.com" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono focus:border-amber-500">
  6427. </div>
  6428. </div>
  6429. <div class="grid grid-cols-2 gap-3">
  6430. <div>
  6431. <label class="block font-semibold text-slate-300 mb-1">Outgoing Interface</label>
  6432. <select id="ipsec-modal-iface" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6433. <option value="%any">Auto (%any)</option>
  6434. </select>
  6435. </div>
  6436. <div>
  6437. <label class="block font-semibold text-slate-300 mb-1">Local Gateway IP (Optional)</label>
  6438. <input type="text" id="ipsec-modal-localaddrs" placeholder="%any (Default)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6439. </div>
  6440. </div>
  6441. <div>
  6442. <label class="block font-semibold text-slate-300 mb-1">Comments</label>
  6443. <input type="text" id="ipsec-modal-comments" placeholder="P2P Tunnel to Branch Office FortiGate" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white">
  6444. </div>
  6445. </div>
  6446. <!-- Authentication -->
  6447. <div class="p-4 bg-slate-950/40 border border-slate-800 rounded-xl space-y-3">
  6448. <div class="text-[11px] font-bold text-amber-400 uppercase tracking-wider">Authentication</div>
  6449. <div class="grid grid-cols-3 gap-3">
  6450. <div>
  6451. <label class="block font-semibold text-slate-300 mb-1">IKE Version</label>
  6452. <select id="ipsec-modal-ikeversion" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6453. <option value="ikev2" selected>IKEv2 (Recommended)</option>
  6454. <option value="ikev1">IKEv1</option>
  6455. </select>
  6456. </div>
  6457. <div class="col-span-2">
  6458. <label class="block font-semibold text-slate-300 mb-1">Pre-Shared Key (PSK) *</label>
  6459. <div class="relative">
  6460. <input type="password" id="ipsec-modal-psk" placeholder="Enter secure pre-shared key" required class="w-full bg-slate-800 border border-slate-700 rounded-lg pl-3 pr-10 py-1.5 text-white font-mono focus:border-amber-500">
  6461. <button type="button" onclick="toggleIpsecPskVisibility()" class="absolute right-2.5 top-2 text-slate-400 hover:text-white" title="Show/Hide Key">
  6462. <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  6463. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
  6464. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
  6465. </svg>
  6466. </button>
  6467. </div>
  6468. </div>
  6469. </div>
  6470. <div class="grid grid-cols-2 gap-3">
  6471. <div>
  6472. <label class="block font-medium text-slate-400 mb-1">Local ID (Optional)</label>
  6473. <input type="text" id="ipsec-modal-localid" placeholder="e.g. my-router-id" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6474. </div>
  6475. <div>
  6476. <label class="block font-medium text-slate-400 mb-1">Remote ID (Optional)</label>
  6477. <input type="text" id="ipsec-modal-remoteid" placeholder="e.g. remote-router-id" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6478. </div>
  6479. </div>
  6480. </div>
  6481. <!-- Phase 1 Proposals & Timers -->
  6482. <div class="p-4 bg-slate-950/40 border border-slate-800 rounded-xl space-y-3">
  6483. <div class="text-[11px] font-bold text-amber-400 uppercase tracking-wider">Phase 1 Cryptographic Proposals</div>
  6484. <div class="grid grid-cols-2 gap-3">
  6485. <div>
  6486. <label class="block font-semibold text-slate-300 mb-1">IKE Proposals</label>
  6487. <input type="text" id="ipsec-modal-ikeprop" value="aes256-sha256-modp2048,default" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6488. <p class="text-[10px] text-slate-500 mt-0.5">e.g. aes256-sha256-modp2048,default</p>
  6489. </div>
  6490. <div>
  6491. <label class="block font-semibold text-slate-300 mb-1">Diffie-Hellman Group</label>
  6492. <select id="ipsec-modal-dhgroup" onchange="updateIpsecIkeProposalFromDH(this.value)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6493. <option value="modp2048" selected>Group 14 (2048-bit MODP)</option>
  6494. <option value="ecp256">Group 19 (256-bit ECP)</option>
  6495. <option value="ecp384">Group 20 (384-bit ECP)</option>
  6496. <option value="modp1536">Group 5 (1536-bit MODP)</option>
  6497. <option value="modp1024">Group 2 (1024-bit MODP)</option>
  6498. </select>
  6499. </div>
  6500. </div>
  6501. <div class="grid grid-cols-2 gap-3">
  6502. <div>
  6503. <label class="block font-semibold text-slate-300 mb-1">IKE Keylife (seconds)</label>
  6504. <input type="number" id="ipsec-modal-ikekeylife" value="86400" min="60" max="604800" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6505. </div>
  6506. <div>
  6507. <label class="block font-semibold text-slate-300 mb-1">Dead Peer Detection (DPD)</label>
  6508. <select id="ipsec-modal-dpd" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6509. <option value="on-demand" selected>On-Demand (Recommended)</option>
  6510. <option value="periodic">Periodic</option>
  6511. <option value="disabled">Disabled</option>
  6512. </select>
  6513. </div>
  6514. </div>
  6515. </div>
  6516. </div>
  6517. <!-- PHASE 2 TAB CONTENT -->
  6518. <div id="ipsec-tab-content-p2" class="hidden space-y-4">
  6519. <!-- Phase 2 Parameters -->
  6520. <div class="p-4 bg-slate-950/40 border border-slate-800 rounded-xl space-y-3">
  6521. <div class="text-[11px] font-bold text-amber-400 uppercase tracking-wider">Phase 2 Parameters &amp; PFS</div>
  6522. <div class="grid grid-cols-2 gap-3">
  6523. <div>
  6524. <label class="block font-semibold text-slate-300 mb-1">Phase 2 Name</label>
  6525. <input type="text" id="ipsec-modal-p2name" placeholder="Auto: <Name>_p2" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6526. </div>
  6527. <div>
  6528. <label class="block font-semibold text-slate-300 mb-1">ESP Keylife (seconds)</label>
  6529. <input type="number" id="ipsec-modal-espkeylife" value="28800" min="60" max="604800" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6530. </div>
  6531. </div>
  6532. <div class="grid grid-cols-2 gap-3">
  6533. <div>
  6534. <label class="block font-semibold text-slate-300 mb-1">ESP Proposals</label>
  6535. <input type="text" id="ipsec-modal-espprop" value="aes256-sha256-modp2048,default" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6536. <p class="text-[10px] text-slate-500 mt-0.5">e.g. aes256-sha256-modp2048,default</p>
  6537. </div>
  6538. <div>
  6539. <label class="block font-semibold text-slate-300 mb-1">PFS Key Group (Perfect Forward Secrecy)</label>
  6540. <select id="ipsec-modal-pfsgroup" onchange="updateIpsecEspProposalFromPFS(this.value)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  6541. <option value="modp2048" selected>Group 14 (2048-bit MODP)</option>
  6542. <option value="ecp256">Group 19 (256-bit ECP)</option>
  6543. <option value="ecp384">Group 20 (384-bit ECP)</option>
  6544. <option value="none">None (No PFS)</option>
  6545. </select>
  6546. </div>
  6547. </div>
  6548. <div class="flex items-center gap-6 pt-1">
  6549. <label class="flex items-center space-x-2 cursor-pointer">
  6550. <input type="checkbox" id="ipsec-modal-autostart" checked class="rounded bg-slate-800 border-slate-700 text-amber-500">
  6551. <span class="font-medium text-slate-300">Auto-negotiate / Always-On (Start)</span>
  6552. </label>
  6553. <label class="flex items-center space-x-2 cursor-pointer">
  6554. <input type="checkbox" id="ipsec-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  6555. <span class="font-medium text-slate-300">Disabled (X)</span>
  6556. </label>
  6557. </div>
  6558. </div>
  6559. <!-- PHASE 2 SELECTORS: DYNAMIC MULTI-SUBNETS (FortiGate Style) -->
  6560. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  6561. <!-- Local Subnets (Local Address / Selectors) -->
  6562. <div class="p-4 bg-slate-950/60 border border-slate-800 rounded-xl space-y-3">
  6563. <div class="flex items-center justify-between">
  6564. <div>
  6565. <div class="text-[11px] font-bold text-cyan-400 uppercase tracking-wider">Local Subnets (Selectors)</div>
  6566. <p class="text-[10px] text-slate-400">Subnets on this router to encrypt</p>
  6567. </div>
  6568. <button type="button" onclick="addIpsecLocalSubnet('')" class="px-2 py-1 text-[11px] font-semibold bg-cyan-600 hover:bg-cyan-500 text-white rounded shadow-sm flex items-center gap-1">
  6569. <span>+ Subnet</span>
  6570. </button>
  6571. </div>
  6572. <!-- Container for dynamic local subnets -->
  6573. <div id="ipsec-modal-local-subnets-container" class="space-y-2 max-h-48 overflow-y-auto pr-1"></div>
  6574. </div>
  6575. <!-- Remote Subnets (Remote Address / Selectors) -->
  6576. <div class="p-4 bg-slate-950/60 border border-slate-800 rounded-xl space-y-3">
  6577. <div class="flex items-center justify-between">
  6578. <div>
  6579. <div class="text-[11px] font-bold text-purple-400 uppercase tracking-wider">Remote Subnets (Selectors)</div>
  6580. <p class="text-[10px] text-slate-400">Subnets on remote router to access</p>
  6581. </div>
  6582. <button type="button" onclick="addIpsecRemoteSubnet('')" class="px-2 py-1 text-[11px] font-semibold bg-purple-600 hover:bg-purple-500 text-white rounded shadow-sm flex items-center gap-1">
  6583. <span>+ Subnet</span>
  6584. </button>
  6585. </div>
  6586. <!-- Container for dynamic remote subnets -->
  6587. <div id="ipsec-modal-remote-subnets-container" class="space-y-2 max-h-48 overflow-y-auto pr-1"></div>
  6588. </div>
  6589. </div>
  6590. </div>
  6591. <!-- Footer -->
  6592. <div class="pt-4 border-t border-slate-800 flex items-center justify-between">
  6593. <button type="button" onclick="closeAddIPSecModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800">Cancel</button>
  6594. <div class="flex items-center space-x-2">
  6595. <button type="button" id="btn-ipsec-switch-step" onclick="toggleIpsecPhaseStep()" class="px-4 py-2 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-amber-300 border border-slate-700">
  6596. Next: Phase 2 Selectors &rarr;
  6597. </button>
  6598. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-bold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20">
  6599. Save Tunnel (OK)
  6600. </button>
  6601. </div>
  6602. </div>
  6603. </form>
  6604. </div>
  6605. </div>
  6606. <!-- ================= MODAL: L2TP CLIENT ================= -->
  6607. <div id="l2tp-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  6608. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  6609. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  6610. <h3 id="l2tp-modal-title" class="text-sm font-bold text-white">Add L2TP Client Interface</h3>
  6611. <button onclick="closeL2TPModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  6612. </div>
  6613. <form onsubmit="submitSaveL2TP(event)" class="p-6 space-y-4">
  6614. <div>
  6615. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Client Interface Name *</label>
  6616. <input type="text" id="l2tp-modal-name" placeholder="l2tp-out1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  6617. </div>
  6618. <div>
  6619. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Connect To (Server IP / Host) *</label>
  6620. <input type="text" id="l2tp-modal-server" placeholder="vpn.example.com or 203.0.113.1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  6621. </div>
  6622. <div class="grid grid-cols-2 gap-4">
  6623. <div>
  6624. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Username *</label>
  6625. <input type="text" id="l2tp-modal-user" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  6626. </div>
  6627. <div>
  6628. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Password *</label>
  6629. <input type="password" id="l2tp-modal-pass" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  6630. </div>
  6631. </div>
  6632. <div class="grid grid-cols-2 gap-4">
  6633. <div>
  6634. <label class="block text-xs font-semibold text-slate-300 mb-1.5">PPP Profile</label>
  6635. <select id="l2tp-modal-profile" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  6636. <option value="default-encryption" selected>default-encryption (MPPE-128)</option>
  6637. <option value="default">default (No encryption)</option>
  6638. </select>
  6639. </div>
  6640. <div>
  6641. <label class="block text-xs font-semibold text-slate-300 mb-1.5">IPSec Secret / PSK (Optional)</label>
  6642. <input type="password" id="l2tp-modal-ipsec" placeholder="Optional Pre-Shared Key" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono">
  6643. </div>
  6644. </div>
  6645. <div class="grid grid-cols-2 gap-4 pt-2">
  6646. <label class="flex items-center space-x-2 cursor-pointer">
  6647. <input type="checkbox" id="l2tp-modal-defroute" checked class="rounded bg-slate-800 border-slate-700 text-cyan-500">
  6648. <span class="text-xs text-slate-300 font-medium">Default Route</span>
  6649. </label>
  6650. <label class="flex items-center space-x-2 cursor-pointer">
  6651. <input type="checkbox" id="l2tp-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  6652. <span class="text-xs text-slate-300 font-medium">Disabled (X)</span>
  6653. </label>
  6654. </div>
  6655. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  6656. <button type="button" onclick="closeL2TPModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800">Cancel</button>
  6657. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20">Save L2TP</button>
  6658. </div>
  6659. </form>
  6660. </div>
  6661. </div>
  6662. <!-- ================= MODAL: PPTP CLIENT ================= -->
  6663. <div id="pptp-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  6664. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  6665. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  6666. <h3 id="pptp-modal-title" class="text-sm font-bold text-white">Add PPTP Client Interface</h3>
  6667. <button onclick="closePPTPModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  6668. </div>
  6669. <form onsubmit="submitSavePPTP(event)" class="p-6 space-y-4">
  6670. <div>
  6671. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Client Interface Name *</label>
  6672. <input type="text" id="pptp-modal-name" placeholder="pptp-out1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-purple-500">
  6673. </div>
  6674. <div>
  6675. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Connect To (Server IP / Host) *</label>
  6676. <input type="text" id="pptp-modal-server" placeholder="vpn.example.com or 203.0.113.1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-purple-500">
  6677. </div>
  6678. <div class="grid grid-cols-2 gap-4">
  6679. <div>
  6680. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Username *</label>
  6681. <input type="text" id="pptp-modal-user" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-purple-500">
  6682. </div>
  6683. <div>
  6684. <label class="block text-xs font-semibold text-slate-300 mb-1.5">Password *</label>
  6685. <input type="password" id="pptp-modal-pass" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-purple-500">
  6686. </div>
  6687. </div>
  6688. <div>
  6689. <label class="block text-xs font-semibold text-slate-300 mb-1.5">PPP Profile</label>
  6690. <select id="pptp-modal-profile" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-purple-500">
  6691. <option value="default-encryption" selected>default-encryption (MPPE-128)</option>
  6692. <option value="default">default (No encryption)</option>
  6693. </select>
  6694. </div>
  6695. <div class="grid grid-cols-3 gap-2 pt-2 border-t border-slate-800/80">
  6696. <label class="flex items-center space-x-2 cursor-pointer">
  6697. <input type="checkbox" id="pptp-modal-mppe" checked class="rounded bg-slate-800 border-slate-700 text-purple-500">
  6698. <span class="text-xs text-slate-300 font-medium">MPPE-128</span>
  6699. </label>
  6700. <label class="flex items-center space-x-2 cursor-pointer">
  6701. <input type="checkbox" id="pptp-modal-defroute" checked class="rounded bg-slate-800 border-slate-700 text-purple-500">
  6702. <span class="text-xs text-slate-300 font-medium">Default Route</span>
  6703. </label>
  6704. <label class="flex items-center space-x-2 cursor-pointer">
  6705. <input type="checkbox" id="pptp-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  6706. <span class="text-xs text-slate-300 font-medium">Disabled (X)</span>
  6707. </label>
  6708. </div>
  6709. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  6710. <button type="button" onclick="closePPTPModal()" class="px-4 py-2 rounded-lg text-xs font-medium text-slate-400 hover:bg-slate-800">Cancel</button>
  6711. <button type="submit" class="px-5 py-2 rounded-lg text-xs font-semibold bg-purple-600 hover:bg-purple-500 text-white shadow-lg shadow-purple-500/20">Save PPTP</button>
  6712. </div>
  6713. </form>
  6714. </div>
  6715. </div>
  6716. <!-- ================= MODAL: FIREWALL FILTER RULE ================= -->
  6717. <div id="filter-modal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] z-50 hidden flex items-center justify-center p-2 sm:p-4">
  6718. <div id="filter-modal-window" class="bg-[#131b26] border border-[#243347] rounded-md shadow-2xl w-full max-w-[720px] max-h-[92vh] flex flex-col overflow-hidden text-xs select-none">
  6719. <!-- Window Title Bar -->
  6720. <div class="bg-[#162335] border-b border-[#223145] px-3.5 py-1.5 flex items-center justify-between shrink-0">
  6721. <div class="flex items-center gap-2">
  6722. <span class="text-slate-200 font-medium text-xs">Filter rule</span>
  6723. <span id="fw-modal-badge" class="bg-[#0078d4] text-white text-[11px] px-2 py-0.5 rounded font-mono font-medium">New...</span>
  6724. </div>
  6725. <div class="flex items-center gap-2">
  6726. <button type="button" onclick="toggleMaximizeFilterModal()" title="Maximize / Restore" class="text-slate-400 hover:text-white p-0.5 text-xs transition-colors">
  6727. <svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
  6728. </button>
  6729. <button type="button" onclick="closeFilterModal()" title="Close" class="text-slate-400 hover:text-rose-400 p-0.5 text-xs font-bold leading-none transition-colors">✕</button>
  6730. </div>
  6731. </div>
  6732. <!-- Window Tabs Bar -->
  6733. <div class="bg-[#111923] border-b border-[#1f2d3e] px-2 flex items-center gap-1 shrink-0">
  6734. <button type="button" onclick="switchFwRuleTab('general')" id="fw-tab-btn-general" class="fw-rule-tab-btn bg-[#182638] text-white border-t-2 border-sky-400 px-3.5 py-1.5 text-xs font-medium transition-colors">General</button>
  6735. <button type="button" onclick="switchFwRuleTab('advanced')" id="fw-tab-btn-advanced" class="fw-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Advanced</button>
  6736. <button type="button" onclick="switchFwRuleTab('extra')" id="fw-tab-btn-extra" class="fw-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Extra</button>
  6737. <button type="button" onclick="switchFwRuleTab('action')" id="fw-tab-btn-action" class="fw-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Target</button>
  6738. <button type="button" onclick="switchFwRuleTab('statistics')" id="fw-tab-btn-statistics" class="fw-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Statistics</button>
  6739. </div>
  6740. <!-- Window Body (Split Layout: Left Tab Content + Right Action Sidebar) -->
  6741. <div class="flex-1 flex min-h-0 overflow-hidden">
  6742. <!-- Form Content Container (Scrollable) -->
  6743. <form id="fw-rule-form" onsubmit="event.preventDefault(); applyFilterRule(true);" class="flex-1 overflow-y-auto px-4 py-3 min-w-0 space-y-1">
  6744. <input type="hidden" id="filter-modal-id">
  6745. <label class="block text-xs text-slate-300">IP family<select id="filter-modal-family" onchange="refreshFirewallRejectTypes()" class="bg-slate-800 rounded p-2 ml-2"><option value="4">IPv4</option><option value="6">IPv6</option></select></label>
  6746. <!-- TAB PANE: GENERAL -->
  6747. <div id="fw-pane-general" class="fw-rule-pane space-y-1">
  6748. <!-- Enabled -->
  6749. <div class="flex items-center text-xs py-1">
  6750. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Enabled</label>
  6751. <div class="flex items-center">
  6752. <input type="checkbox" id="filter-modal-enabled" checked class="rounded bg-[#15202e] border-[#26374d] text-sky-500 focus:ring-0 w-4 h-4 cursor-pointer">
  6753. </div>
  6754. </div>
  6755. <!-- Comment -->
  6756. <div class="flex items-center text-xs py-1">
  6757. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Comment</label>
  6758. <div class="flex-1">
  6759. <input type="text" id="filter-modal-comment" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full">
  6760. </div>
  6761. </div>
  6762. <!-- Chain -->
  6763. <div class="flex items-center text-xs py-1">
  6764. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Chain</label>
  6765. <div class="flex-1">
  6766. <select id="filter-modal-chain-select" onchange="syncFilterChainFromSelect()" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-sky-400 font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  6767. <option value="forward" selected>FORWARD (routed through router)</option>
  6768. <option value="input">INPUT (destined to this router)</option>
  6769. <option value="output">OUTPUT (generated by this router)</option>
  6770. <option value="__custom__">+ Custom Chain...</option>
  6771. </select>
  6772. <div id="filter-modal-custom-wrap" class="hidden mt-1.5 flex items-center gap-1.5">
  6773. <span class="text-[10px] text-amber-400 font-mono uppercase tracking-wider shrink-0">Custom Chain:</span>
  6774. <input type="text" id="filter-modal-custom-input" oninput="syncFilterCustomChainInput()" placeholder="Enter custom chain name (1-16 chars)" maxlength="16" class="bg-[#15202e] border border-amber-500/50 rounded px-2.5 py-1 text-xs text-amber-300 font-mono focus:outline-none focus:border-amber-400 w-full">
  6775. </div>
  6776. <input type="hidden" id="filter-modal-chain" value="forward">
  6777. <datalist id="filter-chain-options">
  6778. <option value="forward">forward</option>
  6779. <option value="input">input</option>
  6780. <option value="output">output</option>
  6781. </datalist>
  6782. <p id="filter-chain-hint" class="mt-1 text-[11px] text-slate-400">FORWARD: packets routed through this router (accepts both In and Out interfaces).</p>
  6783. <div id="filter-action-incompatible-warn" class="hidden mt-1.5 p-2 bg-amber-950/40 border border-amber-600/40 rounded text-amber-300 text-[11px] flex items-center gap-2">
  6784. <svg class="w-4 h-4 text-amber-400 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>
  6785. <span id="filter-action-incompatible-msg" class="flex-1 font-mono"></span>
  6786. </div>
  6787. </div>
  6788. </div>
  6789. <!-- Src. Address -->
  6790. <div class="flex items-center text-xs py-1" id="fw-row-src">
  6791. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Address</label>
  6792. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6793. <button type="button" onclick="toggleFwField('src', true)" id="fw-plus-src" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6794. <div id="fw-field-src" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6795. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6796. <input type="checkbox" id="fw-inv-src" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6797. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6798. </label>
  6799. <input type="text" id="fw-val-src" placeholder="192.168.1.0/24" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  6800. <button type="button" onclick="toggleFwField('src', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6801. </div>
  6802. </div>
  6803. </div>
  6804. <!-- Dst. Address -->
  6805. <div class="flex items-center text-xs py-1" id="fw-row-dst">
  6806. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Address</label>
  6807. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6808. <button type="button" onclick="toggleFwField('dst', true)" id="fw-plus-dst" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6809. <div id="fw-field-dst" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6810. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6811. <input type="checkbox" id="fw-inv-dst" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6812. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6813. </label>
  6814. <input type="text" id="fw-val-dst" placeholder="0.0.0.0/0" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  6815. <button type="button" onclick="toggleFwField('dst', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6816. </div>
  6817. </div>
  6818. </div>
  6819. <!-- Src. Address List -->
  6820. <div class="flex items-center text-xs py-1" id="fw-row-srclist">
  6821. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Address List</label>
  6822. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6823. <button type="button" onclick="toggleFwField('srclist', true)" id="fw-plus-srclist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6824. <div id="fw-field-srclist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6825. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6826. <input type="checkbox" id="fw-inv-srclist" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6827. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6828. </label>
  6829. <select id="fw-val-srclist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  6830. <button type="button" onclick="toggleFwField('srclist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6831. </div>
  6832. </div>
  6833. </div>
  6834. <!-- Dst. Address List -->
  6835. <div class="flex items-center text-xs py-1" id="fw-row-dstlist">
  6836. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Address List</label>
  6837. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6838. <button type="button" onclick="toggleFwField('dstlist', true)" id="fw-plus-dstlist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6839. <div id="fw-field-dstlist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6840. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6841. <input type="checkbox" id="fw-inv-dstlist" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6842. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6843. </label>
  6844. <select id="fw-val-dstlist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  6845. <button type="button" onclick="toggleFwField('dstlist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6846. </div>
  6847. </div>
  6848. </div>
  6849. <!-- Src. Geo Location -->
  6850. <div class="flex items-center text-xs py-1" id="fw-row-srcgeo">
  6851. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Geo Location</label>
  6852. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6853. <button type="button" onclick="toggleFwField('srcgeo', true)" id="fw-plus-srcgeo" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6854. <div id="fw-field-srcgeo" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6855. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6856. <input type="checkbox" id="fw-inv-srcgeo" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6857. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6858. </label>
  6859. <select id="fw-val-srcgeo" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  6860. <button type="button" onclick="toggleFwField('srcgeo', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6861. </div>
  6862. </div>
  6863. </div>
  6864. <!-- Dst. Geo Location -->
  6865. <div class="flex items-center text-xs py-1" id="fw-row-dstgeo">
  6866. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Geo Location</label>
  6867. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6868. <button type="button" onclick="toggleFwField('dstgeo', true)" id="fw-plus-dstgeo" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6869. <div id="fw-field-dstgeo" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6870. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6871. <input type="checkbox" id="fw-inv-dstgeo" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6872. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6873. </label>
  6874. <select id="fw-val-dstgeo" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  6875. <button type="button" onclick="toggleFwField('dstgeo', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6876. </div>
  6877. </div>
  6878. </div>
  6879. <hr class="border-[#213042] my-2">
  6880. <!-- Protocol -->
  6881. <div class="flex items-center text-xs py-1" id="fw-row-proto">
  6882. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Protocol</label>
  6883. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6884. <button type="button" onclick="toggleFwField('proto', true)" id="fw-plus-proto" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6885. <div id="fw-field-proto" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6886. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6887. <input type="checkbox" id="fw-inv-proto" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6888. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6889. </label>
  6890. <select id="fw-val-proto" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  6891. <option value="tcp">6 (tcp)</option>
  6892. <option value="udp">17 (udp)</option>
  6893. <option value="icmp">ICMP (family-specific)</option><option value="ipv6-icmp">ICMPv6</option>
  6894. <option value="gre">47 (gre)</option>
  6895. <option value="esp">50 (esp)</option>
  6896. <option value="ipip">4 (ipip)</option>
  6897. <option value="vrrp">112 (vrrp)</option>
  6898. <option value="all">all</option>
  6899. </select>
  6900. <button type="button" onclick="toggleFwField('proto', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6901. </div>
  6902. </div>
  6903. </div>
  6904. <!-- Src. Port -->
  6905. <div class="flex items-center text-xs py-1" id="fw-row-sport">
  6906. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Port</label>
  6907. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6908. <button type="button" onclick="toggleFwField('sport', true)" id="fw-plus-sport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6909. <div id="fw-field-sport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6910. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6911. <input type="checkbox" id="fw-inv-sport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6912. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6913. </label>
  6914. <input type="text" id="fw-val-sport" placeholder="80 or 1024-65535" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  6915. <button type="button" onclick="toggleFwField('sport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6916. </div>
  6917. </div>
  6918. </div>
  6919. <!-- Dst. Port -->
  6920. <div class="flex items-center text-xs py-1" id="fw-row-dport">
  6921. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Port</label>
  6922. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6923. <button type="button" onclick="toggleFwField('dport', true)" id="fw-plus-dport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6924. <div id="fw-field-dport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6925. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6926. <input type="checkbox" id="fw-inv-dport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6927. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6928. </label>
  6929. <input type="text" id="fw-val-dport" placeholder="80,443" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  6930. <button type="button" onclick="toggleFwField('dport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6931. </div>
  6932. </div>
  6933. </div>
  6934. <!-- Any. Port -->
  6935. <div class="flex items-center text-xs py-1" id="fw-row-anyport">
  6936. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Any. Port</label>
  6937. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6938. <button type="button" onclick="toggleFwField('anyport', true)" id="fw-plus-anyport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6939. <div id="fw-field-anyport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6940. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6941. <input type="checkbox" id="fw-inv-anyport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6942. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6943. </label>
  6944. <input type="text" id="fw-val-anyport" placeholder="53" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  6945. <button type="button" onclick="toggleFwField('anyport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6946. </div>
  6947. </div>
  6948. </div>
  6949. <!-- In. Interface -->
  6950. <div class="flex items-center text-xs py-1" id="fw-row-iniface">
  6951. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Interface</label>
  6952. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6953. <button type="button" onclick="toggleFwField('iniface', true)" id="fw-plus-iniface" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6954. <div id="fw-field-iniface" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6955. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6956. <input type="checkbox" id="fw-inv-iniface" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6957. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6958. </label>
  6959. <select id="fw-val-iniface" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  6960. <button type="button" onclick="toggleFwField('iniface', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6961. </div>
  6962. </div>
  6963. </div>
  6964. <!-- Out. Interface -->
  6965. <div class="flex items-center text-xs py-1" id="fw-row-outiface">
  6966. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Interface</label>
  6967. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6968. <button type="button" onclick="toggleFwField('outiface', true)" id="fw-plus-outiface" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6969. <div id="fw-field-outiface" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6970. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  6971. <input type="checkbox" id="fw-inv-outiface" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  6972. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  6973. </label>
  6974. <select id="fw-val-outiface" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  6975. <button type="button" onclick="toggleFwField('outiface', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6976. </div>
  6977. </div>
  6978. </div>
  6979. <hr class="border-[#213042] my-2">
  6980. <!-- In. Interface List -->
  6981. <div class="flex items-center text-xs py-1" id="fw-row-inifacelist">
  6982. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Interface List</label>
  6983. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6984. <button type="button" onclick="toggleFwField('inifacelist', true)" id="fw-plus-inifacelist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6985. <div id="fw-field-inifacelist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  6986. <select id="fw-val-inifacelist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  6987. <option value="LAN">LAN</option>
  6988. <option value="WAN">WAN</option>
  6989. </select>
  6990. <button type="button" onclick="toggleFwField('inifacelist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  6991. </div>
  6992. </div>
  6993. </div>
  6994. <!-- Out. Interface List -->
  6995. <div class="flex items-center text-xs py-1" id="fw-row-outifacelist">
  6996. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Interface List</label>
  6997. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  6998. <button type="button" onclick="toggleFwField('outifacelist', true)" id="fw-plus-outifacelist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  6999. <div id="fw-field-outifacelist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7000. <select id="fw-val-outifacelist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7001. <option value="LAN">LAN</option>
  7002. <option value="WAN">WAN</option>
  7003. </select>
  7004. <button type="button" onclick="toggleFwField('outifacelist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7005. </div>
  7006. </div>
  7007. </div>
  7008. <hr class="border-[#213042] my-2">
  7009. <!-- Packet Mark -->
  7010. <div class="flex items-center text-xs py-1" id="fw-row-pktmark">
  7011. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Mark</label>
  7012. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7013. <button type="button" onclick="toggleFwField('pktmark', true)" id="fw-plus-pktmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7014. <div id="fw-field-pktmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7015. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7016. <input type="checkbox" id="fw-inv-pktmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7017. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7018. </label>
  7019. <input type="text" id="fw-val-pktmark" placeholder="e.g. qos_voip" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7020. <button type="button" onclick="toggleFwField('pktmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7021. </div>
  7022. </div>
  7023. </div>
  7024. <!-- Connection Mark -->
  7025. <div class="flex items-center text-xs py-1" id="fw-row-connmark">
  7026. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection Mark</label>
  7027. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7028. <button type="button" onclick="toggleFwField('connmark', true)" id="fw-plus-connmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7029. <div id="fw-field-connmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7030. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7031. <input type="checkbox" id="fw-inv-connmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7032. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7033. </label>
  7034. <input type="text" id="fw-val-connmark" placeholder="e.g. wan1_conn" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7035. <button type="button" onclick="toggleFwField('connmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7036. </div>
  7037. </div>
  7038. </div>
  7039. <!-- Routing Mark -->
  7040. <div class="flex items-center text-xs py-1" id="fw-row-routingmark">
  7041. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Routing Mark</label>
  7042. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7043. <button type="button" onclick="toggleFwField('routingmark', true)" id="fw-plus-routingmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7044. <div id="fw-field-routingmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7045. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7046. <input type="checkbox" id="fw-inv-routingmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7047. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7048. </label>
  7049. <input type="text" id="fw-val-routingmark" placeholder="e.g. to_wan2" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7050. <button type="button" onclick="toggleFwField('routingmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7051. </div>
  7052. </div>
  7053. </div>
  7054. <hr class="border-[#213042] my-2">
  7055. <!-- Connection Type -->
  7056. <div class="flex items-center text-xs py-1" id="fw-row-conntype">
  7057. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection Type</label>
  7058. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7059. <button type="button" onclick="toggleFwField('conntype', true)" id="fw-plus-conntype" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7060. <div id="fw-field-conntype" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7061. <select id="fw-val-conntype" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7062. <option value="ftp">ftp</option>
  7063. <option value="h323">h323</option>
  7064. <option value="irc">irc</option>
  7065. <option value="pptp">pptp</option>
  7066. <option value="sip">sip</option>
  7067. <option value="tftp">tftp</option>
  7068. </select>
  7069. <button type="button" onclick="toggleFwField('conntype', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7070. </div>
  7071. </div>
  7072. </div>
  7073. <!-- Connection State -->
  7074. <div class="flex items-center text-xs py-1" id="fw-row-cs">
  7075. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection State</label>
  7076. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7077. <button type="button" onclick="toggleFwField('cs', true)" id="fw-plus-cs" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7078. <div id="fw-field-cs" class="hidden flex-1 flex flex-wrap items-center gap-2 min-w-0 bg-[#15202e] border border-[#26374d] rounded px-2 py-1">
  7079. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="filter-modal-cs-established" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">established</span></label>
  7080. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="filter-modal-cs-related" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">related</span></label>
  7081. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="filter-modal-cs-new" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">new</span></label>
  7082. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="filter-modal-cs-invalid" class="rounded bg-[#101721] border-[#2b3e56] text-rose-500"><span class="text-slate-300">invalid</span></label>
  7083. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="filter-modal-cs-untracked" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500"><span class="text-slate-300">untracked</span></label>
  7084. <button type="button" onclick="toggleFwField('cs', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0 ml-auto" title="Remove / Clear">-</button>
  7085. </div>
  7086. </div>
  7087. </div>
  7088. <!-- Connection NAT State -->
  7089. <div class="flex items-center text-xs py-1" id="fw-row-natstate">
  7090. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection NAT State</label>
  7091. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7092. <button type="button" onclick="toggleFwField('natstate', true)" id="fw-plus-natstate" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7093. <div id="fw-field-natstate" class="hidden flex-1 flex items-center gap-3 min-w-0 bg-[#15202e] border border-[#26374d] rounded px-2 py-1">
  7094. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="filter-modal-nat-srcnat" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">SNAT</span></label>
  7095. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="filter-modal-nat-dstnat" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">DNAT</span></label>
  7096. <button type="button" onclick="toggleFwField('natstate', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0 ml-auto" title="Remove / Clear">-</button>
  7097. </div>
  7098. </div>
  7099. </div>
  7100. </div>
  7101. <!-- TAB PANE: ADVANCED (Screenshot 2) -->
  7102. <div id="fw-pane-advanced" class="fw-rule-pane hidden space-y-1">
  7103. <!-- Layer7 Protocol -->
  7104. <!-- Content -->
  7105. <div class="flex items-center text-xs py-1" id="fw-row-content">
  7106. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Content</label>
  7107. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7108. <button type="button" onclick="toggleFwField('content', true)" id="fw-plus-content" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7109. <div id="fw-field-content" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7110. <input type="text" id="fw-val-content" placeholder="e.g. facebook.com" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7111. <button type="button" onclick="toggleFwField('content', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7112. </div>
  7113. </div>
  7114. </div>
  7115. <!-- Connection Bytes -->
  7116. <!-- Connection Rate -->
  7117. <!-- Per Connection Classifier -->
  7118. <!-- Src. MAC Address -->
  7119. <div class="flex items-center text-xs py-1" id="fw-row-srcmac">
  7120. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. MAC Address</label>
  7121. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7122. <button type="button" onclick="toggleFwField('srcmac', true)" id="fw-plus-srcmac" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7123. <div id="fw-field-srcmac" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7124. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7125. <input type="checkbox" id="fw-inv-srcmac" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7126. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7127. </label>
  7128. <input type="text" id="fw-val-srcmac" placeholder="e.g. 00:11:22:33:44:55" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7129. <button type="button" onclick="toggleFwField('srcmac', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7130. </div>
  7131. </div>
  7132. </div>
  7133. <hr class="border-[#213042] my-2">
  7134. <!-- Out. Bridge Port -->
  7135. <div class="flex items-center text-xs py-1" id="fw-row-outbridgeport">
  7136. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Bridge Port</label>
  7137. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7138. <button type="button" onclick="toggleFwField('outbridgeport', true)" id="fw-plus-outbridgeport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7139. <div id="fw-field-outbridgeport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7140. <input type="text" id="fw-val-outbridgeport" placeholder="e.g. eth1" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7141. <button type="button" onclick="toggleFwField('outbridgeport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7142. </div>
  7143. </div>
  7144. </div>
  7145. <!-- In. Bridge Port -->
  7146. <div class="flex items-center text-xs py-1" id="fw-row-inbridgeport">
  7147. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Bridge Port</label>
  7148. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7149. <button type="button" onclick="toggleFwField('inbridgeport', true)" id="fw-plus-inbridgeport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7150. <div id="fw-field-inbridgeport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7151. <input type="text" id="fw-val-inbridgeport" placeholder="e.g. eth0" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7152. <button type="button" onclick="toggleFwField('inbridgeport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7153. </div>
  7154. </div>
  7155. </div>
  7156. <hr class="border-[#213042] my-2">
  7157. <!-- In. Bridge Port List -->
  7158. <!-- Out. Bridge Port List -->
  7159. <hr class="border-[#213042] my-2">
  7160. <!-- IPsec Policy -->
  7161. <div class="flex items-center text-xs py-1" id="fw-row-ipsec">
  7162. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">IPsec Policy</label>
  7163. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7164. <button type="button" onclick="toggleFwField('ipsec', true)" id="fw-plus-ipsec" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7165. <div id="fw-field-ipsec" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7166. <select id="fw-val-ipsec" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7167. <option value="in,ipsec">in:ipsec</option>
  7168. <option value="out,ipsec">out:ipsec</option>
  7169. <option value="in,none">in:none</option>
  7170. <option value="out,none">out:none</option>
  7171. </select>
  7172. <button type="button" onclick="toggleFwField('ipsec', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7173. </div>
  7174. </div>
  7175. </div>
  7176. <!-- TLS Host -->
  7177. <hr class="border-[#213042] my-2">
  7178. <!-- Ingress Priority -->
  7179. <!-- Priority -->
  7180. <!-- DSCP -->
  7181. <div class="flex items-center text-xs py-1" id="fw-row-dscp">
  7182. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">DSCP</label>
  7183. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7184. <button type="button" onclick="toggleFwField('dscp', true)" id="fw-plus-dscp" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7185. <div id="fw-field-dscp" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7186. <input type="text" id="fw-val-dscp" placeholder="0-63 (e.g. 46 for EF)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7187. <button type="button" onclick="toggleFwField('dscp', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7188. </div>
  7189. </div>
  7190. </div>
  7191. <!-- TCP MSS -->
  7192. <div class="flex items-center text-xs py-1" id="fw-row-tcpmss">
  7193. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TCP MSS</label>
  7194. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7195. <button type="button" onclick="toggleFwField('tcpmss', true)" id="fw-plus-tcpmss" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7196. <div id="fw-field-tcpmss" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7197. <input type="text" id="fw-val-tcpmss" placeholder="e.g. 1440" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7198. <button type="button" onclick="toggleFwField('tcpmss', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7199. </div>
  7200. </div>
  7201. </div>
  7202. <!-- TOS -->
  7203. <div class="flex items-center text-xs py-1" id="fw-row-tos">
  7204. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TOS</label>
  7205. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7206. <select id="fw-val-tos" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  7207. <option value="">(any)</option>
  7208. <option value="0">0 (Normal Service)</option>
  7209. <option value="min-delay">min-delay (16)</option>
  7210. <option value="max-throughput">max-throughput (8)</option>
  7211. <option value="max-reliability">max-reliability (4)</option>
  7212. <option value="min-cost">min-cost (2)</option>
  7213. </select>
  7214. </div>
  7215. </div>
  7216. <!-- Packet Size -->
  7217. <div class="flex items-center text-xs py-1" id="fw-row-pktsize">
  7218. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Size</label>
  7219. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7220. <button type="button" onclick="toggleFwField('pktsize', true)" id="fw-plus-pktsize" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7221. <div id="fw-field-pktsize" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7222. <input type="text" id="fw-val-pktsize" placeholder="e.g. 64-1500" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7223. <button type="button" onclick="toggleFwField('pktsize', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7224. </div>
  7225. </div>
  7226. </div>
  7227. <!-- Random -->
  7228. <div class="flex items-center text-xs py-1" id="fw-row-random">
  7229. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Random</label>
  7230. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7231. <button type="button" onclick="toggleFwField('random', true)" id="fw-plus-random" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7232. <div id="fw-field-random" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7233. <input type="text" id="fw-val-random" placeholder="1-100 %" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7234. <button type="button" onclick="toggleFwField('random', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7235. </div>
  7236. </div>
  7237. </div>
  7238. <!-- TCP Flags -->
  7239. <div class="flex items-center text-xs py-1" id="fw-row-tcpflags">
  7240. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TCP Flags</label>
  7241. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7242. <button type="button" onclick="toggleFwField('tcpflags', true)" id="fw-plus-tcpflags" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7243. <div id="fw-field-tcpflags" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7244. <input type="text" id="fw-val-tcpflags" placeholder="syn,ack,fin,rst" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7245. <button type="button" onclick="toggleFwField('tcpflags', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7246. </div>
  7247. </div>
  7248. </div>
  7249. <!-- ICMP Options -->
  7250. <div class="flex items-center text-xs py-1" id="fw-row-icmpoptions">
  7251. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">ICMP Options</label>
  7252. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7253. <button type="button" onclick="toggleFwField('icmpoptions', true)" id="fw-plus-icmpoptions" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7254. <div id="fw-field-icmpoptions" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7255. <input type="text" id="fw-val-icmpoptions" placeholder="e.g. 8:0 (echo-request)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7256. <button type="button" onclick="toggleFwField('icmpoptions', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7257. </div>
  7258. </div>
  7259. </div>
  7260. <!-- IPv4 Options -->
  7261. <!-- TTL -->
  7262. <div class="flex items-center text-xs py-1" id="fw-row-ttl">
  7263. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TTL</label>
  7264. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7265. <button type="button" onclick="toggleFwField('ttl', true)" id="fw-plus-ttl" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7266. <div id="fw-field-ttl" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7267. <input type="text" id="fw-val-ttl" placeholder="equal:64 or less-than:10" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7268. <button type="button" onclick="toggleFwField('ttl', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7269. </div>
  7270. </div>
  7271. </div>
  7272. </div>
  7273. <!-- TAB PANE: EXTRA -->
  7274. <div id="fw-pane-extra" class="fw-rule-pane hidden space-y-1">
  7275. <!-- Time -->
  7276. <div class="flex items-center text-xs py-1" id="fw-row-time">
  7277. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Time</label>
  7278. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7279. <button type="button" onclick="toggleFwField('time', true)" id="fw-plus-time" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7280. <div id="fw-field-time" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7281. <input type="text" id="fw-val-time" placeholder="00:00:00-23:59:59 (UTC)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7282. <button type="button" onclick="toggleFwField('time', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7283. </div>
  7284. </div>
  7285. </div>
  7286. <!-- Days -->
  7287. <div class="flex items-center text-xs py-1" id="fw-row-days">
  7288. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Days</label>
  7289. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7290. <button type="button" onclick="toggleFwField('days', true)" id="fw-plus-days" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7291. <div id="fw-field-days" class="hidden flex-1 flex flex-wrap items-center gap-2 min-w-0 bg-[#15202e] border border-[#26374d] rounded px-2 py-1">
  7292. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="fw-day-sun" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">sun</span></label>
  7293. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="fw-day-mon" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">mon</span></label>
  7294. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="fw-day-tue" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">tue</span></label>
  7295. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="fw-day-wed" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">wed</span></label>
  7296. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="fw-day-thu" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">thu</span></label>
  7297. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="fw-day-fri" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">fri</span></label>
  7298. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="fw-day-sat" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">sat</span></label>
  7299. <button type="button" onclick="toggleFwField('days', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0 ml-auto">-</button>
  7300. </div>
  7301. </div>
  7302. </div>
  7303. <!-- Limit -->
  7304. <div class="flex items-center text-xs py-1" id="fw-row-limit">
  7305. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Limit</label>
  7306. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7307. <button type="button" onclick="toggleFwField('limit', true)" id="fw-plus-limit" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7308. <div id="fw-field-limit" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7309. <input type="text" id="fw-val-limit" placeholder="100/second,5" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7310. <button type="button" onclick="toggleFwField('limit', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7311. </div>
  7312. </div>
  7313. </div>
  7314. <!-- Dst. Limit -->
  7315. <!-- PSD -->
  7316. </div>
  7317. <!-- TAB PANE: ACTION (Screenshot 3) -->
  7318. <div id="fw-pane-action" class="fw-rule-pane hidden space-y-2">
  7319. <!-- Action -->
  7320. <div class="flex items-center text-xs py-1">
  7321. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Target</label>
  7322. <div class="flex-1">
  7323. <select id="filter-modal-action" onchange="toggleFilterActionFields()" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-sky-400 font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  7324. <option value="accept" selected>ACCEPT</option>
  7325. <option value="add-dst-to-address-list">SET — add destination to address list</option>
  7326. <option value="add-src-to-address-list">SET — add source to address list</option>
  7327. <option value="drop">DROP</option>
  7328. <option value="jump">JUMP to user chain</option>
  7329. <option value="log">LOG</option>
  7330. <option value="passthrough">COUNT / continue</option>
  7331. <option value="reject">REJECT</option>
  7332. <option value="return">RETURN</option>
  7333. </select>
  7334. </div>
  7335. </div>
  7336. <!-- Reject With (Shown when action === reject) -->
  7337. <div id="filter-action-reject-box" class="hidden flex items-center text-xs py-1">
  7338. <label class="w-36 sm:w-44 text-right pr-3.5 text-rose-300 shrink-0 select-none">Reject With</label>
  7339. <div class="flex-1">
  7340. <select id="filter-modal-rejectwith" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-rose-500 w-full cursor-pointer">
  7341. <option value="icmp-network-unreachable">icmp-network-unreachable</option>
  7342. <option value="icmp-host-unreachable">icmp-host-unreachable</option>
  7343. <option value="icmp-port-unreachable">icmp-port-unreachable</option>
  7344. <option value="icmp-admin-prohibited">icmp-admin-prohibited</option>
  7345. <option value="tcp-reset">tcp-reset</option>
  7346. </select>
  7347. </div>
  7348. </div>
  7349. <!-- Jump Target (Shown when action === jump) -->
  7350. <div id="filter-action-jump-box" class="hidden flex items-center text-xs py-1">
  7351. <label class="w-36 sm:w-44 text-right pr-3.5 text-sky-300 shrink-0 select-none">Jump Target</label>
  7352. <div class="flex-1">
  7353. <input type="text" id="filter-modal-jumptarget" placeholder="chain name" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7354. </div>
  7355. </div>
  7356. <!-- Address List & Timeout (Shown when action starts with add-) -->
  7357. <div id="filter-action-addrlist-box" class="hidden space-y-1">
  7358. <div class="flex items-center text-xs py-1">
  7359. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Address List</label>
  7360. <div class="flex-1">
  7361. <input type="text" id="filter-modal-action-addrlist" list="fw-addrlist-datalist" placeholder="list name" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7362. </div>
  7363. </div>
  7364. <div class="flex items-center text-xs py-1">
  7365. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Timeout</label>
  7366. <div class="flex-1">
  7367. <input type="text" id="filter-modal-action-timeout" placeholder="none or 1d 00:00:00" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7368. </div>
  7369. </div>
  7370. </div>
  7371. <hr class="border-[#213042] my-2">
  7372. <!-- Log -->
  7373. <div class="flex items-center text-xs py-1">
  7374. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Log</label>
  7375. <div class="flex items-center">
  7376. <input type="checkbox" id="filter-modal-log" onchange="toggleFwLogPrefix()" class="rounded bg-[#15202e] border-[#26374d] text-sky-500 focus:ring-0 w-4 h-4 cursor-pointer">
  7377. </div>
  7378. </div>
  7379. <!-- Log Prefix -->
  7380. <div class="flex items-center text-xs py-1" id="fw-row-logprefix">
  7381. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Log Prefix</label>
  7382. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7383. <button type="button" onclick="toggleFwField('logprefix', true)" id="fw-plus-logprefix" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7384. <div id="fw-field-logprefix" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7385. <input type="text" id="fw-val-logprefix" placeholder="e.g. FW_DROP: " class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7386. <button type="button" onclick="toggleFwField('logprefix', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7387. </div>
  7388. </div>
  7389. </div>
  7390. </div>
  7391. <!-- TAB PANE: STATISTICS (Screenshot 4) -->
  7392. <div id="fw-pane-statistics" class="fw-rule-pane hidden space-y-2 py-1">
  7393. <!-- Bytes -->
  7394. <div class="flex items-center text-xs">
  7395. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Bytes</label>
  7396. <div class="flex-1">
  7397. <input type="text" id="fw-stat-bytes" readonly value="0 B" class="bg-[#121c27] border border-sky-600/80 rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  7398. </div>
  7399. </div>
  7400. <!-- Packets -->
  7401. <div class="flex items-center text-xs">
  7402. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packets</label>
  7403. <div class="flex-1">
  7404. <input type="text" id="fw-stat-packets" readonly value="0" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  7405. </div>
  7406. </div>
  7407. <div class="h-1"></div>
  7408. <!-- Rate -->
  7409. <div class="flex items-center text-xs">
  7410. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Rate</label>
  7411. <div class="flex-1">
  7412. <input type="text" id="fw-stat-rate" readonly value="0 bps" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  7413. </div>
  7414. </div>
  7415. <!-- Packet Rate -->
  7416. <div class="flex items-center text-xs">
  7417. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Rate</label>
  7418. <div class="flex-1">
  7419. <input type="text" id="fw-stat-pktrate" readonly value="0 p/s" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  7420. </div>
  7421. </div>
  7422. <div class="h-2"></div>
  7423. <!-- Rate Graph -->
  7424. <div class="space-y-1">
  7425. <div class="text-[11px] text-slate-300 font-medium pl-1">Rate Graph</div>
  7426. <div class="bg-[#0f1721] border border-[#202e40] rounded p-1.5 relative overflow-hidden">
  7427. <svg id="fw-stat-rate-svg" class="w-full h-24" viewBox="0 0 500 100" preserveAspectRatio="none">
  7428. <!-- Generated grid and sparkline -->
  7429. </svg>
  7430. </div>
  7431. <div class="flex items-center justify-center gap-1.5 text-[11px] text-slate-300 py-0.5">
  7432. <span class="w-2 h-2 rounded-full bg-sky-400"></span>
  7433. <span id="fw-stat-legend-rate" class="font-mono font-semibold text-white">0 bps</span>
  7434. <span class="text-slate-400">Rate</span>
  7435. </div>
  7436. </div>
  7437. <!-- Packet Rate Graph -->
  7438. <div class="space-y-1">
  7439. <div class="text-[11px] text-slate-300 font-medium pl-1">Packet Rate Graph</div>
  7440. <div class="bg-[#0f1721] border border-[#202e40] rounded p-1.5 relative overflow-hidden">
  7441. <svg id="fw-stat-pktrate-svg" class="w-full h-24" viewBox="0 0 500 100" preserveAspectRatio="none">
  7442. <!-- Generated grid and sparkline -->
  7443. </svg>
  7444. </div>
  7445. <div class="flex items-center justify-center gap-1.5 text-[11px] text-slate-300 py-0.5">
  7446. <span class="w-2 h-2 rounded-full bg-sky-400"></span>
  7447. <span id="fw-stat-legend-pktrate" class="font-mono font-semibold text-white">0 p/s</span>
  7448. <span class="text-slate-400">Packet Rate</span>
  7449. </div>
  7450. </div>
  7451. </div>
  7452. </form>
  7453. <!-- Right Vertical Sidebar -->
  7454. <div class="w-40 min-w-[150px] border-l border-[#213042] bg-[#121a24] p-3 text-xs flex flex-col space-y-4 shrink-0">
  7455. <div>
  7456. <button type="button" onclick="copyFilterRule()" class="w-full flex items-center gap-2 text-slate-300 hover:text-sky-300 text-xs py-1 transition-colors">
  7457. <svg class="w-3.5 h-3.5 text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  7458. <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
  7459. <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
  7460. </svg>
  7461. <span>Copy</span>
  7462. </button>
  7463. </div>
  7464. <div class="space-y-2">
  7465. <div class="text-slate-300 font-semibold flex items-center gap-1.5 text-xs">
  7466. <span class="text-amber-400">⚡</span>
  7467. <span>Actions</span>
  7468. </div>
  7469. <div class="flex flex-col space-y-1.5 pl-1">
  7470. <button type="button" onclick="resetCurrentRuleCounters()" class="text-left text-slate-400 hover:text-sky-300 text-xs py-0.5 transition-colors">Reset Counters</button>
  7471. <button type="button" onclick="resetAllFwCounters()" class="text-left text-slate-400 hover:text-sky-300 text-xs py-0.5 transition-colors">Reset All Counters</button>
  7472. </div>
  7473. </div>
  7474. </div>
  7475. </div>
  7476. <!-- Window Bottom Button Bar -->
  7477. <div class="bg-[#141e2b] border-t border-[#1f2d3e] px-4 py-2 flex items-center justify-end gap-2 shrink-0">
  7478. <button type="button" onclick="closeFilterModal()" class="px-4 py-1.5 rounded bg-[#1c2838] hover:bg-[#25364b] border border-[#2a3c54] text-xs font-medium text-slate-300 transition-colors">Cancel</button>
  7479. <button type="button" onclick="applyFilterRule(false)" class="px-5 py-1.5 rounded bg-[#0078d4] hover:bg-[#006cbd] text-xs font-semibold text-white shadow-sm transition-colors">Apply</button>
  7480. <button type="button" onclick="applyFilterRule(true)" class="px-5 py-1.5 rounded bg-[#0078d4] hover:bg-[#006cbd] text-xs font-semibold text-white shadow-sm transition-colors">OK</button>
  7481. </div>
  7482. </div>
  7483. </div>
  7484. <!-- ================= MODAL: FIREWALL NAT RULE ================= -->
  7485. <div id="nat-modal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] z-50 hidden flex items-center justify-center p-2 sm:p-4">
  7486. <div id="nat-modal-window" class="bg-[#131b26] border border-[#243347] rounded-md shadow-2xl w-full max-w-[720px] max-h-[92vh] flex flex-col overflow-hidden text-xs select-none">
  7487. <!-- Window Title Bar -->
  7488. <div class="bg-[#162335] border-b border-[#223145] px-3.5 py-1.5 flex items-center justify-between shrink-0">
  7489. <div class="flex items-center gap-2">
  7490. <span class="text-slate-200 font-medium text-xs">NAT rule</span>
  7491. <span id="nat-modal-badge" class="bg-[#0078d4] text-white text-[11px] px-2 py-0.5 rounded font-mono font-medium">New...</span>
  7492. </div>
  7493. <div class="flex items-center gap-2">
  7494. <button type="button" onclick="toggleMaximizeNATModal()" title="Maximize / Restore" class="text-slate-400 hover:text-white p-0.5 text-xs transition-colors">
  7495. <svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
  7496. </button>
  7497. <button type="button" onclick="closeNATModal()" title="Close" class="text-slate-400 hover:text-rose-400 p-0.5 text-xs font-bold leading-none transition-colors">✕</button>
  7498. </div>
  7499. </div>
  7500. <!-- Window Tabs Bar -->
  7501. <div class="bg-[#111923] border-b border-[#1f2d3e] px-2 flex items-center gap-1 shrink-0">
  7502. <button type="button" onclick="switchNATRuleTab('general')" id="nat-tab-btn-general" class="nat-rule-tab-btn bg-[#182638] text-white border-t-2 border-sky-400 px-3.5 py-1.5 text-xs font-medium transition-colors">General</button>
  7503. <button type="button" onclick="switchNATRuleTab('advanced')" id="nat-tab-btn-advanced" class="nat-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Advanced</button>
  7504. <button type="button" onclick="switchNATRuleTab('extra')" id="nat-tab-btn-extra" class="nat-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Extra</button>
  7505. <button type="button" onclick="switchNATRuleTab('statistics')" id="nat-tab-btn-statistics" class="nat-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Statistics</button>
  7506. </div>
  7507. <!-- Window Body (Split Layout: Left Tab Content + Right Action Sidebar) -->
  7508. <div class="flex-1 flex min-h-0 overflow-hidden">
  7509. <!-- Form Content Container (Scrollable) -->
  7510. <form id="nat-rule-form" novalidate onsubmit="event.preventDefault(); applyNATRule(true);" class="flex-1 overflow-y-auto px-4 py-3 min-w-0 space-y-1">
  7511. <input type="hidden" id="nat-modal-id">
  7512. <label class="block text-xs text-slate-300">IP family<select id="nat-modal-family" class="bg-slate-800 rounded p-2 ml-2"><option value="4">IPv4</option><option value="6">IPv6</option></select></label>
  7513. <!-- TAB PANE: GENERAL (Screenshot 1) -->
  7514. <div id="nat-pane-general" class="nat-rule-pane space-y-1">
  7515. <!-- Enabled -->
  7516. <div class="flex items-center text-xs py-1">
  7517. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Enabled</label>
  7518. <div class="flex items-center">
  7519. <input type="checkbox" id="nat-modal-enabled" checked class="rounded bg-[#15202e] border-[#26374d] text-sky-500 focus:ring-0 w-4 h-4 cursor-pointer">
  7520. </div>
  7521. </div>
  7522. <!-- Comment -->
  7523. <div class="flex items-center text-xs py-1">
  7524. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Comment</label>
  7525. <div class="flex-1">
  7526. <input type="text" id="nat-modal-comment" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full">
  7527. </div>
  7528. </div>
  7529. <!-- Chain -->
  7530. <div class="flex items-center text-xs py-1">
  7531. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Chain</label>
  7532. <div class="flex-1">
  7533. <select id="nat-modal-chain-select" onchange="syncNATChainFromSelect()" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-sky-400 font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  7534. <option value="postrouting" selected>POSTROUTING (outgoing after routing — SNAT / MASQUERADE)</option>
  7535. <option value="prerouting">PREROUTING (incoming before routing — DNAT / REDIRECT)</option>
  7536. <option value="output">OUTPUT (locally generated — DNAT / REDIRECT)</option>
  7537. <option value="input">INPUT (destined to this machine — SNAT)</option>
  7538. <option value="__custom__">+ Custom Chain...</option>
  7539. </select>
  7540. <div id="nat-modal-custom-wrap" class="hidden mt-1.5 flex items-center gap-1.5">
  7541. <span class="text-[10px] text-amber-400 font-mono uppercase tracking-wider shrink-0">Custom Chain:</span>
  7542. <input type="text" id="nat-modal-custom-input" oninput="syncNATCustomChainInput()" placeholder="Enter custom chain name (1-16 chars)" maxlength="16" class="bg-[#15202e] border border-amber-500/50 rounded px-2.5 py-1 text-xs text-amber-300 font-mono focus:outline-none focus:border-amber-400 w-full">
  7543. </div>
  7544. <input type="hidden" id="nat-modal-chain" value="postrouting" oninput="handleNATChainChange()" onchange="handleNATChainChange()">
  7545. <datalist id="nat-chain-options">
  7546. <option value="prerouting">PREROUTING — incoming before routing</option>
  7547. <option value="input">INPUT — delivered to this machine</option>
  7548. <option value="output">OUTPUT — generated by this machine</option>
  7549. <option value="postrouting">POSTROUTING — outgoing after routing</option>
  7550. </datalist>
  7551. <p id="nat-chain-hint" class="mt-1 text-[11px] text-slate-400">POSTROUTING: SNAT/MASQUERADE on traffic leaving this router (Out-Interface only).</p>
  7552. <div id="nat-action-incompatible-warn" class="hidden mt-1.5 p-2 bg-amber-950/40 border border-amber-600/40 rounded text-amber-300 text-[11px] flex items-center gap-2">
  7553. <svg class="w-4 h-4 text-amber-400 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>
  7554. <span id="nat-action-incompatible-msg" class="flex-1 font-mono"></span>
  7555. </div>
  7556. </div>
  7557. </div>
  7558. <!-- Action -->
  7559. <div class="flex items-center text-xs py-1">
  7560. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Target</label>
  7561. <div class="flex-1">
  7562. <select id="nat-modal-action" onchange="handleNATActionChange()" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-sky-400 font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  7563. <option value="accept">ACCEPT</option>
  7564. <option value="add-dst-to-address-list">SET — add destination to address list</option>
  7565. <option value="add-src-to-address-list">SET — add source to address list</option>
  7566. <option value="dst-nat">DNAT</option>
  7567. <option value="jump">JUMP to user chain</option>
  7568. <option value="log">LOG</option>
  7569. <option value="masquerade" selected>MASQUERADE</option>
  7570. <option value="netmap">NETMAP</option>
  7571. <option value="passthrough">COUNT / continue</option>
  7572. <option value="redirect">REDIRECT</option>
  7573. <option value="return">RETURN</option>
  7574. <option value="src-nat">SNAT</option>
  7575. </select>
  7576. </div>
  7577. </div>
  7578. <!-- To Addresses (Shown for src-nat, dst-nat, netmap, same, socksify, endpoint-independent-nat) -->
  7579. <div id="nat-row-toaddr" class="hidden flex items-center text-xs py-1">
  7580. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none" for="nat-modal-toaddr">Translate to address</label>
  7581. <div class="flex-1">
  7582. <input type="text" id="nat-modal-toaddr" placeholder="192.168.10.50 or WAN IP" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7583. </div>
  7584. </div>
  7585. <!-- To Ports (Shown for src-nat, dst-nat, redirect, masquerade, netmap, socksify, endpoint-independent-nat) -->
  7586. <div id="nat-row-toport" class="hidden flex items-center text-xs py-1">
  7587. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none" for="nat-modal-toport">Translate to port (optional)</label>
  7588. <div class="flex-1">
  7589. <input type="text" id="nat-modal-toport" placeholder="80 or 8080 or 1024-65535" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7590. </div>
  7591. </div>
  7592. <!-- Jump Target (Shown when action === jump) -->
  7593. <div id="nat-action-jump-box" class="hidden flex items-center text-xs py-1">
  7594. <label class="w-36 sm:w-44 text-right pr-3.5 text-sky-300 shrink-0 select-none">Jump Target</label>
  7595. <div class="flex-1">
  7596. <input type="text" id="nat-modal-jumptarget" placeholder="chain name" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7597. </div>
  7598. </div>
  7599. <!-- Address List & Timeout (Shown when action starts with add-) -->
  7600. <div id="nat-action-addrlist-box" class="hidden space-y-1">
  7601. <div class="flex items-center text-xs py-1">
  7602. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Address List</label>
  7603. <div class="flex-1">
  7604. <input type="text" id="nat-modal-action-addrlist" list="fw-addrlist-datalist" placeholder="list name" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7605. </div>
  7606. </div>
  7607. <div class="flex items-center text-xs py-1">
  7608. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Timeout</label>
  7609. <div class="flex-1">
  7610. <input type="text" id="nat-modal-action-timeout" placeholder="none or 1d 00:00:00" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7611. </div>
  7612. </div>
  7613. </div>
  7614. <hr class="border-[#213042] my-2">
  7615. <!-- Log -->
  7616. <div class="flex items-center text-xs py-1">
  7617. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Log</label>
  7618. <div class="flex items-center">
  7619. <input type="checkbox" id="nat-modal-log" onchange="toggleNATLogPrefix()" class="rounded bg-[#15202e] border-[#26374d] text-sky-500 focus:ring-0 w-4 h-4 cursor-pointer">
  7620. </div>
  7621. </div>
  7622. <!-- Log Prefix -->
  7623. <div class="flex items-center text-xs py-1" id="nat-row-logprefix">
  7624. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Log Prefix</label>
  7625. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7626. <button type="button" onclick="toggleNATField('logprefix', true)" id="nat-plus-logprefix" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7627. <div id="nat-field-logprefix" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7628. <input type="text" id="nat-val-logprefix" placeholder="e.g. NAT_LOG: " class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7629. <button type="button" onclick="toggleNATField('logprefix', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7630. </div>
  7631. </div>
  7632. </div>
  7633. <!-- Src. Address -->
  7634. <div class="flex items-center text-xs py-1" id="nat-row-src">
  7635. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Address</label>
  7636. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7637. <button type="button" onclick="toggleNATField('src', true)" id="nat-plus-src" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7638. <div id="nat-field-src" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7639. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7640. <input type="checkbox" id="nat-inv-src" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7641. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7642. </label>
  7643. <input type="text" id="nat-val-src" placeholder="192.168.10.0/24" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7644. <button type="button" onclick="toggleNATField('src', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7645. </div>
  7646. </div>
  7647. </div>
  7648. <!-- Dst. Address -->
  7649. <div class="flex items-center text-xs py-1" id="nat-row-dst">
  7650. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Address</label>
  7651. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7652. <button type="button" onclick="toggleNATField('dst', true)" id="nat-plus-dst" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7653. <div id="nat-field-dst" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7654. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7655. <input type="checkbox" id="nat-inv-dst" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7656. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7657. </label>
  7658. <input type="text" id="nat-val-dst" placeholder="0.0.0.0/0" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7659. <button type="button" onclick="toggleNATField('dst', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7660. </div>
  7661. </div>
  7662. </div>
  7663. <!-- Src. Address List -->
  7664. <div class="flex items-center text-xs py-1" id="nat-row-srclist">
  7665. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Address List</label>
  7666. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7667. <button type="button" onclick="toggleNATField('srclist', true)" id="nat-plus-srclist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7668. <div id="nat-field-srclist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7669. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7670. <input type="checkbox" id="nat-inv-srclist" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7671. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7672. </label>
  7673. <select id="nat-val-srclist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  7674. <button type="button" onclick="toggleNATField('srclist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7675. </div>
  7676. </div>
  7677. </div>
  7678. <!-- Dst. Address List -->
  7679. <div class="flex items-center text-xs py-1" id="nat-row-dstlist">
  7680. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Address List</label>
  7681. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7682. <button type="button" onclick="toggleNATField('dstlist', true)" id="nat-plus-dstlist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7683. <div id="nat-field-dstlist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7684. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7685. <input type="checkbox" id="nat-inv-dstlist" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7686. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7687. </label>
  7688. <select id="nat-val-dstlist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  7689. <button type="button" onclick="toggleNATField('dstlist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7690. </div>
  7691. </div>
  7692. </div>
  7693. <!-- Src. Geo Location -->
  7694. <div class="flex items-center text-xs py-1" id="nat-row-srcgeo">
  7695. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Geo Location</label>
  7696. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7697. <button type="button" onclick="toggleNATField('srcgeo', true)" id="nat-plus-srcgeo" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7698. <div id="nat-field-srcgeo" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7699. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7700. <input type="checkbox" id="nat-inv-srcgeo" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7701. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7702. </label>
  7703. <select id="nat-val-srcgeo" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  7704. <button type="button" onclick="toggleNATField('srcgeo', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7705. </div>
  7706. </div>
  7707. </div>
  7708. <!-- Dst. Geo Location -->
  7709. <div class="flex items-center text-xs py-1" id="nat-row-dstgeo">
  7710. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Geo Location</label>
  7711. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7712. <button type="button" onclick="toggleNATField('dstgeo', true)" id="nat-plus-dstgeo" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7713. <div id="nat-field-dstgeo" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7714. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7715. <input type="checkbox" id="nat-inv-dstgeo" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7716. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7717. </label>
  7718. <select id="nat-val-dstgeo" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  7719. <button type="button" onclick="toggleNATField('dstgeo', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7720. </div>
  7721. </div>
  7722. </div>
  7723. <hr class="border-[#213042] my-2">
  7724. <!-- Protocol -->
  7725. <div class="flex items-center text-xs py-1" id="nat-row-proto">
  7726. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Protocol</label>
  7727. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7728. <button type="button" onclick="toggleNATField('proto', true)" id="nat-plus-proto" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7729. <div id="nat-field-proto" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7730. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7731. <input type="checkbox" id="nat-inv-proto" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7732. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7733. </label>
  7734. <select id="nat-val-proto" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7735. <option value="tcp">6 (tcp)</option>
  7736. <option value="udp">17 (udp)</option>
  7737. <option value="icmp">ICMP (family-specific)</option><option value="ipv6-icmp">ICMPv6</option>
  7738. <option value="gre">47 (gre)</option>
  7739. <option value="esp">50 (esp)</option>
  7740. <option value="all">all</option>
  7741. </select>
  7742. <button type="button" onclick="toggleNATField('proto', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7743. </div>
  7744. </div>
  7745. </div>
  7746. <!-- Src. Port -->
  7747. <div class="flex items-center text-xs py-1" id="nat-row-sport">
  7748. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Port</label>
  7749. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7750. <button type="button" onclick="toggleNATField('sport', true)" id="nat-plus-sport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7751. <div id="nat-field-sport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7752. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7753. <input type="checkbox" id="nat-inv-sport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7754. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7755. </label>
  7756. <input type="text" id="nat-val-sport" placeholder="e.g. 80,443 or 1000-2000" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7757. <button type="button" onclick="toggleNATField('sport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7758. </div>
  7759. </div>
  7760. </div>
  7761. <!-- Dst. Port -->
  7762. <div class="flex items-center text-xs py-1" id="nat-row-dport">
  7763. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Port</label>
  7764. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7765. <button type="button" onclick="toggleNATField('dport', true)" id="nat-plus-dport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7766. <div id="nat-field-dport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7767. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7768. <input type="checkbox" id="nat-inv-dport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7769. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7770. </label>
  7771. <input type="text" id="nat-val-dport" placeholder="e.g. 80,443,8080" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7772. <button type="button" onclick="toggleNATField('dport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7773. </div>
  7774. </div>
  7775. </div>
  7776. <!-- Any. Port -->
  7777. <div class="flex items-center text-xs py-1" id="nat-row-anyport">
  7778. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Any. Port</label>
  7779. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7780. <button type="button" onclick="toggleNATField('anyport', true)" id="nat-plus-anyport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7781. <div id="nat-field-anyport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7782. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7783. <input type="checkbox" id="nat-inv-anyport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7784. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7785. </label>
  7786. <input type="text" id="nat-val-anyport" placeholder="53" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7787. <button type="button" onclick="toggleNATField('anyport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7788. </div>
  7789. </div>
  7790. </div>
  7791. <!-- In. Interface -->
  7792. <div class="flex items-center text-xs py-1" id="nat-row-iniface">
  7793. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Interface</label>
  7794. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7795. <button type="button" onclick="toggleNATField('iniface', true)" id="nat-plus-iniface" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7796. <div id="nat-field-iniface" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7797. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7798. <input type="checkbox" id="nat-inv-iniface" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7799. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7800. </label>
  7801. <select id="nat-val-iniface" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  7802. <button type="button" onclick="toggleNATField('iniface', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7803. </div>
  7804. </div>
  7805. </div>
  7806. <!-- Out. Interface -->
  7807. <div class="flex items-center text-xs py-1" id="nat-row-outiface">
  7808. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Interface</label>
  7809. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7810. <button type="button" onclick="toggleNATField('outiface', true)" id="nat-plus-outiface" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7811. <div id="nat-field-outiface" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7812. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7813. <input type="checkbox" id="nat-inv-outiface" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7814. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7815. </label>
  7816. <select id="nat-val-outiface" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  7817. <button type="button" onclick="toggleNATField('outiface', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7818. </div>
  7819. </div>
  7820. </div>
  7821. <hr class="border-[#213042] my-2">
  7822. <!-- In. Interface List -->
  7823. <div class="flex items-center text-xs py-1" id="nat-row-inifacelist">
  7824. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Interface List</label>
  7825. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7826. <button type="button" onclick="toggleNATField('inifacelist', true)" id="nat-plus-inifacelist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7827. <div id="nat-field-inifacelist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7828. <select id="nat-val-inifacelist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7829. <option value="LAN">LAN</option>
  7830. <option value="WAN">WAN</option>
  7831. </select>
  7832. <button type="button" onclick="toggleNATField('inifacelist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7833. </div>
  7834. </div>
  7835. </div>
  7836. <!-- Out. Interface List -->
  7837. <div class="flex items-center text-xs py-1" id="nat-row-outifacelist">
  7838. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Interface List</label>
  7839. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7840. <button type="button" onclick="toggleNATField('outifacelist', true)" id="nat-plus-outifacelist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7841. <div id="nat-field-outifacelist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7842. <select id="nat-val-outifacelist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7843. <option value="LAN">LAN</option>
  7844. <option value="WAN">WAN</option>
  7845. </select>
  7846. <button type="button" onclick="toggleNATField('outifacelist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7847. </div>
  7848. </div>
  7849. </div>
  7850. <hr class="border-[#213042] my-2">
  7851. <!-- Packet Mark -->
  7852. <div class="flex items-center text-xs py-1" id="nat-row-pktmark">
  7853. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Mark</label>
  7854. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7855. <button type="button" onclick="toggleNATField('pktmark', true)" id="nat-plus-pktmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7856. <div id="nat-field-pktmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7857. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7858. <input type="checkbox" id="nat-inv-pktmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7859. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7860. </label>
  7861. <input type="text" id="nat-val-pktmark" placeholder="e.g. qos_nat" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7862. <button type="button" onclick="toggleNATField('pktmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7863. </div>
  7864. </div>
  7865. </div>
  7866. <!-- Connection Mark -->
  7867. <div class="flex items-center text-xs py-1" id="nat-row-connmark">
  7868. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection Mark</label>
  7869. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7870. <button type="button" onclick="toggleNATField('connmark', true)" id="nat-plus-connmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7871. <div id="nat-field-connmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7872. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7873. <input type="checkbox" id="nat-inv-connmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7874. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7875. </label>
  7876. <input type="text" id="nat-val-connmark" placeholder="e.g. wan1_conn" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7877. <button type="button" onclick="toggleNATField('connmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7878. </div>
  7879. </div>
  7880. </div>
  7881. <!-- Routing Mark -->
  7882. <div class="flex items-center text-xs py-1" id="nat-row-routingmark">
  7883. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Routing Mark</label>
  7884. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7885. <button type="button" onclick="toggleNATField('routingmark', true)" id="nat-plus-routingmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7886. <div id="nat-field-routingmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7887. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7888. <input type="checkbox" id="nat-inv-routingmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7889. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7890. </label>
  7891. <input type="text" id="nat-val-routingmark" placeholder="e.g. to_wan2" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7892. <button type="button" onclick="toggleNATField('routingmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7893. </div>
  7894. </div>
  7895. </div>
  7896. <hr class="border-[#213042] my-2">
  7897. <!-- Connection Type -->
  7898. <div class="flex items-center text-xs py-1" id="nat-row-conntype">
  7899. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection Type</label>
  7900. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7901. <button type="button" onclick="toggleNATField('conntype', true)" id="nat-plus-conntype" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7902. <div id="nat-field-conntype" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7903. <select id="nat-val-conntype" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7904. <option value="ftp">ftp</option>
  7905. <option value="h323">h323</option>
  7906. <option value="irc">irc</option>
  7907. <option value="pptp">pptp</option>
  7908. <option value="sip">sip</option>
  7909. <option value="tftp">tftp</option>
  7910. </select>
  7911. <button type="button" onclick="toggleNATField('conntype', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  7912. </div>
  7913. </div>
  7914. </div>
  7915. </div>
  7916. <!-- TAB PANE: ADVANCED (Screenshot 2) -->
  7917. <div id="nat-pane-advanced" class="nat-rule-pane hidden space-y-1">
  7918. <!-- Layer7 Protocol -->
  7919. <!-- Content -->
  7920. <div class="flex items-center text-xs py-1" id="nat-row-content">
  7921. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Content</label>
  7922. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7923. <button type="button" onclick="toggleNATField('content', true)" id="nat-plus-content" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7924. <div id="nat-field-content" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7925. <input type="text" id="nat-val-content" placeholder="e.g. facebook.com" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7926. <button type="button" onclick="toggleNATField('content', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7927. </div>
  7928. </div>
  7929. </div>
  7930. <!-- Connection Bytes -->
  7931. <!-- Connection Rate -->
  7932. <!-- Per Connection Classifier -->
  7933. <!-- Src. MAC Address -->
  7934. <div class="flex items-center text-xs py-1" id="nat-row-srcmac">
  7935. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. MAC Address</label>
  7936. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7937. <button type="button" onclick="toggleNATField('srcmac', true)" id="nat-plus-srcmac" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7938. <div id="nat-field-srcmac" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7939. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  7940. <input type="checkbox" id="nat-inv-srcmac" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  7941. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  7942. </label>
  7943. <input type="text" id="nat-val-srcmac" placeholder="e.g. 00:11:22:33:44:55" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7944. <button type="button" onclick="toggleNATField('srcmac', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7945. </div>
  7946. </div>
  7947. </div>
  7948. <hr class="border-[#213042] my-2">
  7949. <!-- Out. Bridge Port -->
  7950. <div class="flex items-center text-xs py-1" id="nat-row-outbridgeport">
  7951. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Bridge Port</label>
  7952. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7953. <button type="button" onclick="toggleNATField('outbridgeport', true)" id="nat-plus-outbridgeport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7954. <div id="nat-field-outbridgeport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7955. <input type="text" id="nat-val-outbridgeport" placeholder="e.g. eth1" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7956. <button type="button" onclick="toggleNATField('outbridgeport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7957. </div>
  7958. </div>
  7959. </div>
  7960. <!-- In. Bridge Port -->
  7961. <div class="flex items-center text-xs py-1" id="nat-row-inbridgeport">
  7962. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Bridge Port</label>
  7963. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7964. <button type="button" onclick="toggleNATField('inbridgeport', true)" id="nat-plus-inbridgeport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7965. <div id="nat-field-inbridgeport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7966. <input type="text" id="nat-val-inbridgeport" placeholder="e.g. eth0" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  7967. <button type="button" onclick="toggleNATField('inbridgeport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7968. </div>
  7969. </div>
  7970. </div>
  7971. <hr class="border-[#213042] my-2">
  7972. <!-- In. Bridge Port List -->
  7973. <!-- Out. Bridge Port List -->
  7974. <hr class="border-[#213042] my-2">
  7975. <!-- IPsec Policy -->
  7976. <div class="flex items-center text-xs py-1" id="nat-row-ipsec">
  7977. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">IPsec Policy</label>
  7978. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7979. <button type="button" onclick="toggleNATField('ipsec', true)" id="nat-plus-ipsec" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7980. <div id="nat-field-ipsec" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  7981. <select id="nat-val-ipsec" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  7982. <option value="in,ipsec">in:ipsec</option>
  7983. <option value="out,ipsec">out:ipsec</option>
  7984. <option value="in,none">in:none</option>
  7985. <option value="out,none">out:none</option>
  7986. </select>
  7987. <button type="button" onclick="toggleNATField('ipsec', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  7988. </div>
  7989. </div>
  7990. </div>
  7991. <hr class="border-[#213042] my-2">
  7992. <!-- Ingress Priority -->
  7993. <!-- Priority -->
  7994. <!-- DSCP -->
  7995. <div class="flex items-center text-xs py-1" id="nat-row-dscp">
  7996. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">DSCP</label>
  7997. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  7998. <button type="button" onclick="toggleNATField('dscp', true)" id="nat-plus-dscp" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  7999. <div id="nat-field-dscp" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8000. <input type="text" id="nat-val-dscp" placeholder="0-63" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8001. <button type="button" onclick="toggleNATField('dscp', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8002. </div>
  8003. </div>
  8004. </div>
  8005. <!-- TCP MSS -->
  8006. <div class="flex items-center text-xs py-1" id="nat-row-tcpmss">
  8007. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TCP MSS</label>
  8008. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8009. <button type="button" onclick="toggleNATField('tcpmss', true)" id="nat-plus-tcpmss" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8010. <div id="nat-field-tcpmss" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8011. <input type="text" id="nat-val-tcpmss" placeholder="e.g. 1440" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8012. <button type="button" onclick="toggleNATField('tcpmss', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8013. </div>
  8014. </div>
  8015. </div>
  8016. <!-- TOS -->
  8017. <div class="flex items-center text-xs py-1" id="nat-row-tos">
  8018. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TOS</label>
  8019. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8020. <select id="nat-val-tos" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  8021. <option value="">(any)</option>
  8022. <option value="0">0 (Normal Service)</option>
  8023. <option value="min-delay">min-delay (16)</option>
  8024. <option value="max-throughput">max-throughput (8)</option>
  8025. <option value="max-reliability">max-reliability (4)</option>
  8026. <option value="min-cost">min-cost (2)</option>
  8027. </select>
  8028. </div>
  8029. </div>
  8030. <!-- Packet Size -->
  8031. <div class="flex items-center text-xs py-1" id="nat-row-pktsize">
  8032. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Size</label>
  8033. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8034. <button type="button" onclick="toggleNATField('pktsize', true)" id="nat-plus-pktsize" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8035. <div id="nat-field-pktsize" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8036. <input type="text" id="nat-val-pktsize" placeholder="e.g. 64-1500" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8037. <button type="button" onclick="toggleNATField('pktsize', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8038. </div>
  8039. </div>
  8040. </div>
  8041. <!-- Random -->
  8042. <div class="flex items-center text-xs py-1" id="nat-row-random">
  8043. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Random</label>
  8044. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8045. <button type="button" onclick="toggleNATField('random', true)" id="nat-plus-random" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8046. <div id="nat-field-random" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8047. <input type="text" id="nat-val-random" placeholder="1-100 %" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8048. <button type="button" onclick="toggleNATField('random', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8049. </div>
  8050. </div>
  8051. </div>
  8052. <!-- ICMP Options -->
  8053. <div class="flex items-center text-xs py-1" id="nat-row-icmpoptions">
  8054. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">ICMP Options</label>
  8055. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8056. <button type="button" onclick="toggleNATField('icmpoptions', true)" id="nat-plus-icmpoptions" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8057. <div id="nat-field-icmpoptions" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8058. <input type="text" id="nat-val-icmpoptions" placeholder="e.g. 8:0 (echo-request)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8059. <button type="button" onclick="toggleNATField('icmpoptions', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8060. </div>
  8061. </div>
  8062. </div>
  8063. <!-- IPv4 Options -->
  8064. <!-- TTL -->
  8065. <div class="flex items-center text-xs py-1" id="nat-row-ttl">
  8066. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TTL</label>
  8067. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8068. <button type="button" onclick="toggleNATField('ttl', true)" id="nat-plus-ttl" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8069. <div id="nat-field-ttl" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8070. <input type="text" id="nat-val-ttl" placeholder="equal:64 or less-than:10" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8071. <button type="button" onclick="toggleNATField('ttl', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8072. </div>
  8073. </div>
  8074. </div>
  8075. </div>
  8076. <!-- TAB PANE: EXTRA -->
  8077. <div id="nat-pane-extra" class="nat-rule-pane hidden space-y-1">
  8078. <!-- Time -->
  8079. <div class="flex items-center text-xs py-1" id="nat-row-time">
  8080. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Time</label>
  8081. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8082. <button type="button" onclick="toggleNATField('time', true)" id="nat-plus-time" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8083. <div id="nat-field-time" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8084. <input type="text" id="nat-val-time" placeholder="00:00:00-23:59:59 (UTC)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8085. <button type="button" onclick="toggleNATField('time', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8086. </div>
  8087. </div>
  8088. </div>
  8089. <!-- Days -->
  8090. <div class="flex items-center text-xs py-1" id="nat-row-days">
  8091. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Days</label>
  8092. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8093. <button type="button" onclick="toggleNATField('days', true)" id="nat-plus-days" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8094. <div id="nat-field-days" class="hidden flex-1 flex flex-wrap items-center gap-2 min-w-0 bg-[#15202e] border border-[#26374d] rounded px-2 py-1">
  8095. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="nat-day-sun" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">sun</span></label>
  8096. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="nat-day-mon" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">mon</span></label>
  8097. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="nat-day-tue" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">tue</span></label>
  8098. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="nat-day-wed" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">wed</span></label>
  8099. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="nat-day-thu" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">thu</span></label>
  8100. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="nat-day-fri" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">fri</span></label>
  8101. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="nat-day-sat" checked class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">sat</span></label>
  8102. <button type="button" onclick="toggleNATField('days', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0 ml-auto">-</button>
  8103. </div>
  8104. </div>
  8105. </div>
  8106. <!-- Limit -->
  8107. <div class="flex items-center text-xs py-1" id="nat-row-limit">
  8108. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Limit</label>
  8109. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8110. <button type="button" onclick="toggleNATField('limit', true)" id="nat-plus-limit" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8111. <div id="nat-field-limit" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8112. <input type="text" id="nat-val-limit" placeholder="100/second,5" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8113. <button type="button" onclick="toggleNATField('limit', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8114. </div>
  8115. </div>
  8116. </div>
  8117. <!-- Dst. Limit -->
  8118. <!-- PSD -->
  8119. </div>
  8120. <!-- TAB PANE: STATISTICS (Screenshot 4) -->
  8121. <div id="nat-pane-statistics" class="nat-rule-pane hidden space-y-2 py-1">
  8122. <!-- Bytes -->
  8123. <div class="flex items-center text-xs">
  8124. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Bytes</label>
  8125. <div class="flex-1">
  8126. <input type="text" id="nat-stat-bytes" readonly value="0 B" class="bg-[#121c27] border border-sky-600/80 rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  8127. </div>
  8128. </div>
  8129. <!-- Packets -->
  8130. <div class="flex items-center text-xs">
  8131. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packets</label>
  8132. <div class="flex-1">
  8133. <input type="text" id="nat-stat-packets" readonly value="0" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  8134. </div>
  8135. </div>
  8136. <div class="h-1"></div>
  8137. <!-- Rate -->
  8138. <div class="flex items-center text-xs">
  8139. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Rate</label>
  8140. <div class="flex-1">
  8141. <input type="text" id="nat-stat-rate" readonly value="0 bps" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  8142. </div>
  8143. </div>
  8144. <!-- Packet Rate -->
  8145. <div class="flex items-center text-xs">
  8146. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Rate</label>
  8147. <div class="flex-1">
  8148. <input type="text" id="nat-stat-pktrate" readonly value="0 p/s" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  8149. </div>
  8150. </div>
  8151. <div class="h-2"></div>
  8152. <!-- Rate Graph -->
  8153. <div class="space-y-1">
  8154. <div class="text-[11px] text-slate-300 font-medium pl-1">Rate Graph</div>
  8155. <div class="bg-[#0f1721] border border-[#202e40] rounded p-1.5 relative overflow-hidden">
  8156. <svg id="nat-stat-rate-svg" class="w-full h-24" viewBox="0 0 500 100" preserveAspectRatio="none">
  8157. <!-- Generated grid and sparkline -->
  8158. </svg>
  8159. </div>
  8160. <div class="flex items-center justify-center gap-1.5 text-[11px] text-slate-300 py-0.5">
  8161. <span class="w-2 h-2 rounded-full bg-sky-400"></span>
  8162. <span id="nat-stat-legend-rate" class="font-mono font-semibold text-white">0 bps</span>
  8163. <span class="text-slate-400">Rate</span>
  8164. </div>
  8165. </div>
  8166. <!-- Packet Rate Graph -->
  8167. <div class="space-y-1">
  8168. <div class="text-[11px] text-slate-300 font-medium pl-1">Packet Rate Graph</div>
  8169. <div class="bg-[#0f1721] border border-[#202e40] rounded p-1.5 relative overflow-hidden">
  8170. <svg id="nat-stat-pktrate-svg" class="w-full h-24" viewBox="0 0 500 100" preserveAspectRatio="none">
  8171. <!-- Generated grid and sparkline -->
  8172. </svg>
  8173. </div>
  8174. <div class="flex items-center justify-center gap-1.5 text-[11px] text-slate-300 py-0.5">
  8175. <span class="w-2 h-2 rounded-full bg-sky-400"></span>
  8176. <span id="nat-stat-legend-pktrate" class="font-mono font-semibold text-white">0 p/s</span>
  8177. <span class="text-slate-400">Packet Rate</span>
  8178. </div>
  8179. </div>
  8180. </div>
  8181. </form>
  8182. <!-- Right Vertical Sidebar -->
  8183. <div class="w-40 min-w-[150px] border-l border-[#213042] bg-[#121a24] p-3 text-xs flex flex-col space-y-4 shrink-0">
  8184. <div>
  8185. <button type="button" onclick="copyNATRule()" class="w-full flex items-center gap-2 text-slate-300 hover:text-sky-300 text-xs py-1 transition-colors">
  8186. <svg class="w-3.5 h-3.5 text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  8187. <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
  8188. <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
  8189. </svg>
  8190. <span>Copy</span>
  8191. </button>
  8192. </div>
  8193. <div class="space-y-2">
  8194. <div class="text-slate-300 font-semibold flex items-center gap-1.5 text-xs">
  8195. <span class="text-amber-400">⚡</span>
  8196. <span>Actions</span>
  8197. </div>
  8198. <div class="flex flex-col space-y-1.5 pl-1">
  8199. <button type="button" onclick="resetCurrentNATRuleCounters()" class="text-left text-slate-400 hover:text-sky-300 text-xs py-0.5 transition-colors">Reset Counters</button>
  8200. <button type="button" onclick="resetAllFwCounters()" class="text-left text-slate-400 hover:text-sky-300 text-xs py-0.5 transition-colors">Reset All Counters</button>
  8201. </div>
  8202. </div>
  8203. </div>
  8204. </div>
  8205. <!-- Window Bottom Button Bar -->
  8206. <div class="bg-[#141e2b] border-t border-[#1f2d3e] px-4 py-2 flex items-center justify-end gap-2 shrink-0">
  8207. <button type="button" onclick="closeNATModal()" class="px-4 py-1.5 rounded bg-[#1c2838] hover:bg-[#25364b] border border-[#2a3c54] text-xs font-medium text-slate-300 transition-colors">Cancel</button>
  8208. <button type="button" onclick="applyNATRule(false)" class="px-5 py-1.5 rounded bg-[#0078d4] hover:bg-[#006cbd] text-xs font-semibold text-white shadow-sm transition-colors">Apply</button>
  8209. <button type="button" onclick="applyNATRule(true)" class="px-5 py-1.5 rounded bg-[#0078d4] hover:bg-[#006cbd] text-xs font-semibold text-white shadow-sm transition-colors">OK</button>
  8210. </div>
  8211. </div>
  8212. </div>
  8213. <!-- ================= MODAL: FIREWALL MANGLE RULE ================= -->
  8214. <div id="mangle-modal" class="fixed inset-0 bg-black/60 backdrop-blur-[2px] z-50 hidden flex items-center justify-center p-2 sm:p-4">
  8215. <div id="mangle-modal-window" class="bg-[#131b26] border border-[#243347] rounded-md shadow-2xl w-full max-w-[720px] max-h-[92vh] flex flex-col overflow-hidden text-xs select-none">
  8216. <!-- Window Title Bar -->
  8217. <div class="bg-[#162335] border-b border-[#223145] px-3.5 py-1.5 flex items-center justify-between shrink-0">
  8218. <div class="flex items-center gap-2">
  8219. <span class="text-slate-200 font-medium text-xs">Mangle rule</span>
  8220. <span id="mangle-modal-badge" class="bg-[#0078d4] text-white text-[11px] px-2 py-0.5 rounded font-mono font-medium">New...</span>
  8221. </div>
  8222. <div class="flex items-center gap-2">
  8223. <button type="button" onclick="toggleMaximizeMangleModal()" title="Maximize / Restore" class="text-slate-400 hover:text-white p-0.5 text-xs transition-colors">
  8224. <svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
  8225. </button>
  8226. <button type="button" onclick="closeMangleModal()" title="Close" class="text-slate-400 hover:text-rose-400 p-0.5 text-xs font-bold leading-none transition-colors">✕</button>
  8227. </div>
  8228. </div>
  8229. <!-- Window Tabs Bar -->
  8230. <div class="bg-[#111923] border-b border-[#1f2d3e] px-2 flex items-center gap-1 shrink-0">
  8231. <button type="button" onclick="switchMangleRuleTab('general')" id="mangle-tab-btn-general" class="mangle-rule-tab-btn bg-[#182638] text-white border-t-2 border-sky-400 px-3.5 py-1.5 text-xs font-medium transition-colors">General</button>
  8232. <button type="button" onclick="switchMangleRuleTab('advanced')" id="mangle-tab-btn-advanced" class="mangle-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Advanced</button>
  8233. <button type="button" onclick="switchMangleRuleTab('extra')" id="mangle-tab-btn-extra" class="mangle-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Extra</button>
  8234. <button type="button" onclick="switchMangleRuleTab('action')" id="mangle-tab-btn-action" class="mangle-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Target</button>
  8235. <button type="button" onclick="switchMangleRuleTab('statistics')" id="mangle-tab-btn-statistics" class="mangle-rule-tab-btn text-slate-400 hover:text-white hover:bg-[#15202d] border-t-2 border-transparent px-3.5 py-1.5 text-xs font-medium transition-colors">Statistics</button>
  8236. </div>
  8237. <!-- Window Body (Split Layout: Left Tab Content + Right Action Sidebar) -->
  8238. <div class="flex-1 flex min-h-0 overflow-hidden">
  8239. <!-- Form Content Container (Scrollable) -->
  8240. <form id="mangle-rule-form" onsubmit="event.preventDefault(); applyMangleRule(true);" class="flex-1 overflow-y-auto px-4 py-3 min-w-0 space-y-1">
  8241. <input type="hidden" id="mangle-modal-id">
  8242. <label class="block text-xs text-slate-300">IP family<select id="mangle-modal-family" class="bg-slate-800 rounded p-2 ml-2"><option value="4">IPv4</option><option value="6">IPv6</option></select></label>
  8243. <!-- TAB PANE: GENERAL (Screenshot 1) -->
  8244. <div id="mangle-pane-general" class="mangle-rule-pane space-y-1">
  8245. <!-- Enabled -->
  8246. <div class="flex items-center text-xs py-1">
  8247. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Enabled</label>
  8248. <div class="flex items-center">
  8249. <input type="checkbox" id="mangle-modal-enabled" checked class="rounded bg-[#15202e] border-[#26374d] text-sky-500 focus:ring-0 w-4 h-4 cursor-pointer">
  8250. </div>
  8251. </div>
  8252. <!-- Comment -->
  8253. <div class="flex items-center text-xs py-1">
  8254. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Comment</label>
  8255. <div class="flex-1">
  8256. <input type="text" id="mangle-modal-comment" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full">
  8257. </div>
  8258. </div>
  8259. <!-- Chain -->
  8260. <div class="flex items-center text-xs py-1">
  8261. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Chain</label>
  8262. <div class="flex-1">
  8263. <select id="mangle-modal-chain-select" onchange="syncMangleChainFromSelect()" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-sky-400 font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  8264. <option value="prerouting" selected>PREROUTING (incoming before routing)</option>
  8265. <option value="input">INPUT (destined to this machine)</option>
  8266. <option value="forward">FORWARD (routed through router)</option>
  8267. <option value="output">OUTPUT (generated by this machine)</option>
  8268. <option value="postrouting">POSTROUTING (outgoing after routing)</option>
  8269. <option value="__custom__">+ Custom Chain...</option>
  8270. </select>
  8271. <div id="mangle-modal-custom-wrap" class="hidden mt-1.5 flex items-center gap-1.5">
  8272. <span class="text-[10px] text-amber-400 font-mono uppercase tracking-wider shrink-0">Custom Chain:</span>
  8273. <input type="text" id="mangle-modal-custom-input" oninput="syncMangleCustomChainInput()" placeholder="Enter custom chain name (1-16 chars)" maxlength="16" class="bg-[#15202e] border border-amber-500/50 rounded px-2.5 py-1 text-xs text-amber-300 font-mono focus:outline-none focus:border-amber-400 w-full">
  8274. </div>
  8275. <input type="hidden" id="mangle-modal-chain" value="prerouting" oninput="handleMangleChainChange()" onchange="handleMangleChainChange()">
  8276. <datalist id="mangle-chain-options">
  8277. <option value="prerouting">prerouting</option>
  8278. <option value="postrouting">postrouting</option>
  8279. <option value="forward">forward</option>
  8280. <option value="input">input</option>
  8281. <option value="output">output</option>
  8282. </datalist>
  8283. <p id="mangle-chain-hint" class="mt-1 text-[11px] text-slate-400">PREROUTING: packets altered before routing decision (In-Interface only).</p>
  8284. <div id="mangle-action-incompatible-warn" class="hidden mt-1.5 p-2 bg-amber-950/40 border border-amber-600/40 rounded text-amber-300 text-[11px] flex items-center gap-2">
  8285. <svg class="w-4 h-4 text-amber-400 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>
  8286. <span id="mangle-action-incompatible-msg" class="flex-1 font-mono"></span>
  8287. </div>
  8288. </div>
  8289. </div>
  8290. <!-- Src. Address -->
  8291. <div class="flex items-center text-xs py-1" id="mangle-row-src">
  8292. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Address</label>
  8293. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8294. <button type="button" onclick="toggleMangleField('src', true)" id="mangle-plus-src" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8295. <div id="mangle-field-src" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8296. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8297. <input type="checkbox" id="mangle-inv-src" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8298. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8299. </label>
  8300. <input type="text" id="mangle-val-src" placeholder="192.168.10.0/24" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8301. <button type="button" onclick="toggleMangleField('src', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8302. </div>
  8303. </div>
  8304. </div>
  8305. <!-- Dst. Address -->
  8306. <div class="flex items-center text-xs py-1" id="mangle-row-dst">
  8307. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Address</label>
  8308. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8309. <button type="button" onclick="toggleMangleField('dst', true)" id="mangle-plus-dst" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8310. <div id="mangle-field-dst" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8311. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8312. <input type="checkbox" id="mangle-inv-dst" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8313. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8314. </label>
  8315. <input type="text" id="mangle-val-dst" placeholder="0.0.0.0/0" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8316. <button type="button" onclick="toggleMangleField('dst', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8317. </div>
  8318. </div>
  8319. </div>
  8320. <!-- Src. Address List -->
  8321. <div class="flex items-center text-xs py-1" id="mangle-row-srclist">
  8322. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Address List</label>
  8323. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8324. <button type="button" onclick="toggleMangleField('srclist', true)" id="mangle-plus-srclist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8325. <div id="mangle-field-srclist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8326. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8327. <input type="checkbox" id="mangle-inv-srclist" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8328. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8329. </label>
  8330. <select id="mangle-val-srclist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  8331. <button type="button" onclick="toggleMangleField('srclist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8332. </div>
  8333. </div>
  8334. </div>
  8335. <!-- Dst. Address List -->
  8336. <div class="flex items-center text-xs py-1" id="mangle-row-dstlist">
  8337. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Address List</label>
  8338. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8339. <button type="button" onclick="toggleMangleField('dstlist', true)" id="mangle-plus-dstlist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8340. <div id="mangle-field-dstlist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8341. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8342. <input type="checkbox" id="mangle-inv-dstlist" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8343. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8344. </label>
  8345. <select id="mangle-val-dstlist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  8346. <button type="button" onclick="toggleMangleField('dstlist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8347. </div>
  8348. </div>
  8349. </div>
  8350. <!-- Src. Geo Location -->
  8351. <div class="flex items-center text-xs py-1" id="mangle-row-srcgeo">
  8352. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Geo Location</label>
  8353. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8354. <button type="button" onclick="toggleMangleField('srcgeo', true)" id="mangle-plus-srcgeo" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8355. <div id="mangle-field-srcgeo" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8356. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8357. <input type="checkbox" id="mangle-inv-srcgeo" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8358. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8359. </label>
  8360. <select id="mangle-val-srcgeo" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  8361. <button type="button" onclick="toggleMangleField('srcgeo', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8362. </div>
  8363. </div>
  8364. </div>
  8365. <!-- Dst. Geo Location -->
  8366. <div class="flex items-center text-xs py-1" id="mangle-row-dstgeo">
  8367. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Geo Location</label>
  8368. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8369. <button type="button" onclick="toggleMangleField('dstgeo', true)" id="mangle-plus-dstgeo" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8370. <div id="mangle-field-dstgeo" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8371. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8372. <input type="checkbox" id="mangle-inv-dstgeo" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8373. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8374. </label>
  8375. <select id="mangle-val-dstgeo" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  8376. <button type="button" onclick="toggleMangleField('dstgeo', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8377. </div>
  8378. </div>
  8379. </div>
  8380. <hr class="border-[#213042] my-2">
  8381. <!-- Protocol -->
  8382. <div class="flex items-center text-xs py-1" id="mangle-row-proto">
  8383. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Protocol</label>
  8384. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8385. <button type="button" onclick="toggleMangleField('proto', true)" id="mangle-plus-proto" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8386. <div id="mangle-field-proto" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8387. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8388. <input type="checkbox" id="mangle-inv-proto" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8389. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8390. </label>
  8391. <select id="mangle-val-proto" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8392. <option value="tcp">6 (tcp)</option>
  8393. <option value="udp">17 (udp)</option>
  8394. <option value="icmp">ICMP (family-specific)</option><option value="ipv6-icmp">ICMPv6</option>
  8395. <option value="gre">47 (gre)</option>
  8396. <option value="esp">50 (esp)</option>
  8397. <option value="all">all</option>
  8398. </select>
  8399. <button type="button" onclick="toggleMangleField('proto', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8400. </div>
  8401. </div>
  8402. </div>
  8403. <!-- Src. Port -->
  8404. <div class="flex items-center text-xs py-1" id="mangle-row-sport">
  8405. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Port</label>
  8406. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8407. <button type="button" onclick="toggleMangleField('sport', true)" id="mangle-plus-sport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8408. <div id="mangle-field-sport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8409. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8410. <input type="checkbox" id="mangle-inv-sport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8411. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8412. </label>
  8413. <input type="text" id="mangle-val-sport" placeholder="e.g. 80,443 or 1000-2000" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8414. <button type="button" onclick="toggleMangleField('sport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8415. </div>
  8416. </div>
  8417. </div>
  8418. <!-- Dst. Port -->
  8419. <div class="flex items-center text-xs py-1" id="mangle-row-dport">
  8420. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Port</label>
  8421. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8422. <button type="button" onclick="toggleMangleField('dport', true)" id="mangle-plus-dport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8423. <div id="mangle-field-dport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8424. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8425. <input type="checkbox" id="mangle-inv-dport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8426. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8427. </label>
  8428. <input type="text" id="mangle-val-dport" placeholder="e.g. 80,443,8080" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8429. <button type="button" onclick="toggleMangleField('dport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8430. </div>
  8431. </div>
  8432. </div>
  8433. <!-- Any. Port -->
  8434. <div class="flex items-center text-xs py-1" id="mangle-row-anyport">
  8435. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Any. Port</label>
  8436. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8437. <button type="button" onclick="toggleMangleField('anyport', true)" id="mangle-plus-anyport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8438. <div id="mangle-field-anyport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8439. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8440. <input type="checkbox" id="mangle-inv-anyport" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8441. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8442. </label>
  8443. <input type="text" id="mangle-val-anyport" placeholder="53" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8444. <button type="button" onclick="toggleMangleField('anyport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8445. </div>
  8446. </div>
  8447. </div>
  8448. <!-- In. Interface -->
  8449. <div class="flex items-center text-xs py-1" id="mangle-row-iniface">
  8450. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Interface</label>
  8451. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8452. <button type="button" onclick="toggleMangleField('iniface', true)" id="mangle-plus-iniface" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8453. <div id="mangle-field-iniface" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8454. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8455. <input type="checkbox" id="mangle-inv-iniface" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8456. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8457. </label>
  8458. <select id="mangle-val-iniface" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  8459. <button type="button" onclick="toggleMangleField('iniface', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8460. </div>
  8461. </div>
  8462. </div>
  8463. <!-- Out. Interface -->
  8464. <div class="flex items-center text-xs py-1" id="mangle-row-outiface">
  8465. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Interface</label>
  8466. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8467. <button type="button" onclick="toggleMangleField('outiface', true)" id="mangle-plus-outiface" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8468. <div id="mangle-field-outiface" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8469. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8470. <input type="checkbox" id="mangle-inv-outiface" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8471. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8472. </label>
  8473. <select id="mangle-val-outiface" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full"></select>
  8474. <button type="button" onclick="toggleMangleField('outiface', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8475. </div>
  8476. </div>
  8477. </div>
  8478. <hr class="border-[#213042] my-2">
  8479. <!-- In. Interface List -->
  8480. <div class="flex items-center text-xs py-1" id="mangle-row-inifacelist">
  8481. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Interface List</label>
  8482. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8483. <button type="button" onclick="toggleMangleField('inifacelist', true)" id="mangle-plus-inifacelist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8484. <div id="mangle-field-inifacelist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8485. <select id="mangle-val-inifacelist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8486. <option value="LAN">LAN</option>
  8487. <option value="WAN">WAN</option>
  8488. </select>
  8489. <button type="button" onclick="toggleMangleField('inifacelist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8490. </div>
  8491. </div>
  8492. </div>
  8493. <!-- Out. Interface List -->
  8494. <div class="flex items-center text-xs py-1" id="mangle-row-outifacelist">
  8495. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Interface List</label>
  8496. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8497. <button type="button" onclick="toggleMangleField('outifacelist', true)" id="mangle-plus-outifacelist" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8498. <div id="mangle-field-outifacelist" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8499. <select id="mangle-val-outifacelist" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8500. <option value="LAN">LAN</option>
  8501. <option value="WAN">WAN</option>
  8502. </select>
  8503. <button type="button" onclick="toggleMangleField('outifacelist', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8504. </div>
  8505. </div>
  8506. </div>
  8507. <hr class="border-[#213042] my-2">
  8508. <!-- Packet Mark -->
  8509. <div class="flex items-center text-xs py-1" id="mangle-row-pktmark">
  8510. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Mark</label>
  8511. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8512. <button type="button" onclick="toggleMangleField('pktmark', true)" id="mangle-plus-pktmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8513. <div id="mangle-field-pktmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8514. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8515. <input type="checkbox" id="mangle-inv-pktmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8516. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8517. </label>
  8518. <input type="text" id="mangle-val-pktmark" placeholder="e.g. qos_voip" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8519. <button type="button" onclick="toggleMangleField('pktmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8520. </div>
  8521. </div>
  8522. </div>
  8523. <!-- Connection Mark -->
  8524. <div class="flex items-center text-xs py-1" id="mangle-row-connmark">
  8525. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection Mark</label>
  8526. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8527. <button type="button" onclick="toggleMangleField('connmark', true)" id="mangle-plus-connmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8528. <div id="mangle-field-connmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8529. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8530. <input type="checkbox" id="mangle-inv-connmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8531. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8532. </label>
  8533. <input type="text" id="mangle-val-connmark" placeholder="e.g. wan1_conn" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8534. <button type="button" onclick="toggleMangleField('connmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8535. </div>
  8536. </div>
  8537. </div>
  8538. <!-- Routing Mark -->
  8539. <div class="flex items-center text-xs py-1" id="mangle-row-routingmark">
  8540. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Routing Mark</label>
  8541. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8542. <button type="button" onclick="toggleMangleField('routingmark', true)" id="mangle-plus-routingmark" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8543. <div id="mangle-field-routingmark" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8544. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8545. <input type="checkbox" id="mangle-inv-routingmark" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8546. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8547. </label>
  8548. <input type="text" id="mangle-val-routingmark" placeholder="e.g. to_wan2" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8549. <button type="button" onclick="toggleMangleField('routingmark', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8550. </div>
  8551. </div>
  8552. </div>
  8553. <hr class="border-[#213042] my-2">
  8554. <!-- Connection Type -->
  8555. <div class="flex items-center text-xs py-1" id="mangle-row-conntype">
  8556. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection Type</label>
  8557. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8558. <button type="button" onclick="toggleMangleField('conntype', true)" id="mangle-plus-conntype" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8559. <div id="mangle-field-conntype" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8560. <select id="mangle-val-conntype" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8561. <option value="ftp">ftp</option>
  8562. <option value="h323">h323</option>
  8563. <option value="irc">irc</option>
  8564. <option value="pptp">pptp</option>
  8565. <option value="sip">sip</option>
  8566. <option value="tftp">tftp</option>
  8567. </select>
  8568. <button type="button" onclick="toggleMangleField('conntype', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0" title="Remove / Clear">-</button>
  8569. </div>
  8570. </div>
  8571. </div>
  8572. <!-- Connection State -->
  8573. <div class="flex items-center text-xs py-1" id="mangle-row-cs">
  8574. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection State</label>
  8575. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8576. <button type="button" onclick="toggleMangleField('cs', true)" id="mangle-plus-cs" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8577. <div id="mangle-field-cs" class="hidden flex-1 flex flex-wrap items-center gap-2 min-w-0 bg-[#15202e] border border-[#26374d] rounded px-2 py-1">
  8578. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="mangle-modal-cs-established" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">established</span></label>
  8579. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="mangle-modal-cs-related" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">related</span></label>
  8580. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="mangle-modal-cs-new" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">new</span></label>
  8581. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="mangle-modal-cs-invalid" class="rounded bg-[#101721] border-[#2b3e56] text-rose-500"><span class="text-slate-300">invalid</span></label>
  8582. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="mangle-modal-cs-untracked" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500"><span class="text-slate-300">untracked</span></label>
  8583. <button type="button" onclick="toggleMangleField('cs', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0 ml-auto" title="Remove / Clear">-</button>
  8584. </div>
  8585. </div>
  8586. </div>
  8587. <!-- Connection NAT State -->
  8588. <div class="flex items-center text-xs py-1" id="mangle-row-natstate">
  8589. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection NAT State</label>
  8590. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8591. <button type="button" onclick="toggleMangleField('natstate', true)" id="mangle-plus-natstate" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8592. <div id="mangle-field-natstate" class="hidden flex-1 flex items-center gap-3 min-w-0 bg-[#15202e] border border-[#26374d] rounded px-2 py-1">
  8593. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="mangle-modal-nat-srcnat" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">SNAT</span></label>
  8594. <label class="flex items-center gap-1 cursor-pointer"><input type="checkbox" id="mangle-modal-nat-dstnat" class="rounded bg-[#101721] border-[#2b3e56] text-sky-500"><span class="text-slate-300">DNAT</span></label>
  8595. <button type="button" onclick="toggleMangleField('natstate', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0 ml-auto" title="Remove / Clear">-</button>
  8596. </div>
  8597. </div>
  8598. </div>
  8599. </div>
  8600. <!-- TAB PANE: ADVANCED (Screenshot 2) -->
  8601. <div id="mangle-pane-advanced" class="mangle-rule-pane hidden space-y-1">
  8602. <!-- Layer7 Protocol -->
  8603. <!-- Content -->
  8604. <div class="flex items-center text-xs py-1" id="mangle-row-content">
  8605. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Content</label>
  8606. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8607. <button type="button" onclick="toggleMangleField('content', true)" id="mangle-plus-content" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8608. <div id="mangle-field-content" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8609. <input type="text" id="mangle-val-content" placeholder="e.g. facebook.com" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8610. <button type="button" onclick="toggleMangleField('content', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8611. </div>
  8612. </div>
  8613. </div>
  8614. <!-- Connection Bytes -->
  8615. <!-- Connection Rate -->
  8616. <!-- Per Connection Classifier -->
  8617. <!-- Src. MAC Address -->
  8618. <div class="flex items-center text-xs py-1" id="mangle-row-srcmac">
  8619. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. MAC Address</label>
  8620. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8621. <button type="button" onclick="toggleMangleField('srcmac', true)" id="mangle-plus-srcmac" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8622. <div id="mangle-field-srcmac" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8623. <label title="Invert match (!)" class="flex items-center gap-1 cursor-pointer pr-1 select-none">
  8624. <input type="checkbox" id="mangle-inv-srcmac" class="rounded bg-[#101721] border-[#2b3e56] text-amber-500 focus:ring-0 w-3.5 h-3.5">
  8625. <span class="font-mono font-bold text-amber-400 text-xs">!</span>
  8626. </label>
  8627. <input type="text" id="mangle-val-srcmac" placeholder="AA:BB:CC:DD:EE:FF" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8628. <button type="button" onclick="toggleMangleField('srcmac', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8629. </div>
  8630. </div>
  8631. </div>
  8632. <hr class="border-[#213042] my-2">
  8633. <!-- Out. Bridge Port -->
  8634. <div class="flex items-center text-xs py-1" id="mangle-row-outbridgeport">
  8635. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Out. Bridge Port</label>
  8636. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8637. <button type="button" onclick="toggleMangleField('outbridgeport', true)" id="mangle-plus-outbridgeport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8638. <div id="mangle-field-outbridgeport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8639. <input type="text" id="mangle-val-outbridgeport" placeholder="ether1" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8640. <button type="button" onclick="toggleMangleField('outbridgeport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8641. </div>
  8642. </div>
  8643. </div>
  8644. <!-- In. Bridge Port -->
  8645. <div class="flex items-center text-xs py-1" id="mangle-row-inbridgeport">
  8646. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">In. Bridge Port</label>
  8647. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8648. <button type="button" onclick="toggleMangleField('inbridgeport', true)" id="mangle-plus-inbridgeport" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8649. <div id="mangle-field-inbridgeport" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8650. <input type="text" id="mangle-val-inbridgeport" placeholder="ether2" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8651. <button type="button" onclick="toggleMangleField('inbridgeport', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8652. </div>
  8653. </div>
  8654. </div>
  8655. <hr class="border-[#213042] my-2">
  8656. <!-- In. Bridge Port List -->
  8657. <!-- Out. Bridge Port List -->
  8658. <hr class="border-[#213042] my-2">
  8659. <!-- IPsec Policy -->
  8660. <div class="flex items-center text-xs py-1" id="mangle-row-ipsec">
  8661. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">IPsec Policy</label>
  8662. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8663. <button type="button" onclick="toggleMangleField('ipsec', true)" id="mangle-plus-ipsec" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8664. <div id="mangle-field-ipsec" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8665. <select id="mangle-val-ipsec" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8666. <option value="">none</option>
  8667. <option value="in,ipsec">in:ipsec</option>
  8668. <option value="in,none">in:none</option>
  8669. <option value="out,ipsec">out:ipsec</option>
  8670. <option value="out,none">out:none</option>
  8671. </select>
  8672. <button type="button" onclick="toggleMangleField('ipsec', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8673. </div>
  8674. </div>
  8675. </div>
  8676. <!-- TLS Host -->
  8677. <hr class="border-[#213042] my-2">
  8678. <!-- Ingress Priority -->
  8679. <!-- Priority -->
  8680. <!-- DSCP -->
  8681. <div class="flex items-center text-xs py-1" id="mangle-row-dscp">
  8682. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">DSCP</label>
  8683. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8684. <button type="button" onclick="toggleMangleField('dscp', true)" id="mangle-plus-dscp" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8685. <div id="mangle-field-dscp" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8686. <input type="text" id="mangle-val-dscp" placeholder="0-63" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8687. <button type="button" onclick="toggleMangleField('dscp', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8688. </div>
  8689. </div>
  8690. </div>
  8691. <!-- TCP MSS -->
  8692. <div class="flex items-center text-xs py-1" id="mangle-row-tcpmss">
  8693. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TCP MSS</label>
  8694. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8695. <button type="button" onclick="toggleMangleField('tcpmss', true)" id="mangle-plus-tcpmss" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8696. <div id="mangle-field-tcpmss" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8697. <input type="text" id="mangle-val-tcpmss" placeholder="e.g. 1440" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8698. <button type="button" onclick="toggleMangleField('tcpmss', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8699. </div>
  8700. </div>
  8701. </div>
  8702. <!-- TOS -->
  8703. <div class="flex items-center text-xs py-1" id="mangle-row-tos">
  8704. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TOS</label>
  8705. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8706. <select id="mangle-val-tos" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  8707. <option value="">(any)</option>
  8708. <option value="0">0 (Normal Service)</option>
  8709. <option value="min-delay">min-delay (16)</option>
  8710. <option value="max-throughput">max-throughput (8)</option>
  8711. <option value="max-reliability">max-reliability (4)</option>
  8712. <option value="min-cost">min-cost (2)</option>
  8713. </select>
  8714. </div>
  8715. </div>
  8716. <!-- Packet Size -->
  8717. <div class="flex items-center text-xs py-1" id="mangle-row-pktsize">
  8718. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Size</label>
  8719. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8720. <button type="button" onclick="toggleMangleField('pktsize', true)" id="mangle-plus-pktsize" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8721. <div id="mangle-field-pktsize" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8722. <input type="text" id="mangle-val-pktsize" placeholder="e.g. 64-1500" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8723. <button type="button" onclick="toggleMangleField('pktsize', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8724. </div>
  8725. </div>
  8726. </div>
  8727. <!-- Random -->
  8728. <div class="flex items-center text-xs py-1" id="mangle-row-random">
  8729. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Random</label>
  8730. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8731. <button type="button" onclick="toggleMangleField('random', true)" id="mangle-plus-random" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8732. <div id="mangle-field-random" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8733. <input type="text" id="mangle-val-random" placeholder="1-100 %" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8734. <button type="button" onclick="toggleMangleField('random', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8735. </div>
  8736. </div>
  8737. </div>
  8738. <!-- TCP Flags -->
  8739. <div class="flex items-center text-xs py-1" id="mangle-row-tcpflags">
  8740. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TCP Flags</label>
  8741. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8742. <button type="button" onclick="toggleMangleField('tcpflags', true)" id="mangle-plus-tcpflags" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8743. <div id="mangle-field-tcpflags" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8744. <input type="text" id="mangle-val-tcpflags" placeholder="syn,ack,fin,rst" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8745. <button type="button" onclick="toggleMangleField('tcpflags', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8746. </div>
  8747. </div>
  8748. </div>
  8749. <!-- ICMP Options -->
  8750. <div class="flex items-center text-xs py-1" id="mangle-row-icmpoptions">
  8751. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">ICMP Options</label>
  8752. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8753. <button type="button" onclick="toggleMangleField('icmpoptions', true)" id="mangle-plus-icmpoptions" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8754. <div id="mangle-field-icmpoptions" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8755. <input type="text" id="mangle-val-icmpoptions" placeholder="e.g. 8:0 (echo-request)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8756. <button type="button" onclick="toggleMangleField('icmpoptions', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8757. </div>
  8758. </div>
  8759. </div>
  8760. <!-- IPv4 Options -->
  8761. <!-- TTL -->
  8762. <div class="flex items-center text-xs py-1" id="mangle-row-ttl">
  8763. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">TTL</label>
  8764. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8765. <button type="button" onclick="toggleMangleField('ttl', true)" id="mangle-plus-ttl" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8766. <div id="mangle-field-ttl" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8767. <input type="text" id="mangle-val-ttl" placeholder="equal:64 or less-than:10" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8768. <button type="button" onclick="toggleMangleField('ttl', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8769. </div>
  8770. </div>
  8771. </div>
  8772. </div>
  8773. <!-- TAB PANE: EXTRA (Screenshot 3) -->
  8774. <div id="mangle-pane-extra" class="mangle-rule-pane hidden space-y-1">
  8775. <!-- Nth -->
  8776. <div class="flex items-center text-xs py-1" id="mangle-row-nth">
  8777. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Nth</label>
  8778. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8779. <button type="button" onclick="toggleMangleField('nth', true)" id="mangle-plus-nth" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8780. <div id="mangle-field-nth" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8781. <input type="text" id="mangle-val-nth" placeholder="e.g. 2,1" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8782. <button type="button" onclick="toggleMangleField('nth', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8783. </div>
  8784. </div>
  8785. </div>
  8786. <!-- Connection Limit -->
  8787. <div class="flex items-center text-xs py-1" id="mangle-row-connlimit">
  8788. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Connection Limit</label>
  8789. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8790. <button type="button" onclick="toggleMangleField('connlimit', true)" id="mangle-plus-connlimit" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8791. <div id="mangle-field-connlimit" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8792. <input type="text" id="mangle-val-connlimit" placeholder="e.g. 100,32" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8793. <button type="button" onclick="toggleMangleField('connlimit', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8794. </div>
  8795. </div>
  8796. </div>
  8797. <!-- Src. Address Type -->
  8798. <div class="flex items-center text-xs py-1" id="mangle-row-srcaddrtype">
  8799. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Src. Address Type</label>
  8800. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8801. <button type="button" onclick="toggleMangleField('srcaddrtype', true)" id="mangle-plus-srcaddrtype" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8802. <div id="mangle-field-srcaddrtype" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8803. <select id="mangle-val-srcaddrtype" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8804. <option value="unicast">unicast</option>
  8805. <option value="local">local</option>
  8806. <option value="broadcast">broadcast</option>
  8807. <option value="multicast">multicast</option>
  8808. </select>
  8809. <button type="button" onclick="toggleMangleField('srcaddrtype', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8810. </div>
  8811. </div>
  8812. </div>
  8813. <!-- Dst. Address Type -->
  8814. <div class="flex items-center text-xs py-1" id="mangle-row-dstaddrtype">
  8815. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Dst. Address Type</label>
  8816. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8817. <button type="button" onclick="toggleMangleField('dstaddrtype', true)" id="mangle-plus-dstaddrtype" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8818. <div id="mangle-field-dstaddrtype" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8819. <select id="mangle-val-dstaddrtype" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8820. <option value="unicast">unicast</option>
  8821. <option value="local">local</option>
  8822. <option value="broadcast">broadcast</option>
  8823. <option value="multicast">multicast</option>
  8824. </select>
  8825. <button type="button" onclick="toggleMangleField('dstaddrtype', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8826. </div>
  8827. </div>
  8828. </div>
  8829. <!-- Hotspot -->
  8830. <!-- IP Fragment -->
  8831. <div class="flex items-center text-xs py-1" id="mangle-row-ipfragment">
  8832. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">IP Fragment</label>
  8833. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8834. <button type="button" onclick="toggleMangleField('ipfragment', true)" id="mangle-plus-ipfragment" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8835. <div id="mangle-field-ipfragment" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8836. <select id="mangle-val-ipfragment" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8837. <option value="yes">yes</option>
  8838. <option value="no">no</option>
  8839. </select>
  8840. <button type="button" onclick="toggleMangleField('ipfragment', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8841. </div>
  8842. </div>
  8843. </div>
  8844. <!-- Limit -->
  8845. <div class="flex items-center text-xs py-1" id="mangle-row-limit">
  8846. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Limit</label>
  8847. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8848. <button type="button" onclick="toggleMangleField('limit', true)" id="mangle-plus-limit" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8849. <div id="mangle-field-limit" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8850. <input type="text" id="mangle-val-limit" placeholder="100/second,5" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8851. <button type="button" onclick="toggleMangleField('limit', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8852. </div>
  8853. </div>
  8854. </div>
  8855. <!-- Dst. Limit -->
  8856. <!-- Time -->
  8857. <div class="flex items-center text-xs py-1" id="mangle-row-time">
  8858. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Time</label>
  8859. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8860. <button type="button" onclick="toggleMangleField('time', true)" id="mangle-plus-time" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8861. <div id="mangle-field-time" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8862. <input type="text" id="mangle-val-time" placeholder="00:00:00-23:59:59 (UTC)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8863. <button type="button" onclick="toggleMangleField('time', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8864. </div>
  8865. </div>
  8866. </div>
  8867. <!-- PSD -->
  8868. </div>
  8869. <!-- TAB PANE: ACTION (Screenshot 4) -->
  8870. <div id="mangle-pane-action" class="mangle-rule-pane hidden space-y-2">
  8871. <!-- Action -->
  8872. <div class="flex items-center text-xs py-1">
  8873. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Target</label>
  8874. <div class="flex-1">
  8875. <select id="mangle-modal-action" onchange="toggleMangleActionFields()" class="bg-[#15202e] border border-sky-600/80 rounded px-2.5 py-1 text-xs text-sky-400 font-mono focus:outline-none focus:border-sky-500 w-full cursor-pointer">
  8876. <option value="accept" selected>ACCEPT</option>
  8877. <option value="add-dst-to-address-list">SET — add destination to address list</option>
  8878. <option value="add-src-to-address-list">SET — add source to address list</option>
  8879. <option value="change-dscp">DSCP</option>
  8880. <option value="change-mss">TCPMSS</option>
  8881. <option value="change-ttl">TTL / HL</option>
  8882. <option value="jump">JUMP to user chain</option>
  8883. <option value="log">LOG</option>
  8884. <option value="mark-connection">CONNMARK — set connection mark</option>
  8885. <option value="mark-packet">MARK — set packet mark</option>
  8886. <option value="mark-routing">MARK — set routing mark</option>
  8887. <option value="passthrough">COUNT / continue</option>
  8888. <option value="return">RETURN</option>
  8889. <option value="set-priority">CLASSIFY</option>
  8890. </select>
  8891. </div>
  8892. </div>
  8893. <!-- New Connection Mark (when mark-connection) -->
  8894. <div id="mangle-row-newconnmark" class="hidden flex items-center text-xs py-1">
  8895. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">New Connection Mark</label>
  8896. <div class="flex-1">
  8897. <input type="text" id="mangle-modal-newconnmark" placeholder="e.g. wan1_conn" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8898. </div>
  8899. </div>
  8900. <!-- New Routing Mark (when mark-routing) -->
  8901. <div id="mangle-row-newroutingmark" class="hidden flex items-center text-xs py-1">
  8902. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">New Routing Mark</label>
  8903. <div class="flex-1">
  8904. <input type="text" id="mangle-modal-newroutingmark" placeholder="e.g. to_wan2" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8905. </div>
  8906. </div>
  8907. <!-- New Packet Mark (when mark-packet) -->
  8908. <div id="mangle-row-newpktmark" class="hidden flex items-center text-xs py-1">
  8909. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">New Packet Mark</label>
  8910. <div class="flex-1">
  8911. <input type="text" id="mangle-modal-newpktmark" placeholder="e.g. qos_voip" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8912. </div>
  8913. </div>
  8914. <!-- New TCP MSS (when change-mss) -->
  8915. <div id="mangle-row-newmss" class="hidden flex items-center text-xs py-1">
  8916. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">New TCP MSS</label>
  8917. <div class="flex-1">
  8918. <input type="text" id="mangle-modal-newmss" placeholder="1440 or clamp-to-pmtu" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8919. </div>
  8920. </div>
  8921. <!-- New DSCP (when change-dscp) -->
  8922. <div id="mangle-row-newdscp" class="hidden flex items-center text-xs py-1">
  8923. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">New DSCP</label>
  8924. <div class="flex-1">
  8925. <input type="number" id="mangle-modal-newdscp" min="0" max="63" placeholder="0-63" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8926. </div>
  8927. </div>
  8928. <!-- New TTL (when change-ttl) -->
  8929. <div id="mangle-row-newttl" class="hidden flex items-center text-xs py-1">
  8930. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">New TTL</label>
  8931. <div class="flex-1">
  8932. <input type="text" id="mangle-modal-newttl" placeholder="64 (set), -1 (decrease), +1 (increase)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8933. </div>
  8934. </div>
  8935. <!-- New Priority (when set-priority) -->
  8936. <div id="mangle-row-newpriority" class="hidden flex items-center text-xs py-1">
  8937. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">New Priority</label>
  8938. <div class="flex-1">
  8939. <input type="number" id="mangle-modal-newpriority" min="0" max="7" placeholder="0-7" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8940. </div>
  8941. </div>
  8942. <!-- Route (when route) -->
  8943. <!-- Sniff Target (when sniff-pc or sniff-tzsp) -->
  8944. <!-- Passthrough Checkbox (visible for marks / changes) -->
  8945. <div id="mangle-row-passthrough" class="hidden flex items-center text-xs py-1">
  8946. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Passthrough</label>
  8947. <div class="flex items-center">
  8948. <input type="checkbox" id="mangle-modal-passthrough" checked class="rounded bg-[#15202e] border-[#26374d] text-sky-500 focus:ring-0 w-4 h-4 cursor-pointer">
  8949. </div>
  8950. </div>
  8951. <!-- Jump Target (Shown when action === jump) -->
  8952. <div id="mangle-action-jump-box" class="hidden flex items-center text-xs py-1">
  8953. <label class="w-36 sm:w-44 text-right pr-3.5 text-sky-300 shrink-0 select-none">Jump Target</label>
  8954. <div class="flex-1">
  8955. <input type="text" id="mangle-modal-jumptarget" placeholder="chain name" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8956. </div>
  8957. </div>
  8958. <!-- Address List & Timeout (Shown when action starts with add-) -->
  8959. <div id="mangle-action-addrlist-box" class="hidden space-y-1">
  8960. <div class="flex items-center text-xs py-1">
  8961. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Address List</label>
  8962. <div class="flex-1">
  8963. <input type="text" id="mangle-modal-action-addrlist" list="fw-addrlist-datalist" placeholder="list name" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8964. </div>
  8965. </div>
  8966. <div class="flex items-center text-xs py-1">
  8967. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Timeout</label>
  8968. <div class="flex-1">
  8969. <input type="text" id="mangle-modal-action-timeout" placeholder="none or 1d 00:00:00" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
  8970. </div>
  8971. </div>
  8972. </div>
  8973. <hr class="border-[#213042] my-2">
  8974. <!-- Log -->
  8975. <div class="flex items-center text-xs py-1">
  8976. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Log</label>
  8977. <div class="flex items-center">
  8978. <input type="checkbox" id="mangle-modal-log" onchange="toggleMangleLogPrefix()" class="rounded bg-[#15202e] border-[#26374d] text-sky-500 focus:ring-0 w-4 h-4 cursor-pointer">
  8979. </div>
  8980. </div>
  8981. <!-- Log Prefix -->
  8982. <div class="flex items-center text-xs py-1" id="mangle-row-logprefix">
  8983. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Log Prefix</label>
  8984. <div class="flex-1 flex items-center gap-1.5 min-w-0">
  8985. <button type="button" onclick="toggleMangleField('logprefix', true)" id="mangle-plus-logprefix" class="w-5 h-5 flex items-center justify-center bg-[#172435] hover:bg-[#1e3047] border border-[#2b3e56] rounded text-slate-300 hover:text-sky-300 text-xs font-bold leading-none">+</button>
  8986. <div id="mangle-field-logprefix" class="hidden flex-1 flex items-center gap-1.5 min-w-0">
  8987. <input type="text" id="mangle-val-logprefix" placeholder="e.g. MANGLE_LOG: " class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white focus:outline-none focus:border-sky-500 w-full font-mono">
  8988. <button type="button" onclick="toggleMangleField('logprefix', false)" class="w-5 h-5 flex items-center justify-center text-slate-500 hover:text-rose-400 text-xs font-bold leading-none shrink-0">-</button>
  8989. </div>
  8990. </div>
  8991. </div>
  8992. </div>
  8993. <!-- TAB PANE: STATISTICS -->
  8994. <div id="mangle-pane-statistics" class="mangle-rule-pane hidden space-y-2 py-1">
  8995. <!-- Bytes -->
  8996. <div class="flex items-center text-xs">
  8997. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Bytes</label>
  8998. <div class="flex-1">
  8999. <input type="text" id="mangle-stat-bytes" readonly value="0 B" class="bg-[#121c27] border border-sky-600/80 rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  9000. </div>
  9001. </div>
  9002. <!-- Packets -->
  9003. <div class="flex items-center text-xs">
  9004. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packets</label>
  9005. <div class="flex-1">
  9006. <input type="text" id="mangle-stat-packets" readonly value="0" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  9007. </div>
  9008. </div>
  9009. <div class="h-1"></div>
  9010. <!-- Rate -->
  9011. <div class="flex items-center text-xs">
  9012. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Rate</label>
  9013. <div class="flex-1">
  9014. <input type="text" id="mangle-stat-rate" readonly value="0 bps" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  9015. </div>
  9016. </div>
  9017. <!-- Packet Rate -->
  9018. <div class="flex items-center text-xs">
  9019. <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">Packet Rate</label>
  9020. <div class="flex-1">
  9021. <input type="text" id="mangle-stat-pktrate" readonly value="0 p/s" class="bg-[#121c27] border border-[#26374d] rounded px-2.5 py-1 text-xs text-slate-200 font-mono w-full select-all">
  9022. </div>
  9023. </div>
  9024. <div class="h-2"></div>
  9025. <!-- Rate Graph -->
  9026. <div class="space-y-1">
  9027. <div class="text-[11px] text-slate-300 font-medium pl-1">Rate Graph</div>
  9028. <div class="bg-[#0f1721] border border-[#202e40] rounded p-1.5 relative overflow-hidden">
  9029. <svg id="mangle-stat-rate-svg" class="w-full h-24" viewBox="0 0 500 100" preserveAspectRatio="none">
  9030. <!-- Generated grid and sparkline -->
  9031. </svg>
  9032. </div>
  9033. <div class="flex items-center justify-center gap-1.5 text-[11px] text-slate-300 py-0.5">
  9034. <span class="w-2 h-2 rounded-full bg-sky-400"></span>
  9035. <span id="mangle-stat-legend-rate" class="font-mono font-semibold text-white">0 bps</span>
  9036. <span class="text-slate-400">Rate</span>
  9037. </div>
  9038. </div>
  9039. <!-- Packet Rate Graph -->
  9040. <div class="space-y-1">
  9041. <div class="text-[11px] text-slate-300 font-medium pl-1">Packet Rate Graph</div>
  9042. <div class="bg-[#0f1721] border border-[#202e40] rounded p-1.5 relative overflow-hidden">
  9043. <svg id="mangle-stat-pktrate-svg" class="w-full h-24" viewBox="0 0 500 100" preserveAspectRatio="none">
  9044. <!-- Generated grid and sparkline -->
  9045. </svg>
  9046. </div>
  9047. <div class="flex items-center justify-center gap-1.5 text-[11px] text-slate-300 py-0.5">
  9048. <span class="w-2 h-2 rounded-full bg-sky-400"></span>
  9049. <span id="mangle-stat-legend-pktrate" class="font-mono font-semibold text-white">0 p/s</span>
  9050. <span class="text-slate-400">Packet Rate</span>
  9051. </div>
  9052. </div>
  9053. </div>
  9054. </form>
  9055. <!-- Right Vertical Sidebar -->
  9056. <div class="w-40 min-w-[150px] border-l border-[#213042] bg-[#121a24] p-3 text-xs flex flex-col space-y-4 shrink-0">
  9057. <div>
  9058. <button type="button" onclick="copyMangleRule()" class="w-full flex items-center gap-2 text-slate-300 hover:text-sky-300 text-xs py-1 transition-colors">
  9059. <svg class="w-3.5 h-3.5 text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  9060. <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
  9061. <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
  9062. </svg>
  9063. <span>Copy</span>
  9064. </button>
  9065. </div>
  9066. <div class="space-y-2">
  9067. <div class="text-slate-300 font-semibold flex items-center gap-1.5 text-xs">
  9068. <span class="text-amber-400">⚡</span>
  9069. <span>Actions</span>
  9070. </div>
  9071. <div class="flex flex-col space-y-1.5 pl-1">
  9072. <button type="button" onclick="resetCurrentMangleRuleCounters()" class="text-left text-slate-400 hover:text-sky-300 text-xs py-0.5 transition-colors">Reset Counters</button>
  9073. <button type="button" onclick="resetAllFwCounters()" class="text-left text-slate-400 hover:text-sky-300 text-xs py-0.5 transition-colors">Reset All Counters</button>
  9074. </div>
  9075. </div>
  9076. </div>
  9077. </div>
  9078. <!-- Window Bottom Button Bar -->
  9079. <div class="bg-[#141e2b] border-t border-[#1f2d3e] px-4 py-2 flex items-center justify-end gap-2 shrink-0">
  9080. <button type="button" onclick="closeMangleModal()" class="px-4 py-1.5 rounded bg-[#1c2838] hover:bg-[#25364b] border border-[#2a3c54] text-xs font-medium text-slate-300 transition-colors">Cancel</button>
  9081. <button type="button" onclick="applyMangleRule(false)" class="px-5 py-1.5 rounded bg-[#0078d4] hover:bg-[#006cbd] text-xs font-semibold text-white shadow-sm transition-colors">Apply</button>
  9082. <button type="button" onclick="applyMangleRule(true)" class="px-5 py-1.5 rounded bg-[#0078d4] hover:bg-[#006cbd] text-xs font-semibold text-white shadow-sm transition-colors">OK</button>
  9083. </div>
  9084. </div>
  9085. </div>
  9086. <!-- ================= MODAL: ADDRESS LIST ENTRY ================= -->
  9087. <div id="addrlist-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9088. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  9089. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9090. <h3 id="addrlist-modal-title" class="text-sm font-bold text-white">Add Address List Entry</h3>
  9091. <button onclick="closeAddrListModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9092. </div>
  9093. <form onsubmit="submitSaveAddrList(event)" class="p-6 space-y-4 text-xs">
  9094. <input type="hidden" id="addrlist-modal-id">
  9095. <div>
  9096. <label class="block font-semibold text-slate-300 mb-1.5">List Name *</label>
  9097. <input type="text" id="addrlist-modal-name" placeholder="e.g. LAN_Subnets, Blocked_IPs" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  9098. </div>
  9099. <div>
  9100. <label class="block font-semibold text-slate-300 mb-1.5">IP Address / Subnet (CIDR) *</label>
  9101. <input type="text" id="addrlist-modal-addr" placeholder="192.168.10.0/24 or 203.0.113.5" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  9102. </div>
  9103. <div>
  9104. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9105. <input type="text" id="addrlist-modal-comment" placeholder="Optional description" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9106. </div>
  9107. <div class="pt-2">
  9108. <label class="flex items-center space-x-2 cursor-pointer">
  9109. <input type="checkbox" id="addrlist-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  9110. <span class="font-medium text-slate-300">Disabled (X)</span>
  9111. </label>
  9112. </div>
  9113. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9114. <button type="button" onclick="closeAddrListModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9115. <button type="submit" id="addrlist-modal-submit-btn" class="px-5 py-2 rounded-lg font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">Save Entry</button>
  9116. </div>
  9117. </form>
  9118. </div>
  9119. </div>
  9120. <!-- ================= MODAL: GEO INTERVAL SETTINGS ================= -->
  9121. <div id="geo-interval-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9122. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  9123. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9124. <h3 class="text-sm font-bold text-white">Geo-Location Auto-Update Settings</h3>
  9125. <button onclick="closeGeoIntervalModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9126. </div>
  9127. <form onsubmit="submitGeoIntervalSettings(event)" class="p-6 space-y-4 text-xs">
  9128. <div class="p-3 bg-slate-950/60 border border-slate-800 rounded-xl space-y-2">
  9129. <label class="flex items-center space-x-2.5 cursor-pointer">
  9130. <input type="checkbox" id="geo-cfg-autoupdate" checked class="rounded bg-slate-800 border-slate-700 text-emerald-500">
  9131. <span class="font-semibold text-white">Enable Automatic Background Updates</span>
  9132. </label>
  9133. <p class="text-[11px] text-slate-400 pl-6">Automatically fetches the latest country IP blocks from IPdeny registries and updates active kernel ipsets without service interruption.</p>
  9134. </div>
  9135. <div>
  9136. <label class="block font-semibold text-slate-300 mb-1.5">Update Interval *</label>
  9137. <select id="geo-cfg-interval" onchange="toggleCustomGeoInterval()" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  9138. <option value="12">Every 12 Hours</option>
  9139. <option value="24" selected>Every 24 Hours (Daily - Recommended)</option>
  9140. <option value="48">Every 48 Hours (2 Days)</option>
  9141. <option value="72">Every 3 Days</option>
  9142. <option value="168">Every 7 Days (Weekly)</option>
  9143. <option value="720">Every 30 Days (Monthly)</option>
  9144. <option value="custom">Custom Hours...</option>
  9145. </select>
  9146. </div>
  9147. <div id="geo-cfg-custom-box" class="hidden">
  9148. <label class="block font-semibold text-slate-300 mb-1.5">Custom Interval (Hours)</label>
  9149. <input type="number" id="geo-cfg-custom-hours" min="1" max="8760" value="24" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9150. </div>
  9151. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9152. <button type="button" onclick="closeGeoIntervalModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9153. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">Save Schedule</button>
  9154. </div>
  9155. </form>
  9156. </div>
  9157. </div>
  9158. <!-- ================= MODAL: GEO SUBNETS PREVIEW ================= -->
  9159. <div id="geo-subnets-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9160. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9161. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9162. <div>
  9163. <h3 id="geo-subnets-title" class="text-sm font-bold text-white">Country Subnets</h3>
  9164. <span id="geo-subnets-subtitle" class="text-[11px] font-mono text-slate-400">CIDR Blocks</span>
  9165. </div>
  9166. <button onclick="closeGeoSubnetsModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9167. </div>
  9168. <div class="p-6 space-y-3 text-xs">
  9169. <p class="text-slate-400">Showing first 100 subnets in this country zone:</p>
  9170. <div id="geo-subnets-list" class="bg-slate-950 border border-slate-800 rounded-xl p-3 max-h-72 overflow-y-auto font-mono text-[11px] text-emerald-400 space-y-1"></div>
  9171. <div class="pt-3 border-t border-slate-800 flex justify-end">
  9172. <button type="button" onclick="closeGeoSubnetsModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Close</button>
  9173. </div>
  9174. </div>
  9175. </div>
  9176. </div>
  9177. <!-- ================= MODAL: ADD / EDIT ROUTE ================= -->
  9178. <div id="route-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9179. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9180. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9181. <h3 id="route-modal-title" class="text-sm font-bold text-white">Add Static Route</h3>
  9182. <button onclick="closeRouteModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9183. </div>
  9184. <form onsubmit="submitSaveRoute(event)" class="p-6 space-y-4 text-xs">
  9185. <input type="hidden" id="route-modal-id">
  9186. <input type="hidden" id="route-modal-old-dst">
  9187. <input type="hidden" id="route-modal-old-gw">
  9188. <input type="hidden" id="route-modal-old-distance">
  9189. <input type="hidden" id="route-modal-old-table">
  9190. <input type="hidden" id="route-modal-old-iface">
  9191. <div>
  9192. <label class="block font-semibold text-slate-300 mb-1.5">Dst. Address (CIDR) *</label>
  9193. <input type="text" id="route-modal-dst" value="0.0.0.0/0" placeholder="0.0.0.0/0 or 10.0.0.0/8" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  9194. </div>
  9195. <div class="grid grid-cols-2 gap-4">
  9196. <div>
  9197. <label class="block font-semibold text-slate-300 mb-1.5">Gateway (IP or Interface)</label>
  9198. <input type="text" id="route-modal-gw" placeholder="10.42.36.1 or wg0" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  9199. </div>
  9200. <div>
  9201. <label class="block font-semibold text-slate-300 mb-1.5">Routing Table / Mark</label>
  9202. <select id="route-modal-table" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  9203. <option value="main" selected>main</option>
  9204. </select>
  9205. </div>
  9206. </div>
  9207. <div class="grid grid-cols-2 gap-4">
  9208. <div>
  9209. <label class="block font-semibold text-slate-300 mb-1.5">Distance (Metric)</label>
  9210. <input type="number" id="route-modal-distance" value="1" min="0" max="255" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  9211. </div>
  9212. <div>
  9213. <label class="block font-semibold text-slate-300 mb-1.5">Interface (Optional)</label>
  9214. <select id="route-modal-iface" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500"></select>
  9215. </div>
  9216. </div>
  9217. <!-- Gateway Health Check & Behind-NAT Internet Prober -->
  9218. <div class="bg-slate-950/80 border border-slate-800 rounded-xl p-4 space-y-3">
  9219. <div class="flex items-center justify-between">
  9220. <label class="font-bold text-white flex items-center gap-2">
  9221. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
  9222. <span>Gateway Health & Behind-NAT Internet Prober</span>
  9223. </label>
  9224. <span class="text-[10px] px-2 py-0.5 rounded bg-emerald-500/10 text-emerald-300 border border-emerald-500/20 font-mono">Failover Prober</span>
  9225. </div>
  9226. <p class="text-[11px] text-slate-400 leading-relaxed">
  9227. When your router is behind a local modem/ONT/NAT (e.g. 192.168.1.1), the local link stays UP even if the ISP line drops. Enable Internet Check to continuously probe public targets through this gateway and automatically failover if Internet dies.
  9228. </p>
  9229. <div class="grid grid-cols-2 gap-3 pt-1">
  9230. <div>
  9231. <label class="block font-semibold text-slate-300 mb-1">Check Mode</label>
  9232. <select id="route-modal-check-gw" onchange="toggleRouteCheckModeUI()" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  9233. <option value="none">None (Standard static route)</option>
  9234. <option value="internet" selected>Internet Check (Detects Behind-NAT Outages)</option>
  9235. <option value="ping">Direct Gateway Ping (Local next-hop only)</option>
  9236. </select>
  9237. </div>
  9238. <div id="route-row-check-target">
  9239. <div class="flex justify-between items-center mb-1">
  9240. <label class="block font-semibold text-slate-300">Probe Target IP</label>
  9241. <div class="flex gap-1 text-[10px] font-mono">
  9242. <button type="button" onclick="setRouteCheckTarget('1.1.1.1')" class="px-1.5 py-0.2 bg-slate-800 hover:bg-slate-700 text-cyan-400 rounded border border-slate-700">1.1.1.1</button>
  9243. <button type="button" onclick="setRouteCheckTarget('8.8.8.8')" class="px-1.5 py-0.2 bg-slate-800 hover:bg-slate-700 text-emerald-400 rounded border border-slate-700">8.8.8.8</button>
  9244. <button type="button" onclick="setRouteCheckTarget('9.9.9.9')" class="px-1.5 py-0.2 bg-slate-800 hover:bg-slate-700 text-purple-400 rounded border border-slate-700">9.9.9.9</button>
  9245. </div>
  9246. </div>
  9247. <input type="text" id="route-modal-check-target" value="1.1.1.1" placeholder="e.g. 1.1.1.1 or 8.8.8.8" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  9248. </div>
  9249. </div>
  9250. <div id="route-row-check-params" class="grid grid-cols-2 gap-3 pt-1">
  9251. <div>
  9252. <label class="block font-semibold text-slate-300 mb-1">Check Interval (Seconds)</label>
  9253. <input type="number" id="route-modal-check-interval" value="5" min="2" max="60" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  9254. </div>
  9255. <div>
  9256. <label class="block font-semibold text-slate-300 mb-1">Failure Threshold (Drops)</label>
  9257. <input type="number" id="route-modal-check-threshold" value="3" min="1" max="10" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500" title="Consecutive timeouts before withdrawing route for failover">
  9258. </div>
  9259. </div>
  9260. </div>
  9261. <div>
  9262. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9263. <input type="text" id="route-modal-comment" placeholder="e.g. Primary WAN Default Gateway" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9264. </div>
  9265. <div class="pt-2">
  9266. <label class="flex items-center space-x-2 cursor-pointer">
  9267. <input type="checkbox" id="route-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  9268. <span class="font-medium text-slate-300">Disabled (X)</span>
  9269. </label>
  9270. </div>
  9271. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9272. <button type="button" onclick="closeRouteModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9273. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20">Save Route</button>
  9274. </div>
  9275. </form>
  9276. </div>
  9277. </div>
  9278. <!-- ================= MODAL: ADD POLICY ROUTING RULE ================= -->
  9279. <div id="prule-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9280. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9281. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9282. <h3 id="prule-modal-title" class="text-sm font-bold text-white">Add Policy Routing Rule</h3>
  9283. <button onclick="closePolicyRuleModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9284. </div>
  9285. <form onsubmit="submitSavePolicyRule(event)" class="p-6 space-y-4 text-xs">
  9286. <input type="hidden" id="prule-modal-id">
  9287. <div class="grid grid-cols-2 gap-4">
  9288. <div>
  9289. <label class="block font-semibold text-slate-300 mb-1.5">Priority / Preference</label>
  9290. <input type="number" id="prule-modal-pref" placeholder="1000" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9291. </div>
  9292. <div>
  9293. <label class="block font-semibold text-slate-300 mb-1.5">Routing Mark (fwmark)</label>
  9294. <input type="text" id="prule-modal-mark" placeholder="e.g. to_WAN2 or 100" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9295. </div>
  9296. </div>
  9297. <div class="grid grid-cols-2 gap-4">
  9298. <div>
  9299. <label class="block font-semibold text-slate-300 mb-1.5">Src. Address (CIDR)</label>
  9300. <input type="text" id="prule-modal-src" placeholder="192.168.10.0/24" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9301. </div>
  9302. <div>
  9303. <label class="block font-semibold text-slate-300 mb-1.5">Dst. Address (CIDR)</label>
  9304. <input type="text" id="prule-modal-dst" placeholder="0.0.0.0/0" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9305. </div>
  9306. </div>
  9307. <div class="grid grid-cols-2 gap-4">
  9308. <div>
  9309. <label class="block font-semibold text-slate-300 mb-1.5">Lookup Table *</label>
  9310. <select id="prule-modal-table" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  9311. </div>
  9312. <div>
  9313. <label class="block font-semibold text-slate-300 mb-1.5">Action</label>
  9314. <select id="prule-modal-action" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9315. <option value="lookup" selected>lookup (standard)</option>
  9316. <option value="unreachable">unreachable</option>
  9317. <option value="blackhole">blackhole</option>
  9318. </select>
  9319. </div>
  9320. </div>
  9321. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9322. <button type="button" onclick="closePolicyRuleModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9323. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20">Save Policy Rule</button>
  9324. </div>
  9325. </form>
  9326. </div>
  9327. </div>
  9328. <!-- ================= MODAL: CREATE ROUTING TABLE ================= -->
  9329. <div id="table-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9330. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  9331. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9332. <h3 class="text-sm font-bold text-white">Create New Routing Table</h3>
  9333. <button onclick="closeTableModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9334. </div>
  9335. <form onsubmit="submitSaveTable(event)" class="p-6 space-y-4 text-xs">
  9336. <div>
  9337. <label class="block font-semibold text-slate-300 mb-1.5">Table Name *</label>
  9338. <input type="text" id="table-modal-name" placeholder="e.g. wan2_traffic, vpn_table" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  9339. </div>
  9340. <div>
  9341. <label class="block font-semibold text-slate-300 mb-1.5">FIB Table ID (Optional)</label>
  9342. <input type="number" id="table-modal-id" min="1" max="252" placeholder="Auto (100+)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9343. </div>
  9344. <div>
  9345. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9346. <input type="text" id="table-modal-comment" placeholder="Purpose of this routing table" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9347. </div>
  9348. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9349. <button type="button" onclick="closeTableModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9350. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20">Create Table</button>
  9351. </div>
  9352. </form>
  9353. </div>
  9354. </div>
  9355. <!-- ================= MODAL: ADD/EDIT EoIP TUNNEL ================= -->
  9356. <div id="eoip-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9357. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9358. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9359. <div>
  9360. <h3 id="eoip-modal-title" class="text-sm font-bold text-white">Add EoIP Tunnel</h3>
  9361. <p class="text-[11px] text-slate-400">Layer 2 Ethernet-over-IP (GRETAP) Tunnel</p>
  9362. </div>
  9363. <button onclick="closeEoIPModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9364. </div>
  9365. <form onsubmit="submitSaveEoIP(event)" class="p-6 space-y-4 text-xs font-sans">
  9366. <input type="hidden" id="eoip-modal-id">
  9367. <div class="grid grid-cols-2 gap-4">
  9368. <div>
  9369. <label class="block font-semibold text-slate-300 mb-1.5">Interface Name *</label>
  9370. <input type="text" id="eoip-modal-name" placeholder="eoip-tunnel1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  9371. </div>
  9372. <div>
  9373. <label class="block font-semibold text-slate-300 mb-1.5">Tunnel ID (1 - 65535) *</label>
  9374. <input type="number" id="eoip-modal-tunnelid" min="1" max="65535" placeholder="10" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  9375. </div>
  9376. </div>
  9377. <div class="grid grid-cols-2 gap-4">
  9378. <div>
  9379. <label class="block font-semibold text-slate-300 mb-1.5">Remote Address (Remote Router IP) *</label>
  9380. <input type="text" id="eoip-modal-remote" placeholder="e.g. 10.2.145.253" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  9381. <span class="text-[10px] text-slate-400">Underlay public or transport IP of remote peer</span>
  9382. </div>
  9383. <div>
  9384. <label class="block font-semibold text-slate-300 mb-1.5">Tunnel IP / Subnet (CIDR)</label>
  9385. <input type="text" id="eoip-modal-ip" placeholder="e.g. 172.16.33.254/30" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  9386. <span class="text-[10px] text-slate-400">Overlay IPv4 assigned directly to tunnel (/30, /24)</span>
  9387. </div>
  9388. </div>
  9389. <div class="grid grid-cols-2 gap-4">
  9390. <div>
  9391. <label class="block font-semibold text-slate-300 mb-1.5">Underlay Local IP (GRE Source)</label>
  9392. <input type="text" id="eoip-modal-local" placeholder="e.g. 10.42.36.5" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  9393. <span class="text-[10px] text-slate-400">Local router IP for outer GRE packets</span>
  9394. </div>
  9395. <div>
  9396. <label class="block font-semibold text-slate-300 mb-1.5">MAC Address (Auto if empty)</label>
  9397. <input type="text" id="eoip-modal-mac" placeholder="02:64:xx:xx:xx:xx" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9398. </div>
  9399. </div>
  9400. <div class="grid grid-cols-2 gap-4">
  9401. <div>
  9402. <label class="block font-semibold text-slate-300 mb-1.5">MTU</label>
  9403. <input type="number" id="eoip-modal-mtu" value="1500" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9404. </div>
  9405. <div>
  9406. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9407. <input type="text" id="eoip-modal-comment" placeholder="Bridge to Office Branch" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9408. </div>
  9409. </div>
  9410. <div class="flex items-center justify-between pt-1">
  9411. <label class="flex items-center space-x-2 text-slate-300">
  9412. <input type="checkbox" id="eoip-modal-clampmss" checked class="rounded bg-slate-800 border-slate-700 text-emerald-600">
  9413. <span>Clamp TCP MSS (PMTUD protection)</span>
  9414. </label>
  9415. <label class="flex items-center space-x-2 text-slate-300">
  9416. <input type="checkbox" id="eoip-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-600">
  9417. <span>Disabled</span>
  9418. </label>
  9419. </div>
  9420. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9421. <button type="button" onclick="closeEoIPModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9422. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">Save Tunnel</button>
  9423. </div>
  9424. </form>
  9425. </div>
  9426. </div>
  9427. <!-- ================= MODAL: ADD/EDIT VXLAN INTERFACE ================= -->
  9428. <div id="vxlan-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9429. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9430. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9431. <div>
  9432. <h3 id="vxlan-modal-title" class="text-sm font-bold text-white">Add VXLAN Interface</h3>
  9433. <p class="text-[11px] text-slate-400">Virtual Extensible LAN (RFC 7348) L2 Overlay</p>
  9434. </div>
  9435. <button onclick="closeVXLANModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9436. </div>
  9437. <form onsubmit="submitSaveVXLAN(event)" class="p-6 space-y-4 text-xs font-sans max-h-[85vh] overflow-y-auto">
  9438. <input type="hidden" id="vxlan-modal-id">
  9439. <div class="grid grid-cols-2 gap-4">
  9440. <div>
  9441. <label class="block font-semibold text-slate-300 mb-1.5">Interface Name *</label>
  9442. <input type="text" id="vxlan-modal-name" placeholder="vxlan100" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9443. </div>
  9444. <div>
  9445. <label class="block font-semibold text-slate-300 mb-1.5">VNI (1 - 16777215) *</label>
  9446. <input type="number" id="vxlan-modal-vni" min="1" max="16777215" placeholder="100" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9447. </div>
  9448. </div>
  9449. <div class="grid grid-cols-2 gap-4">
  9450. <div>
  9451. <label class="block font-semibold text-slate-300 mb-1.5">UDP Port (Default: 4789)</label>
  9452. <input type="number" id="vxlan-modal-port" value="4789" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9453. </div>
  9454. <div>
  9455. <label class="block font-semibold text-slate-300 mb-1.5">Overlay IP / CIDR</label>
  9456. <input type="text" id="vxlan-modal-ip" placeholder="e.g. 10.200.0.1/24" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9457. </div>
  9458. </div>
  9459. <div class="grid grid-cols-2 gap-4">
  9460. <div>
  9461. <label class="block font-semibold text-slate-300 mb-1.5">Underlay Interface</label>
  9462. <input type="text" id="vxlan-modal-parent" placeholder="e.g. eth0" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9463. <span class="text-[10px] text-slate-400">Physical/VLAN egress device</span>
  9464. </div>
  9465. <div>
  9466. <label class="block font-semibold text-slate-300 mb-1.5">Local Source IP</label>
  9467. <input type="text" id="vxlan-modal-local" placeholder="e.g. 192.168.1.10" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9468. <span class="text-[10px] text-slate-400">Source VTEP IP for outer UDP</span>
  9469. </div>
  9470. </div>
  9471. <div class="grid grid-cols-2 gap-4">
  9472. <div>
  9473. <label class="block font-semibold text-slate-300 mb-1.5">Remote IP (Point-to-Point)</label>
  9474. <input type="text" id="vxlan-modal-remote" placeholder="e.g. 192.168.1.20" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9475. <span class="text-[10px] text-slate-400">Leave empty if using VTEPs list</span>
  9476. </div>
  9477. <div>
  9478. <label class="block font-semibold text-slate-300 mb-1.5">Multicast Group (Optional)</label>
  9479. <input type="text" id="vxlan-modal-group" placeholder="e.g. 239.1.1.1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9480. <span class="text-[10px] text-slate-400">Optional IGMP multicast group</span>
  9481. </div>
  9482. </div>
  9483. <div class="grid grid-cols-2 gap-4">
  9484. <div>
  9485. <label class="block font-semibold text-slate-300 mb-1.5">MTU</label>
  9486. <input type="number" id="vxlan-modal-mtu" value="1500" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9487. </div>
  9488. <div>
  9489. <label class="block font-semibold text-slate-300 mb-1.5">MAC Address (Auto if empty)</label>
  9490. <input type="text" id="vxlan-modal-mac" placeholder="02:58:xx:xx:xx:xx" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9491. </div>
  9492. </div>
  9493. <div>
  9494. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9495. <input type="text" id="vxlan-modal-comment" placeholder="Data center overlay network" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9496. </div>
  9497. <div class="flex items-center justify-between pt-1">
  9498. <label class="flex items-center space-x-2 text-slate-300">
  9499. <input type="checkbox" id="vxlan-modal-learning" checked class="rounded bg-slate-800 border-slate-700 text-amber-500">
  9500. <span>MAC Address Learning</span>
  9501. </label>
  9502. <label class="flex items-center space-x-2 text-slate-300">
  9503. <input type="checkbox" id="vxlan-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-600">
  9504. <span>Disabled</span>
  9505. </label>
  9506. </div>
  9507. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9508. <button type="button" onclick="closeVXLANModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9509. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20">Save Interface</button>
  9510. </div>
  9511. </form>
  9512. </div>
  9513. </div>
  9514. <!-- ================= MODAL: ADD REMOTE VTEP ================= -->
  9515. <div id="vxlan-vtep-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9516. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  9517. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9518. <div>
  9519. <h3 id="vxlan-vtep-modal-title" class="text-sm font-bold text-white">Add Remote VTEP</h3>
  9520. <p class="text-[11px] text-slate-400">Unicast peer for VXLAN flood replication</p>
  9521. </div>
  9522. <button onclick="closeVXLANVtepModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9523. </div>
  9524. <form onsubmit="submitSaveVXLANVtep(event)" class="p-6 space-y-4 text-xs font-sans">
  9525. <div>
  9526. <label class="block font-semibold text-slate-300 mb-1.5">Parent VXLAN Interface *</label>
  9527. <select id="vxlan-vtep-modal-interface" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9528. </select>
  9529. </div>
  9530. <div>
  9531. <label class="block font-semibold text-slate-300 mb-1.5">Remote IP Address *</label>
  9532. <input type="text" id="vxlan-vtep-modal-remote" placeholder="e.g. 192.168.10.50" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9533. <span class="text-[10px] text-slate-400">Unicast transport IP of the remote VTEP</span>
  9534. </div>
  9535. <div>
  9536. <label class="block font-semibold text-slate-300 mb-1.5">UDP Port (Optional)</label>
  9537. <input type="number" id="vxlan-vtep-modal-port" placeholder="Default (interface port)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9538. </div>
  9539. <div>
  9540. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9541. <input type="text" id="vxlan-vtep-modal-comment" placeholder="Remote Data Center Host 2" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9542. </div>
  9543. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9544. <button type="button" onclick="closeVXLANVtepModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9545. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20">Add VTEP</button>
  9546. </div>
  9547. </form>
  9548. </div>
  9549. </div>
  9550. <!-- ================= MODAL: ADD/EDIT AeroTUN TUNNEL ================= -->
  9551. <div id="aerotun-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9552. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9553. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9554. <div>
  9555. <h3 id="aerotun-modal-title" class="text-sm font-bold text-white">Add AeroTUN Interface</h3>
  9556. <p class="text-[11px] text-slate-400">Noise identity handshake + XChaCha20-Poly1305</p>
  9557. </div>
  9558. <button onclick="closeAeroTUNModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9559. </div>
  9560. <form onsubmit="submitSaveAeroTUN(event)" class="p-6 space-y-4 text-xs font-sans max-h-[80vh] overflow-y-auto">
  9561. <p id="aerotun-setup-hint" role="status" class="p-3 rounded-lg bg-sky-950/40 text-sky-200"></p>
  9562. <input type="hidden" id="aerotun-modal-id">
  9563. <div class="space-y-2">
  9564. <label class="block text-slate-300">Protocol</label>
  9565. <select id="aerotun-modal-protocol" onchange="updateAeroTUNSetupHint()" class="w-full bg-slate-800 rounded p-2"><option value="3">v3 — Noise, pinned identities and forward secrecy</option><option value="2">v2 — legacy PSK protocol</option></select>
  9566. <div id="aerotun-v3-fields" class="space-y-2">
  9567. <label class="block text-slate-300">Local public key</label>
  9568. <input id="aerotun-modal-public" readonly class="w-full bg-slate-800 rounded p-2 font-mono" placeholder="Generating local identity…">
  9569. <button type="button" onclick="copyAeroTUNPublicKey()" class="text-sky-400 underline">Copy local public key</button>
  9570. <button type="button" id="aerotun-retry-identity" onclick="generateAeroTUNIdentity()" class="hidden text-sky-400 underline ml-3">Retry key generation</button>
  9571. <label class="block text-slate-300">Pinned peer public key</label>
  9572. <input id="aerotun-modal-peerkey" oninput="updateAeroTUNSetupHint()" class="w-full bg-slate-800 rounded p-2 font-mono" placeholder="Public key from the other endpoint">
  9573. <label class="block text-slate-300">Allowed peer source prefixes (comma separated)</label>
  9574. <input id="aerotun-modal-allowed" class="w-full bg-slate-800 rounded p-2 font-mono" placeholder="10.99.0.2/32, 192.168.20.0/24">
  9575. <p class="text-xs text-slate-400">Exchange public keys through a trusted channel. Allowed source prefixes do not create routes.</p>
  9576. </div>
  9577. </div>
  9578. <div class="grid grid-cols-2 gap-4">
  9579. <div>
  9580. <label class="block font-semibold text-slate-300 mb-1.5">Interface Name *</label>
  9581. <input type="text" id="aerotun-modal-name" placeholder="aerotun0" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-sky-500">
  9582. </div>
  9583. <div>
  9584. <label class="block font-semibold text-slate-300 mb-1.5">Operating Mode *</label>
  9585. <select id="aerotun-modal-mode" onchange="onAeroTUNModeChange()" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-sky-500">
  9586. <option value="server">Server (Listen for incoming peers)</option>
  9587. <option value="client">Client (Initiate connection to peer)</option>
  9588. </select>
  9589. </div>
  9590. </div>
  9591. <div class="grid grid-cols-2 gap-4">
  9592. <div>
  9593. <label class="block font-semibold text-slate-300 mb-1.5">Listen UDP Port</label>
  9594. <input type="number" id="aerotun-modal-port" min="0" max="65535" placeholder="51830" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-sky-500">
  9595. <span class="text-[10px] text-slate-400">Default: 51830</span>
  9596. </div>
  9597. <div>
  9598. <label class="block font-semibold text-slate-300 mb-1.5">Remote Endpoint (Host:Port)</label>
  9599. <input type="text" id="aerotun-modal-endpoint" placeholder="peer.example.com:51830" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-sky-500">
  9600. <span class="text-[10px] text-slate-400">Required in Client mode</span>
  9601. </div>
  9602. </div>
  9603. <div class="grid grid-cols-2 gap-4">
  9604. <div>
  9605. <label class="block font-semibold text-slate-300 mb-1.5">Local Tunnel IP (CIDR) *</label>
  9606. <input type="text" id="aerotun-modal-local" placeholder="10.99.0.1/24" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-sky-500">
  9607. <span class="text-[10px] text-slate-400">Overlay IPv4 assigned to interface</span>
  9608. </div>
  9609. <div>
  9610. <label class="block font-semibold text-slate-300 mb-1.5">Peer Tunnel IP (Optional CIDR)</label>
  9611. <input type="text" id="aerotun-modal-peer" oninput="onAeroTUNPeerChange()" placeholder="10.99.0.2/24" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-sky-500">
  9612. <span class="text-[10px] text-slate-400">Adds a host route to the peer overlay IP</span>
  9613. </div>
  9614. </div>
  9615. <div>
  9616. <div class="flex items-center justify-between mb-1.5">
  9617. <label class="block font-semibold text-slate-300">Pre-Shared Key (PSK) *</label>
  9618. <button type="button" onclick="togglePasswordVisibility('aerotun-modal-psk')" class="text-[11px] text-slate-300 underline">Show / hide PSK</button>
  9619. <button type="button" onclick="copyAeroTUNPSK()" class="text-[11px] text-sky-400 underline">Copy PSK</button>
  9620. <button type="button" onclick="generateAeroTUNKey()" class="text-[11px] text-sky-400 hover:text-sky-300 underline font-mono">⚡ Generate Key</button>
  9621. </div>
  9622. <input type="password" autocomplete="new-password" id="aerotun-modal-psk" placeholder="Leave blank when editing to retain the existing key" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-sky-500">
  9623. <span class="text-[10px] text-slate-400">Both ends must use the same random 256-bit PSK and protocol version. V3 also requires pinned peer public keys.</span>
  9624. </div>
  9625. <div class="grid grid-cols-2 gap-4">
  9626. <div>
  9627. <label class="block font-semibold text-slate-300 mb-1.5">MTU</label>
  9628. <input type="number" id="aerotun-modal-mtu" min="576" max="9000" value="1420" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-sky-500">
  9629. <span class="text-[10px] text-slate-400">Maximum requested MTU; reduced to fit the local underlay</span>
  9630. </div>
  9631. <div>
  9632. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9633. <input type="text" id="aerotun-modal-comment" placeholder="High-speed interconnect" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9634. </div>
  9635. </div>
  9636. <div class="flex flex-wrap items-center justify-between pt-2 border-t border-slate-800/80 gap-3">
  9637. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  9638. <input type="checkbox" id="aerotun-modal-compression" class="rounded bg-slate-800 border-slate-700 text-sky-600">
  9639. <span class="font-medium">Adaptive LZ4 Compression</span>
  9640. </label>
  9641. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  9642. <input type="checkbox" id="aerotun-modal-defroute" class="rounded bg-slate-800 border-slate-700 text-sky-600">
  9643. <span class="font-medium">Default Route</span>
  9644. </label>
  9645. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  9646. <input type="checkbox" id="aerotun-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-600">
  9647. <span class="font-medium">Disabled (X)</span>
  9648. </label>
  9649. </div>
  9650. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9651. <button type="button" onclick="closeAeroTUNModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9652. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-500/20">Save AeroTUN</button>
  9653. </div>
  9654. </form>
  9655. </div>
  9656. </div>
  9657. <!-- ================= MODAL: ADD/EDIT PPP SECRET (USER) ================= -->
  9658. <div id="ppp-secret-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9659. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9660. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9661. <div>
  9662. <h3 id="secret-modal-title" class="text-sm font-bold text-white">Add PPP Secret</h3>
  9663. <p class="text-[11px] text-slate-400">User account for PPPoE, PPTP, and L2TP authentication</p>
  9664. </div>
  9665. <button onclick="closePPPSecretModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9666. </div>
  9667. <form onsubmit="submitSavePPPSecret(event)" class="p-6 space-y-4 text-xs font-sans">
  9668. <input type="hidden" id="secret-modal-id">
  9669. <div class="grid grid-cols-2 gap-4">
  9670. <div>
  9671. <label class="block font-semibold text-slate-300 mb-1.5">Username (Name) *</label>
  9672. <input type="text" id="secret-modal-name" placeholder="client_user1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-violet-500">
  9673. </div>
  9674. <div>
  9675. <label class="block font-semibold text-slate-300 mb-1.5">Password *</label>
  9676. <input type="password" id="secret-modal-password" placeholder="••••••••" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-violet-500">
  9677. </div>
  9678. </div>
  9679. <div class="grid grid-cols-2 gap-4">
  9680. <div>
  9681. <label class="block font-semibold text-slate-300 mb-1.5">Service</label>
  9682. <select id="secret-modal-service" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9683. <option value="any">any</option>
  9684. <option value="pppoe">pppoe</option>
  9685. <option value="pptp">pptp</option>
  9686. <option value="l2tp">l2tp</option>
  9687. </select>
  9688. </div>
  9689. <div>
  9690. <label class="block font-semibold text-slate-300 mb-1.5">Profile</label>
  9691. <select id="secret-modal-profile" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  9692. </div>
  9693. </div>
  9694. <div class="grid grid-cols-2 gap-4">
  9695. <div>
  9696. <label class="block font-semibold text-slate-300 mb-1.5">Local Address (Optional)</label>
  9697. <input type="text" id="secret-modal-local" placeholder="Override server IP" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9698. </div>
  9699. <div>
  9700. <label class="block font-semibold text-slate-300 mb-1.5">Remote Address (Optional Static IP)</label>
  9701. <input type="text" id="secret-modal-remote" placeholder="Static client IP" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9702. </div>
  9703. </div>
  9704. <div>
  9705. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9706. <input type="text" id="secret-modal-comment" placeholder="Home user / Office user" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9707. </div>
  9708. <div class="flex items-center space-x-2 pt-1">
  9709. <input type="checkbox" id="secret-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-600">
  9710. <label for="secret-modal-disabled" class="text-slate-300">Disabled</label>
  9711. </div>
  9712. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9713. <button type="button" onclick="closePPPSecretModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9714. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-violet-600 hover:bg-violet-500 text-white shadow-lg shadow-violet-500/20">Save Secret</button>
  9715. </div>
  9716. </form>
  9717. </div>
  9718. </div>
  9719. <!-- ================= MODAL: ADD/EDIT PPP PROFILE ================= -->
  9720. <div id="ppp-profile-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9721. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9722. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9723. <div>
  9724. <h3 id="profile-modal-title" class="text-sm font-bold text-white">Add PPP Profile</h3>
  9725. <p class="text-[11px] text-slate-400">IP pool and encryption settings for PPP servers</p>
  9726. </div>
  9727. <button onclick="closePPPProfileModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9728. </div>
  9729. <form onsubmit="submitSavePPPProfile(event)" class="p-6 space-y-4 text-xs font-sans">
  9730. <input type="hidden" id="profile-modal-id">
  9731. <div>
  9732. <label class="block font-semibold text-slate-300 mb-1.5">Profile Name *</label>
  9733. <input type="text" id="profile-modal-name" placeholder="e.g. office-pool" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-violet-500">
  9734. </div>
  9735. <div class="grid grid-cols-2 gap-4">
  9736. <div>
  9737. <label class="block font-semibold text-slate-300 mb-1.5">Local Address *</label>
  9738. <input type="text" id="profile-modal-local" placeholder="10.10.10.1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9739. </div>
  9740. <div>
  9741. <label class="block font-semibold text-slate-300 mb-1.5">Remote Address (Pool) *</label>
  9742. <input type="text" id="profile-modal-remote" placeholder="10.10.10.10-10.10.10.50" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9743. </div>
  9744. </div>
  9745. <div class="grid grid-cols-2 gap-4">
  9746. <div>
  9747. <label class="block font-semibold text-slate-300 mb-1.5">DNS Servers</label>
  9748. <input type="text" id="profile-modal-dns" value="1.1.1.1,8.8.8.8" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9749. </div>
  9750. <div>
  9751. <label class="block font-semibold text-slate-300 mb-1.5">Use Encryption</label>
  9752. <select id="profile-modal-encryption" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9753. <option value="no">no</option>
  9754. <option value="yes">yes</option>
  9755. <option value="required" selected>required (MPPE-128)</option>
  9756. </select>
  9757. </div>
  9758. </div>
  9759. <div>
  9760. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  9761. <input type="text" id="profile-modal-comment" placeholder="Description of this profile" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  9762. </div>
  9763. <div class="flex items-center space-x-2 pt-1">
  9764. <input type="checkbox" id="profile-modal-onlyone" checked class="rounded bg-slate-800 border-slate-700 text-violet-600">
  9765. <label for="profile-modal-onlyone" class="text-slate-300">Only One session per user account</label>
  9766. </div>
  9767. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9768. <button type="button" onclick="closePPPProfileModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9769. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-violet-600 hover:bg-violet-500 text-white shadow-lg shadow-violet-500/20">Save Profile</button>
  9770. </div>
  9771. </form>
  9772. </div>
  9773. </div>
  9774. <!-- ================= MODAL: IPSEC ROADWARRIOR SERVER ================= -->
  9775. <div id="ipsec-server-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9776. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9777. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9778. <div>
  9779. <h3 class="text-sm font-bold text-white">IPSec (IKEv2) Roadwarrior Server</h3>
  9780. <p class="text-[11px] text-slate-400">Mobile VPN server for Windows, macOS, iOS, Android clients</p>
  9781. </div>
  9782. <button onclick="closeIPSecServerModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9783. </div>
  9784. <form onsubmit="submitSaveIPSecServer(event)" class="p-6 space-y-4 text-xs font-sans">
  9785. <div class="flex items-center justify-between border-b border-slate-800 pb-3">
  9786. <span class="text-slate-300 font-semibold">Server State</span>
  9787. <div class="flex items-center gap-3">
  9788. <span id="modal-ipsec-server-status" class="px-2 py-0.5 rounded text-[10px] font-mono font-semibold bg-slate-800 text-slate-400">STOPPED</span>
  9789. <label class="relative inline-flex items-center cursor-pointer">
  9790. <input type="checkbox" id="modal-ipsec-server-enabled" class="sr-only peer">
  9791. <div class="w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-slate-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-amber-600"></div>
  9792. </label>
  9793. </div>
  9794. </div>
  9795. <div>
  9796. <label class="block font-semibold text-slate-300 mb-1.5">Pre-Shared Key (PSK) *</label>
  9797. <input type="password" id="modal-ipsec-server-psk" placeholder="••••••••" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  9798. </div>
  9799. <div class="grid grid-cols-2 gap-4">
  9800. <div>
  9801. <label class="block font-semibold text-slate-300 mb-1.5">Virtual IP Pool (CIDR) *</label>
  9802. <input type="text" id="modal-ipsec-server-pool" value="10.10.30.0/24" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9803. </div>
  9804. <div>
  9805. <label class="block font-semibold text-slate-300 mb-1.5">DNS Servers</label>
  9806. <input type="text" id="modal-ipsec-server-dns" value="1.1.1.1,8.8.8.8" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9807. </div>
  9808. </div>
  9809. <div>
  9810. <label class="block font-semibold text-slate-300 mb-1.5">Local Traffic Selector (Subnet)</label>
  9811. <input type="text" id="modal-ipsec-server-subnet" value="0.0.0.0/0" placeholder="0.0.0.0/0 (Full Tunnel)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9812. </div>
  9813. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9814. <button type="button" onclick="closeIPSecServerModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9815. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20">Apply IPSec Server</button>
  9816. </div>
  9817. </form>
  9818. </div>
  9819. </div>
  9820. <!-- ================= MODAL: WIREGUARD CLIENT CONFIG VIEWER ================= -->
  9821. <div id="wg-client-conf-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9822. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9823. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9824. <div>
  9825. <h3 class="text-sm font-bold text-white">WireGuard Client Configuration</h3>
  9826. <p class="text-[11px] text-slate-400">Import this .conf into WireGuard on iOS, Android, macOS or Windows</p>
  9827. </div>
  9828. <button onclick="closeWGClientConfModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9829. </div>
  9830. <div class="p-6 space-y-4">
  9831. <div>
  9832. <label class="block text-xs font-semibold text-slate-300 mb-1.5">WireGuard Client Profile (.conf)</label>
  9833. <textarea id="wg-client-conf-text" rows="11" readonly class="w-full bg-slate-950 border border-slate-800 rounded-xl p-3 text-xs font-mono text-cyan-300 select-all focus:outline-none"></textarea>
  9834. </div>
  9835. <div class="flex justify-between items-center pt-2">
  9836. <span class="text-[11px] text-slate-500">Ready for instant import</span>
  9837. <div class="space-x-2">
  9838. <button onclick="copyWGClientConf()" class="px-4 py-2 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-white border border-slate-700 transition">
  9839. Copy Config
  9840. </button>
  9841. <button onclick="downloadWGClientConf()" class="px-4 py-2 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20 transition">
  9842. Download .conf
  9843. </button>
  9844. </div>
  9845. </div>
  9846. </div>
  9847. </div>
  9848. </div>
  9849. <!-- ================= MODAL: BGP INSTANCE SETTINGS ================= -->
  9850. <div id="bgp-instance-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9851. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  9852. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  9853. <div>
  9854. <h3 class="text-sm font-bold text-white">BGP Instance Settings</h3>
  9855. <p class="text-[11px] text-slate-400">Configure global Autonomous System and route redistribution</p>
  9856. </div>
  9857. <button onclick="closeBgpInstanceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9858. </div>
  9859. <form id="bgp-instance-form" onsubmit="submitBgpInstance(event)" class="p-6 space-y-4 text-xs">
  9860. <div class="flex items-center justify-between p-3 rounded-xl bg-slate-800/60 border border-slate-700/60">
  9861. <div>
  9862. <div class="font-semibold text-white">Enable BGP Routing</div>
  9863. <div class="text-[11px] text-slate-400">Run BIRD 2 daemon for dynamic exterior gateway routing & multi-instance AS peering</div>
  9864. </div>
  9865. <input type="checkbox" id="modal-bgp-enabled" class="w-4 h-4 rounded text-purple-600 focus:ring-purple-500 bg-slate-800 border-slate-600">
  9866. </div>
  9867. <div class="grid grid-cols-2 gap-4">
  9868. <div>
  9869. <label class="block font-semibold text-slate-300 mb-1.5">Autonomous System (AS) *</label>
  9870. <input type="number" id="modal-bgp-as" required min="1" max="4294967295" placeholder="65001" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-purple-500">
  9871. </div>
  9872. <div>
  9873. <label class="block font-semibold text-slate-300 mb-1.5">Router ID</label>
  9874. <input type="text" id="modal-bgp-router-id" placeholder="192.168.131.56" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-purple-500">
  9875. </div>
  9876. </div>
  9877. <div class="grid grid-cols-2 gap-4">
  9878. <div>
  9879. <label class="block font-semibold text-slate-300 mb-1.5">Keepalive Time (s)</label>
  9880. <input type="number" id="modal-bgp-keepalive" value="60" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9881. </div>
  9882. <div>
  9883. <label class="block font-semibold text-slate-300 mb-1.5">Hold Time (s)</label>
  9884. <input type="number" id="modal-bgp-holdtime" value="180" min="3" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  9885. </div>
  9886. </div>
  9887. <div class="border-t border-slate-800 pt-3">
  9888. <label class="block font-semibold text-slate-300 mb-2">Redistribute Routes into BGP</label>
  9889. <div class="space-y-2">
  9890. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  9891. <input type="checkbox" id="modal-bgp-redist-conn" class="rounded text-purple-600 bg-slate-800 border-slate-700">
  9892. <span>Redistribute Connected Subnets</span>
  9893. </label>
  9894. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  9895. <input type="checkbox" id="modal-bgp-redist-static" class="rounded text-purple-600 bg-slate-800 border-slate-700">
  9896. <span>Redistribute Static Routes</span>
  9897. </label>
  9898. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  9899. <input type="checkbox" id="modal-bgp-redist-ospf" class="rounded text-purple-600 bg-slate-800 border-slate-700">
  9900. <span>Redistribute OSPF Routes</span>
  9901. </label>
  9902. </div>
  9903. </div>
  9904. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  9905. <button type="button" onclick="closeBgpInstanceModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  9906. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-purple-600 hover:bg-purple-500 text-white shadow-lg shadow-purple-500/20">Save BGP Settings</button>
  9907. </div>
  9908. </form>
  9909. </div>
  9910. </div>
  9911. <!-- ================= MODAL: BGP CONNECTION ================= -->
  9912. <div id="bgp-peer-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  9913. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-4xl shadow-2xl overflow-hidden animate-in flex flex-col max-h-[90vh]">
  9914. <!-- Header -->
  9915. <div class="px-6 py-3.5 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  9916. <div class="flex items-center gap-2.5">
  9917. <span class="text-cyan-400 text-sm">🔀</span>
  9918. <span class="text-xs font-semibold text-slate-400">BGP Connection &gt;</span>
  9919. <span id="bgp-peer-modal-title" class="text-xs font-bold text-cyan-300 font-mono">New...</span>
  9920. </div>
  9921. <button type="button" onclick="closeBgpPeerModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  9922. </div>
  9923. <!-- Tab Navigation -->
  9924. <div class="flex border-b border-slate-800 bg-slate-950/30 px-6 gap-2">
  9925. <button type="button" id="bgp-peer-tab-btn-general" onclick="switchBgpPeerModalTab('general')" class="px-4 py-2 text-xs font-bold border-b-2 border-cyan-500 text-white transition">General</button>
  9926. <button type="button" id="bgp-peer-tab-btn-filters" onclick="switchBgpPeerModalTab('filters')" class="px-4 py-2 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition">Routing &amp; Filters</button>
  9927. <button type="button" id="bgp-peer-tab-btn-networks" onclick="switchBgpPeerModalTab('networks')" class="px-4 py-2 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition">Advertised Networks</button>
  9928. </div>
  9929. <!-- Body Form -->
  9930. <form id="bgp-peer-form" onsubmit="submitBgpPeer(event)" class="flex-1 overflow-hidden flex flex-col">
  9931. <input type="hidden" id="modal-bgp-peer-id">
  9932. <div class="flex-1 overflow-y-auto p-6 flex gap-6">
  9933. <!-- Main Tabs Area -->
  9934. <div class="flex-1 space-y-4 text-xs">
  9935. <!-- TAB 1: GENERAL -->
  9936. <div id="bgp-peer-tab-general" class="space-y-3.5">
  9937. <div class="grid grid-cols-3 items-center gap-3">
  9938. <label class="text-slate-400 font-medium">Enabled</label>
  9939. <div class="col-span-2">
  9940. <input type="checkbox" id="modal-bgp-peer-enabled" checked class="w-4 h-4 rounded text-cyan-500 bg-slate-800 border-slate-700">
  9941. </div>
  9942. </div>
  9943. <div class="grid grid-cols-3 items-center gap-3">
  9944. <label class="text-slate-400 font-medium">Connection Name *</label>
  9945. <div class="col-span-2">
  9946. <input type="text" id="modal-bgp-peer-name" required placeholder="e.g. Primcast, ebgp-JB172" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  9947. </div>
  9948. </div>
  9949. <div class="grid grid-cols-3 items-center gap-3">
  9950. <label class="text-slate-400 font-medium">Comment</label>
  9951. <div class="col-span-2">
  9952. <input type="text" id="modal-bgp-peer-comment" placeholder="Optional notes / description" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white">
  9953. </div>
  9954. </div>
  9955. <hr class="border-slate-800/80 my-2">
  9956. <div class="grid grid-cols-3 items-center gap-3">
  9957. <label class="text-slate-400 font-medium">Remote AS *</label>
  9958. <div class="col-span-2">
  9959. <input type="number" id="modal-bgp-peer-remote-as" required min="1" max="4294967295" placeholder="e.g. 65555" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  9960. </div>
  9961. </div>
  9962. <div class="grid grid-cols-3 items-center gap-3">
  9963. <label class="text-slate-400 font-medium">Remote Address *</label>
  9964. <div class="col-span-2">
  9965. <input type="text" id="modal-bgp-peer-remote-addr" required placeholder="e.g. 192.168.129.225" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  9966. </div>
  9967. </div>
  9968. <div class="grid grid-cols-3 items-center gap-3">
  9969. <label class="text-slate-400 font-medium">Remote Port</label>
  9970. <div class="col-span-2">
  9971. <input type="number" id="modal-bgp-peer-remote-port" placeholder="179 (default)" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  9972. </div>
  9973. </div>
  9974. <hr class="border-slate-800/80 my-2">
  9975. <div class="grid grid-cols-3 items-center gap-3">
  9976. <label class="text-slate-400 font-medium">Local AS (Override)</label>
  9977. <div class="col-span-2">
  9978. <input type="number" id="modal-bgp-peer-local-as" min="1" max="4294967295" placeholder="Leave blank to use Global Router AS" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  9979. <p class="text-[10px] text-slate-500 mt-0.5">Per-peer Local AS override. If blank, uses global BGP Instance AS.</p>
  9980. </div>
  9981. </div>
  9982. <div class="grid grid-cols-3 items-center gap-3">
  9983. <label class="text-slate-400 font-medium">Local Address &amp; Port</label>
  9984. <div class="col-span-2 grid grid-cols-3 gap-2">
  9985. <div class="col-span-2">
  9986. <input type="text" id="modal-bgp-peer-local-addr" placeholder="Optional bind IP (e.g. 192.168.129.226)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  9987. </div>
  9988. <div>
  9989. <input type="number" id="modal-bgp-peer-local-port" placeholder="Port" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  9990. </div>
  9991. </div>
  9992. </div>
  9993. <div class="grid grid-cols-3 items-center gap-3">
  9994. <label class="text-slate-400 font-medium">Local Role</label>
  9995. <div class="col-span-2">
  9996. <select id="modal-bgp-peer-local-role" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  9997. <option value="ebgp">ebgp (External BGP - Default)</option>
  9998. <option value="ibgp">ibgp (Internal BGP)</option>
  9999. </select>
  10000. </div>
  10001. </div>
  10002. <div class="grid grid-cols-3 items-center gap-3">
  10003. <label class="text-slate-400 font-medium">TCP MD5 Secret</label>
  10004. <div class="col-span-2">
  10005. <input type="password" id="modal-bgp-peer-password" placeholder="BGP authentication password (optional)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10006. </div>
  10007. </div>
  10008. <hr class="border-slate-800/80 my-2">
  10009. <div class="grid grid-cols-3 items-start gap-3">
  10010. <label class="text-slate-400 font-medium pt-1">Session Flags</label>
  10011. <div class="col-span-2 space-y-2 text-slate-300">
  10012. <label class="flex items-center gap-2 cursor-pointer">
  10013. <input type="checkbox" id="modal-bgp-peer-multihop" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
  10014. <span>eBGP Multi-hop (allow peers across multiple routing hops)</span>
  10015. </label>
  10016. <label class="flex items-center gap-2 cursor-pointer">
  10017. <input type="checkbox" id="modal-bgp-peer-passive" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
  10018. <span>Passive Mode (Listen only, do not initiate connection actively)</span>
  10019. </label>
  10020. <label class="flex items-center gap-2 cursor-pointer">
  10021. <input type="checkbox" id="modal-bgp-peer-nexthop-self" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
  10022. <span>Next-Hop Self (Rewrite next-hop to local router address)</span>
  10023. </label>
  10024. </div>
  10025. </div>
  10026. <hr class="border-slate-800/80 my-2">
  10027. <div class="grid grid-cols-3 items-center gap-3">
  10028. <label class="text-slate-400 font-medium">Timers (Seconds)</label>
  10029. <div class="col-span-2 grid grid-cols-2 gap-3">
  10030. <div>
  10031. <span class="text-[10px] text-slate-500 block mb-0.5">Hold Time</span>
  10032. <input type="text" id="modal-bgp-peer-holdtime" placeholder="30 (or 00:00:30)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1 text-white font-mono">
  10033. </div>
  10034. <div>
  10035. <span class="text-[10px] text-slate-500 block mb-0.5">Keepalive Time</span>
  10036. <input type="text" id="modal-bgp-peer-keepalive" placeholder="10 (or 00:00:10)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1 text-white font-mono">
  10037. </div>
  10038. </div>
  10039. </div>
  10040. </div>
  10041. <!-- TAB 2: ROUTING & FILTERS -->
  10042. <div id="bgp-peer-tab-filters" class="space-y-4 hidden">
  10043. <datalist id="filter-chains-list"></datalist>
  10044. <div class="p-3 bg-slate-950/40 border border-slate-800 rounded-xl space-y-3">
  10045. <div class="font-bold text-white text-xs">Route Policy Filters</div>
  10046. <div class="grid grid-cols-3 items-center gap-3">
  10047. <label class="text-slate-400 font-medium">In Filter</label>
  10048. <div class="col-span-2">
  10049. <input type="text" list="filter-chains-list" id="modal-bgp-peer-in-filter" placeholder="e.g. Internal, awmn" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10050. <p class="text-[10px] text-slate-500 mt-0.5">Routing filter chain applied to routes received from this peer</p>
  10051. </div>
  10052. </div>
  10053. <div class="grid grid-cols-3 items-center gap-3">
  10054. <label class="text-slate-400 font-medium">Out Filter</label>
  10055. <div class="col-span-2">
  10056. <input type="text" list="filter-chains-list" id="modal-bgp-peer-out-filter" placeholder="e.g. Internal, awmn" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10057. <p class="text-[10px] text-slate-500 mt-0.5">Routing filter chain applied to routes exported to this peer</p>
  10058. </div>
  10059. </div>
  10060. </div>
  10061. <div class="p-3 bg-slate-950/40 border border-slate-800 rounded-xl space-y-3">
  10062. <div class="font-bold text-white text-xs">Route Redistribution into BGP</div>
  10063. <p class="text-[11px] text-slate-400">Select which router routing sources should be announced to this peer session:</p>
  10064. <div class="space-y-2.5 text-slate-300">
  10065. <label class="flex items-center gap-2.5 cursor-pointer">
  10066. <input type="checkbox" id="modal-bgp-peer-redist-conn" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
  10067. <div>
  10068. <span class="font-medium text-white">Redistribute Connected Subnets</span>
  10069. <p class="text-[10px] text-slate-500">Announces directly attached interface networks (RTS_DEVICE)</p>
  10070. </div>
  10071. </label>
  10072. <label class="flex items-center gap-2.5 cursor-pointer">
  10073. <input type="checkbox" id="modal-bgp-peer-redist-static" checked class="rounded text-cyan-500 bg-slate-800 border-slate-700">
  10074. <div>
  10075. <span class="font-medium text-white">Redistribute Static Routes</span>
  10076. <p class="text-[10px] text-slate-500">Announces kernel static routes and blackhole discard routes (distance 255)</p>
  10077. </div>
  10078. </label>
  10079. <label class="flex items-center gap-2.5 cursor-pointer">
  10080. <input type="checkbox" id="modal-bgp-peer-redist-ospf" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
  10081. <div>
  10082. <span class="font-medium text-white">Redistribute OSPF Routes</span>
  10083. <p class="text-[10px] text-slate-500">Announces routes learned from OSPF (RTS_OSPF)</p>
  10084. </div>
  10085. </label>
  10086. <label class="flex items-center gap-2.5 cursor-pointer">
  10087. <input type="checkbox" id="modal-bgp-peer-default-orig" class="rounded text-cyan-500 bg-slate-800 border-slate-700">
  10088. <div>
  10089. <span class="font-medium text-white">Default Originate (0.0.0.0/0)</span>
  10090. <p class="text-[10px] text-slate-500">Always generate and announce a default gateway route to this peer</p>
  10091. </div>
  10092. </label>
  10093. </div>
  10094. </div>
  10095. </div>
  10096. <!-- TAB 3: ADVERTISED NETWORKS -->
  10097. <div id="bgp-peer-tab-networks" class="space-y-4 hidden">
  10098. <div class="p-3 bg-purple-950/20 border border-purple-800/40 rounded-xl flex items-start gap-3">
  10099. <span class="text-purple-400 text-base">🛡️</span>
  10100. <div class="text-[11px] text-slate-300">
  10101. <div class="font-semibold text-purple-300 mb-0.5">Blackhole Discard (Distance 255) Protection</div>
  10102. Advertised networks listed here are announced directly to this peer. The router automatically installs a blackhole discard route with metric 255 in the Linux kernel routing table so your BGP daemon can reliably originate the prefix even if physical interfaces flap or subnets have unassigned addresses, eliminating packet bouncing and routing loops.
  10103. </div>
  10104. </div>
  10105. <div>
  10106. <div class="flex items-center justify-between mb-2">
  10107. <div>
  10108. <label class="text-slate-200 font-bold block">Output Networks</label>
  10109. <p class="text-[10px] text-slate-400">Prefixes/subnets announced to this peer connection</p>
  10110. </div>
  10111. <button type="button" onclick="addOutputNetworkRow('')" class="text-xs px-2.5 py-1 bg-cyan-600/30 hover:bg-cyan-600 text-cyan-300 hover:text-white rounded border border-cyan-500/40 font-semibold transition flex items-center gap-1">
  10112. <span>+</span>
  10113. <span>Add Network</span>
  10114. </button>
  10115. </div>
  10116. <div id="modal-bgp-output-networks-container" class="space-y-2 p-3 bg-slate-950/40 border border-slate-800 rounded-xl min-h-[80px]">
  10117. <!-- Dynamic rows go here -->
  10118. </div>
  10119. </div>
  10120. </div>
  10121. </div>
  10122. <!-- Right Sidebar (Actions) -->
  10123. <div class="w-36 border-l border-slate-800 pl-4 space-y-2 font-sans">
  10124. <div class="text-[10px] uppercase font-bold text-slate-500 tracking-wider mb-2">Actions</div>
  10125. <button type="button" onclick="copyCurrentBgpPeer()" class="w-full text-left text-xs px-2.5 py-1.5 rounded-lg text-slate-300 hover:text-white hover:bg-slate-800 transition flex items-center gap-2">
  10126. <span>📋</span>
  10127. <span>Copy</span>
  10128. </button>
  10129. <button type="button" id="modal-btn-remove-peer" onclick="removeCurrentBgpPeer()" class="w-full text-left text-xs px-2.5 py-1.5 rounded-lg text-rose-400 hover:text-rose-300 hover:bg-rose-950/40 transition flex items-center gap-2">
  10130. <span>✕</span>
  10131. <span>Remove</span>
  10132. </button>
  10133. </div>
  10134. </div>
  10135. <!-- Footer Buttons -->
  10136. <div class="px-6 py-3.5 border-t border-slate-800 bg-slate-950/60 flex items-center justify-end space-x-2.5">
  10137. <button type="button" onclick="closeBgpPeerModal()" class="px-4 py-1.5 rounded-lg text-xs font-semibold text-slate-400 hover:bg-slate-800 hover:text-white transition">Cancel</button>
  10138. <button type="button" onclick="applyBgpPeerModal(event)" class="px-4 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-cyan-300 border border-slate-700 transition">Apply</button>
  10139. <button type="submit" class="px-5 py-1.5 rounded-lg text-xs font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20 transition">OK</button>
  10140. </div>
  10141. </form>
  10142. </div>
  10143. </div>
  10144. <!-- ================= MODAL: BGP NETWORK ================= -->
  10145. <div id="bgp-network-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10146. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  10147. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  10148. <h3 id="bgp-network-modal-title" class="text-sm font-bold text-white">Advertise BGP Network</h3>
  10149. <button onclick="closeBgpNetworkModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10150. </div>
  10151. <form id="bgp-network-form" onsubmit="submitBgpNetwork(event)" class="p-6 space-y-4 text-xs">
  10152. <input type="hidden" id="modal-bgp-net-id">
  10153. <div class="p-2.5 rounded-lg bg-purple-950/30 border border-purple-800/40 text-[11px] text-purple-300">
  10154. <span class="font-bold">🛡️ Discard Route:</span> Advertised prefixes automatically maintain a kernel blackhole route with metric 255 so BGP originates the prefix reliably without routing loops.
  10155. </div>
  10156. <div>
  10157. <label class="block font-semibold text-slate-300 mb-1.5">Network Prefix (CIDR) *</label>
  10158. <input type="text" id="modal-bgp-net-cidr" required placeholder="10.200.0.0/24" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-purple-500">
  10159. </div>
  10160. <div>
  10161. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  10162. <input type="text" id="modal-bgp-net-comment" placeholder="Public IP block prefix" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  10163. </div>
  10164. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  10165. <input type="checkbox" id="modal-bgp-net-disabled" class="rounded text-purple-600 bg-slate-800 border-slate-700">
  10166. <span>Disabled</span>
  10167. </label>
  10168. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  10169. <button type="button" onclick="closeBgpNetworkModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  10170. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-purple-600 hover:bg-purple-500 text-white shadow-lg shadow-purple-500/20">Save Network</button>
  10171. </div>
  10172. </form>
  10173. </div>
  10174. </div>
  10175. <!-- ================= MODAL: OSPF INSTANCE ================= -->
  10176. <div id="ospf-instance-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10177. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-2xl shadow-2xl overflow-hidden animate-in flex flex-col max-h-[90vh]">
  10178. <div class="px-6 py-3.5 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10179. <div class="flex items-center gap-2.5">
  10180. <span class="text-sky-400 text-sm">🔀</span>
  10181. <span class="text-xs font-semibold text-slate-400">OSPF Instance &gt;</span>
  10182. <span id="ospf-instance-modal-title" class="text-xs font-bold text-sky-300 font-mono">New...</span>
  10183. </div>
  10184. <button type="button" onclick="closeOspfInstanceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10185. </div>
  10186. <div class="flex border-b border-slate-800 bg-slate-950/30 px-6 gap-2">
  10187. <button type="button" class="px-4 py-2 text-xs font-bold border-b-2 border-sky-500 text-white transition">General</button>
  10188. </div>
  10189. <form id="ospf-instance-form" onsubmit="submitOspfInstance(event)" class="flex-1 overflow-hidden flex flex-col">
  10190. <input type="hidden" id="modal-ospf-inst-id">
  10191. <div class="flex-1 overflow-y-auto p-6 flex gap-6">
  10192. <div class="flex-1 space-y-3.5 text-xs">
  10193. <div class="grid grid-cols-3 items-center gap-3">
  10194. <label class="text-slate-400 font-medium">Enabled</label>
  10195. <div class="col-span-2">
  10196. <input type="checkbox" id="modal-ospf-inst-enabled" checked class="w-4 h-4 rounded text-sky-500 bg-slate-800 border-slate-700">
  10197. </div>
  10198. </div>
  10199. <div class="grid grid-cols-3 items-center gap-3">
  10200. <label class="text-slate-400 font-medium">Comment</label>
  10201. <div class="col-span-2">
  10202. <input type="text" id="modal-ospf-inst-comment" placeholder="Optional comment" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white">
  10203. </div>
  10204. </div>
  10205. <div class="grid grid-cols-3 items-center gap-3">
  10206. <label class="text-slate-400 font-medium">Name *</label>
  10207. <div class="col-span-2">
  10208. <input type="text" id="modal-ospf-inst-name" required value="Default" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10209. </div>
  10210. </div>
  10211. <div class="grid grid-cols-3 items-center gap-3">
  10212. <label class="text-slate-400 font-medium">Version</label>
  10213. <div class="col-span-2">
  10214. <select id="modal-ospf-inst-version" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10215. <option value="2">2 (OSPFv2 IPv4)</option>
  10216. <option value="3">3 (OSPFv3 IPv6)</option>
  10217. </select>
  10218. </div>
  10219. </div>
  10220. <div class="grid grid-cols-3 items-center gap-3">
  10221. <label class="text-slate-400 font-medium">VRF</label>
  10222. <div class="col-span-2">
  10223. <input type="text" id="modal-ospf-inst-vrf" value="main" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10224. </div>
  10225. </div>
  10226. <div class="grid grid-cols-3 items-center gap-3">
  10227. <label class="text-slate-400 font-medium">Router ID</label>
  10228. <div class="col-span-2">
  10229. <input type="text" id="modal-ospf-inst-router-id" placeholder="10.42.62.254" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10230. </div>
  10231. </div>
  10232. <div class="grid grid-cols-3 items-center gap-3">
  10233. <label class="text-slate-400 font-medium">Routing Table</label>
  10234. <div class="col-span-2">
  10235. <input type="text" id="modal-ospf-inst-routing-table" value="main" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10236. </div>
  10237. </div>
  10238. <div class="grid grid-cols-3 items-start gap-3 pt-2 border-t border-slate-800">
  10239. <label class="text-slate-400 font-medium pt-1">Redistribute</label>
  10240. <div class="col-span-2 space-y-2">
  10241. <label class="flex items-center gap-2 cursor-pointer text-slate-300">
  10242. <input type="checkbox" id="modal-ospf-inst-redist-conn" class="rounded text-sky-500 bg-slate-800 border-slate-700">
  10243. <span>connected</span>
  10244. </label>
  10245. <label class="flex items-center gap-2 cursor-pointer text-slate-300">
  10246. <input type="checkbox" id="modal-ospf-inst-redist-static" checked class="rounded text-sky-500 bg-slate-800 border-slate-700">
  10247. <span>static</span>
  10248. </label>
  10249. <label class="flex items-center gap-2 cursor-pointer text-slate-300">
  10250. <input type="checkbox" id="modal-ospf-inst-redist-bgp" class="rounded text-sky-500 bg-slate-800 border-slate-700">
  10251. <span>bgp</span>
  10252. </label>
  10253. <label class="flex items-center gap-2 cursor-pointer text-slate-300">
  10254. <input type="checkbox" id="modal-ospf-inst-redist-default" onchange="toggleOspfDefaultModeUI()" class="rounded text-sky-500 bg-slate-800 border-slate-700">
  10255. <span>default (0.0.0.0/0)</span>
  10256. </label>
  10257. </div>
  10258. </div>
  10259. <div id="row-ospf-inst-originate-default" class="grid grid-cols-3 items-center gap-3">
  10260. <label class="text-slate-400 font-medium">Originate Default</label>
  10261. <div class="col-span-2">
  10262. <select id="modal-ospf-inst-originate-default" onchange="onOspfOriginateDefaultChange()" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white">
  10263. <option value="never">never (Disabled)</option>
  10264. <option value="if-installed">if-installed (Only if default route present)</option>
  10265. <option value="always">always (Always advertise 0.0.0.0/0)</option>
  10266. </select>
  10267. </div>
  10268. </div>
  10269. <div class="grid grid-cols-3 items-center gap-3">
  10270. <label class="text-slate-400 font-medium">Default Metric</label>
  10271. <div class="col-span-2">
  10272. <input type="number" id="modal-ospf-inst-default-metric" placeholder="10" min="0" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10273. </div>
  10274. </div>
  10275. </div>
  10276. <!-- Sidebar Actions -->
  10277. <div class="w-28 flex flex-col gap-2 border-l border-slate-800 pl-4 select-none">
  10278. <button type="button" onclick="copyOspfInstance()" class="text-xs text-left px-2 py-1.5 rounded hover:bg-slate-800 text-slate-300">📄 Copy</button>
  10279. <button type="button" onclick="deleteCurrentOspfInstance()" id="btn-ospf-inst-modal-remove" class="text-xs text-left px-2 py-1.5 rounded hover:bg-rose-950/40 text-rose-400">✕ Remove</button>
  10280. </div>
  10281. </div>
  10282. <!-- Footer -->
  10283. <div class="px-6 py-3 border-t border-slate-800 bg-slate-950/40 flex justify-end gap-2.5">
  10284. <button type="button" onclick="closeOspfInstanceModal()" class="px-4 py-1.5 rounded-lg text-xs font-semibold text-slate-400 hover:text-white hover:bg-slate-800 transition">Cancel</button>
  10285. <button type="button" onclick="applyOspfInstanceForm()" class="px-4 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-sky-300 border border-slate-700 transition">Apply</button>
  10286. <button type="submit" class="px-5 py-1.5 rounded-lg text-xs font-bold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-600/30 transition">OK</button>
  10287. </div>
  10288. </form>
  10289. </div>
  10290. </div>
  10291. <!-- ================= MODAL: OSPF AREA ================= -->
  10292. <div id="ospf-area-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10293. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-2xl shadow-2xl overflow-hidden animate-in flex flex-col max-h-[90vh]">
  10294. <div class="px-6 py-3.5 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10295. <div class="flex items-center gap-2.5">
  10296. <span class="text-sky-400 text-sm">🔀</span>
  10297. <span class="text-xs font-semibold text-slate-400">OSPF Area &gt;</span>
  10298. <span id="ospf-area-modal-title" class="text-xs font-bold text-sky-300 font-mono">New...</span>
  10299. </div>
  10300. <button type="button" onclick="closeOspfAreaModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10301. </div>
  10302. <div class="flex border-b border-slate-800 bg-slate-950/30 px-6 gap-2">
  10303. <button type="button" class="px-4 py-2 text-xs font-bold border-b-2 border-sky-500 text-white transition">General</button>
  10304. </div>
  10305. <form id="ospf-area-form" onsubmit="submitOspfArea(event)" class="flex-1 overflow-hidden flex flex-col">
  10306. <input type="hidden" id="modal-ospf-area-id">
  10307. <div class="flex-1 overflow-y-auto p-6 flex gap-6">
  10308. <div class="flex-1 space-y-3.5 text-xs">
  10309. <div class="grid grid-cols-3 items-center gap-3">
  10310. <label class="text-slate-400 font-medium">Enabled</label>
  10311. <div class="col-span-2">
  10312. <input type="checkbox" id="modal-ospf-area-enabled" checked class="w-4 h-4 rounded text-sky-500 bg-slate-800 border-slate-700">
  10313. </div>
  10314. </div>
  10315. <div class="grid grid-cols-3 items-center gap-3">
  10316. <label class="text-slate-400 font-medium">Comment</label>
  10317. <div class="col-span-2">
  10318. <input type="text" id="modal-ospf-area-comment" placeholder="Optional comment" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white">
  10319. </div>
  10320. </div>
  10321. <div class="grid grid-cols-3 items-center gap-3">
  10322. <label class="text-slate-400 font-medium">Name *</label>
  10323. <div class="col-span-2">
  10324. <input type="text" id="modal-ospf-area-name" required value="Default" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10325. </div>
  10326. </div>
  10327. <div class="grid grid-cols-3 items-center gap-3">
  10328. <label class="text-slate-400 font-medium">Instance</label>
  10329. <div class="col-span-2">
  10330. <select id="modal-ospf-area-instance" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10331. <option value="Default">Default</option>
  10332. </select>
  10333. </div>
  10334. </div>
  10335. <div class="grid grid-cols-3 items-center gap-3">
  10336. <label class="text-slate-400 font-medium">Area ID *</label>
  10337. <div class="col-span-2">
  10338. <input type="text" id="modal-ospf-area-areaid" required value="0.0.0.0" placeholder="0.0.0.0" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10339. </div>
  10340. </div>
  10341. <div class="grid grid-cols-3 items-center gap-3">
  10342. <label class="text-slate-400 font-medium">Type</label>
  10343. <div class="col-span-2">
  10344. <select id="modal-ospf-area-type" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10345. <option value="default">default</option>
  10346. <option value="stub">stub</option>
  10347. <option value="nssa">nssa</option>
  10348. </select>
  10349. </div>
  10350. </div>
  10351. <div class="grid grid-cols-3 items-center gap-3">
  10352. <label class="text-slate-400 font-medium">Default Cost</label>
  10353. <div class="col-span-2">
  10354. <input type="number" id="modal-ospf-area-default-cost" min="1" max="65535" placeholder="1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10355. </div>
  10356. </div>
  10357. <div class="grid grid-cols-3 items-center gap-3">
  10358. <label class="text-slate-400 font-medium">No Summaries</label>
  10359. <div class="col-span-2">
  10360. <input type="checkbox" id="modal-ospf-area-no-summaries" class="rounded text-sky-500 bg-slate-800 border-slate-700">
  10361. </div>
  10362. </div>
  10363. </div>
  10364. <!-- Sidebar Actions -->
  10365. <div class="w-28 flex flex-col gap-2 border-l border-slate-800 pl-4 select-none">
  10366. <button type="button" onclick="copyOspfArea()" class="text-xs text-left px-2 py-1.5 rounded hover:bg-slate-800 text-slate-300">📄 Copy</button>
  10367. <button type="button" onclick="deleteCurrentOspfArea()" id="btn-ospf-area-modal-remove" class="text-xs text-left px-2 py-1.5 rounded hover:bg-rose-950/40 text-rose-400">✕ Remove</button>
  10368. </div>
  10369. </div>
  10370. <!-- Footer -->
  10371. <div class="px-6 py-3 border-t border-slate-800 bg-slate-950/40 flex justify-end gap-2.5">
  10372. <button type="button" onclick="closeOspfAreaModal()" class="px-4 py-1.5 rounded-lg text-xs font-semibold text-slate-400 hover:text-white hover:bg-slate-800 transition">Cancel</button>
  10373. <button type="button" onclick="applyOspfAreaForm()" class="px-4 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-sky-300 border border-slate-700 transition">Apply</button>
  10374. <button type="submit" class="px-5 py-1.5 rounded-lg text-xs font-bold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-600/30 transition">OK</button>
  10375. </div>
  10376. </form>
  10377. </div>
  10378. </div>
  10379. <!-- ================= MODAL: OSPF AREA RANGE ================= -->
  10380. <div id="ospf-area-range-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10381. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-2xl shadow-2xl overflow-hidden animate-in flex flex-col max-h-[90vh]">
  10382. <div class="px-6 py-3.5 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10383. <div class="flex items-center gap-2.5">
  10384. <span class="text-sky-400 text-sm">🔀</span>
  10385. <span class="text-xs font-semibold text-slate-400">Area Range &gt;</span>
  10386. <span id="ospf-area-range-modal-title" class="text-xs font-bold text-sky-300 font-mono">New...</span>
  10387. </div>
  10388. <button type="button" onclick="closeOspfAreaRangeModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10389. </div>
  10390. <div class="flex border-b border-slate-800 bg-slate-950/30 px-6 gap-2">
  10391. <button type="button" class="px-4 py-2 text-xs font-bold border-b-2 border-sky-500 text-white transition">General</button>
  10392. </div>
  10393. <form id="ospf-area-range-form" onsubmit="submitOspfAreaRange(event)" class="flex-1 overflow-hidden flex flex-col">
  10394. <input type="hidden" id="modal-ospf-range-id">
  10395. <div class="flex-1 overflow-y-auto p-6 flex gap-6">
  10396. <div class="flex-1 space-y-3.5 text-xs">
  10397. <div class="grid grid-cols-3 items-center gap-3">
  10398. <label class="text-slate-400 font-medium">Enabled</label>
  10399. <div class="col-span-2">
  10400. <input type="checkbox" id="modal-ospf-range-enabled" checked class="w-4 h-4 rounded text-sky-500 bg-slate-800 border-slate-700">
  10401. </div>
  10402. </div>
  10403. <div class="grid grid-cols-3 items-center gap-3">
  10404. <label class="text-slate-400 font-medium">Comment</label>
  10405. <div class="col-span-2">
  10406. <input type="text" id="modal-ospf-range-comment" placeholder="Optional comment" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white">
  10407. </div>
  10408. </div>
  10409. <div class="grid grid-cols-3 items-center gap-3">
  10410. <label class="text-slate-400 font-medium">Area *</label>
  10411. <div class="col-span-2">
  10412. <select id="modal-ospf-range-area" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10413. <option value="Default">Default</option>
  10414. </select>
  10415. </div>
  10416. </div>
  10417. <div class="grid grid-cols-3 items-center gap-3">
  10418. <label class="text-slate-400 font-medium">Prefix *</label>
  10419. <div class="col-span-2">
  10420. <input type="text" id="modal-ospf-range-prefix" required placeholder="10.42.0.0/16" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10421. </div>
  10422. </div>
  10423. <div class="grid grid-cols-3 items-center gap-3">
  10424. <label class="text-slate-400 font-medium">Cost</label>
  10425. <div class="col-span-2">
  10426. <input type="number" id="modal-ospf-range-cost" min="0" max="65535" placeholder="1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10427. </div>
  10428. </div>
  10429. <div class="grid grid-cols-3 items-center gap-3">
  10430. <label class="text-slate-400 font-medium">Advertise</label>
  10431. <div class="col-span-2">
  10432. <input type="checkbox" id="modal-ospf-range-advertise" checked class="rounded text-sky-500 bg-slate-800 border-slate-700">
  10433. </div>
  10434. </div>
  10435. </div>
  10436. <!-- Sidebar Actions -->
  10437. <div class="w-28 flex flex-col gap-2 border-l border-slate-800 pl-4 select-none">
  10438. <button type="button" onclick="deleteCurrentOspfAreaRange()" id="btn-ospf-range-modal-remove" class="text-xs text-left px-2 py-1.5 rounded hover:bg-rose-950/40 text-rose-400">✕ Remove</button>
  10439. </div>
  10440. </div>
  10441. <!-- Footer -->
  10442. <div class="px-6 py-3 border-t border-slate-800 bg-slate-950/40 flex justify-end gap-2.5">
  10443. <button type="button" onclick="closeOspfAreaRangeModal()" class="px-4 py-1.5 rounded-lg text-xs font-semibold text-slate-400 hover:text-white hover:bg-slate-800 transition">Cancel</button>
  10444. <button type="button" onclick="applyOspfAreaRangeForm()" class="px-4 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-sky-300 border border-slate-700 transition">Apply</button>
  10445. <button type="submit" class="px-5 py-1.5 rounded-lg text-xs font-bold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-600/30 transition">OK</button>
  10446. </div>
  10447. </form>
  10448. </div>
  10449. </div>
  10450. <!-- ================= MODAL: OSPF INTERFACE TEMPLATE ================= -->
  10451. <div id="ospf-template-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10452. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-3xl shadow-2xl overflow-hidden animate-in flex flex-col max-h-[90vh]">
  10453. <div class="px-6 py-3.5 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10454. <div class="flex items-center gap-2.5">
  10455. <span class="text-sky-400 text-sm">🔀</span>
  10456. <span class="text-xs font-semibold text-slate-400">Interface Template &gt;</span>
  10457. <span id="ospf-template-modal-title" class="text-xs font-bold text-sky-300 font-mono">New...</span>
  10458. </div>
  10459. <button type="button" onclick="closeOspfTemplateModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10460. </div>
  10461. <div class="flex border-b border-slate-800 bg-slate-950/30 px-6 gap-2">
  10462. <button type="button" id="ospf-tmpl-tab-btn-general" onclick="switchOspfTemplateTab('general')" class="px-4 py-2 text-xs font-bold border-b-2 border-sky-500 text-white transition">General</button>
  10463. <button type="button" id="ospf-tmpl-tab-btn-auth" onclick="switchOspfTemplateTab('auth')" class="px-4 py-2 text-xs font-bold border-b-2 border-transparent text-slate-400 hover:text-slate-200 transition">Authentication</button>
  10464. </div>
  10465. <form id="ospf-template-form" onsubmit="submitOspfTemplate(event)" class="flex-1 overflow-hidden flex flex-col">
  10466. <input type="hidden" id="modal-ospf-tmpl-id">
  10467. <div class="flex-1 overflow-y-auto p-6 flex gap-6">
  10468. <div class="flex-1 space-y-3.5 text-xs">
  10469. <!-- Tab General -->
  10470. <div id="ospf-tmpl-tab-general" class="space-y-3.5">
  10471. <div class="grid grid-cols-3 items-center gap-3">
  10472. <label class="text-slate-400 font-medium">Enabled</label>
  10473. <div class="col-span-2">
  10474. <input type="checkbox" id="modal-ospf-tmpl-enabled" checked class="w-4 h-4 rounded text-sky-500 bg-slate-800 border-slate-700">
  10475. </div>
  10476. </div>
  10477. <div class="grid grid-cols-3 items-center gap-3">
  10478. <label class="text-slate-400 font-medium">Comment</label>
  10479. <div class="col-span-2">
  10480. <input type="text" id="modal-ospf-tmpl-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white">
  10481. </div>
  10482. </div>
  10483. <div class="grid grid-cols-3 items-center gap-3">
  10484. <label class="text-slate-400 font-medium">Interfaces</label>
  10485. <div class="col-span-2">
  10486. <input type="text" id="modal-ospf-tmpl-interfaces" placeholder="e.g. ether1, wlan1 or all" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10487. <p class="text-[10px] text-slate-500 mt-0.5">Physical/VLAN/tunnel interfaces or leave empty to match by networks</p>
  10488. </div>
  10489. </div>
  10490. <div class="grid grid-cols-3 items-center gap-3">
  10491. <label class="text-slate-400 font-medium">Area *</label>
  10492. <div class="col-span-2">
  10493. <select id="modal-ospf-tmpl-area" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10494. <option value="Default">Default</option>
  10495. </select>
  10496. </div>
  10497. </div>
  10498. <div class="grid grid-cols-3 items-center gap-3">
  10499. <label class="text-slate-400 font-medium">Networks</label>
  10500. <div class="col-span-2">
  10501. <input type="text" id="modal-ospf-tmpl-networks" placeholder="e.g. 10.42.36.244/30" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10502. <p class="text-[10px] text-slate-500 mt-0.5">Subnet CIDR to match interfaces against</p>
  10503. </div>
  10504. </div>
  10505. <div class="grid grid-cols-3 items-center gap-3">
  10506. <label class="text-slate-400 font-medium">Network Type</label>
  10507. <div class="col-span-2">
  10508. <select id="modal-ospf-tmpl-net-type" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10509. <option value="broadcast">broadcast</option>
  10510. <option value="ptp">ptp (point-to-point)</option>
  10511. <option value="nbma">nbma</option>
  10512. <option value="point-to-multipoint">point-to-multipoint</option>
  10513. </select>
  10514. </div>
  10515. </div>
  10516. <div class="grid grid-cols-3 items-center gap-3">
  10517. <label class="text-slate-400 font-medium">Cost</label>
  10518. <div class="col-span-2">
  10519. <input type="number" id="modal-ospf-tmpl-cost" value="1" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10520. </div>
  10521. </div>
  10522. <div class="grid grid-cols-3 items-center gap-3">
  10523. <label class="text-slate-400 font-medium">Priority</label>
  10524. <div class="col-span-2">
  10525. <input type="number" id="modal-ospf-tmpl-priority" value="128" min="0" max="255" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10526. </div>
  10527. </div>
  10528. <div class="grid grid-cols-3 items-center gap-3">
  10529. <label class="text-slate-400 font-medium">Passive</label>
  10530. <div class="col-span-2">
  10531. <input type="checkbox" id="modal-ospf-tmpl-passive" class="rounded text-sky-500 bg-slate-800 border-slate-700">
  10532. <span class="text-[11px] text-slate-400 ml-1.5">Do not send/listen for OSPF hello packets</span>
  10533. </div>
  10534. </div>
  10535. <div class="grid grid-cols-3 items-center gap-3">
  10536. <label class="text-slate-400 font-medium">Timers (Hello / Dead)</label>
  10537. <div class="col-span-2 grid grid-cols-2 gap-2">
  10538. <input type="number" id="modal-ospf-tmpl-hello" value="10" min="1" max="65535" placeholder="Hello 10s" class="bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10539. <input type="number" id="modal-ospf-tmpl-dead" value="40" min="1" max="65535" placeholder="Dead 40s" class="bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10540. </div>
  10541. </div>
  10542. </div>
  10543. <!-- Tab Authentication -->
  10544. <div id="ospf-tmpl-tab-auth" class="hidden space-y-3.5">
  10545. <div class="grid grid-cols-3 items-center gap-3">
  10546. <label class="text-slate-400 font-medium">Auth</label>
  10547. <div class="col-span-2">
  10548. <select id="modal-ospf-tmpl-auth" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10549. <option value="none">none</option>
  10550. <option value="simple">simple</option>
  10551. <option value="md5">md5</option>
  10552. </select>
  10553. </div>
  10554. </div>
  10555. <div class="grid grid-cols-3 items-center gap-3">
  10556. <label class="text-slate-400 font-medium">Auth Key</label>
  10557. <div class="col-span-2">
  10558. <input type="password" id="modal-ospf-tmpl-auth-key" placeholder="••••••••" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10559. </div>
  10560. </div>
  10561. </div>
  10562. </div>
  10563. <!-- Sidebar Actions -->
  10564. <div class="w-28 flex flex-col gap-2 border-l border-slate-800 pl-4 select-none">
  10565. <button type="button" onclick="copyOspfTemplate()" class="text-xs text-left px-2 py-1.5 rounded hover:bg-slate-800 text-slate-300">📄 Copy</button>
  10566. <button type="button" onclick="deleteCurrentOspfTemplate()" id="btn-ospf-tmpl-modal-remove" class="text-xs text-left px-2 py-1.5 rounded hover:bg-rose-950/40 text-rose-400">✕ Remove</button>
  10567. </div>
  10568. </div>
  10569. <!-- Footer -->
  10570. <div class="px-6 py-3 border-t border-slate-800 bg-slate-950/40 flex justify-end gap-2.5">
  10571. <button type="button" onclick="closeOspfTemplateModal()" class="px-4 py-1.5 rounded-lg text-xs font-semibold text-slate-400 hover:text-white hover:bg-slate-800 transition">Cancel</button>
  10572. <button type="button" onclick="applyOspfTemplateForm()" class="px-4 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-sky-300 border border-slate-700 transition">Apply</button>
  10573. <button type="submit" class="px-5 py-1.5 rounded-lg text-xs font-bold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-600/30 transition">OK</button>
  10574. </div>
  10575. </form>
  10576. </div>
  10577. </div>
  10578. <!-- ================= MODAL: OSPF STATIC NEIGHBOR ================= -->
  10579. <div id="ospf-static-nbr-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10580. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-xl shadow-2xl overflow-hidden animate-in flex flex-col max-h-[90vh]">
  10581. <div class="px-6 py-3.5 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10582. <div class="flex items-center gap-2.5">
  10583. <span class="text-sky-400 text-sm">🔀</span>
  10584. <span class="text-xs font-semibold text-slate-400">Static Neighbor &gt;</span>
  10585. <span id="ospf-static-nbr-modal-title" class="text-xs font-bold text-sky-300 font-mono">New...</span>
  10586. </div>
  10587. <button type="button" onclick="closeOspfStaticNbrModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10588. </div>
  10589. <form id="ospf-static-nbr-form" onsubmit="submitOspfStaticNbr(event)" class="flex-1 overflow-hidden flex flex-col">
  10590. <input type="hidden" id="modal-ospf-snbr-id">
  10591. <div class="flex-1 overflow-y-auto p-6 flex gap-6">
  10592. <div class="flex-1 space-y-3.5 text-xs">
  10593. <div class="grid grid-cols-3 items-center gap-3">
  10594. <label class="text-slate-400 font-medium">Enabled</label>
  10595. <div class="col-span-2">
  10596. <input type="checkbox" id="modal-ospf-snbr-enabled" checked class="w-4 h-4 rounded text-sky-500 bg-slate-800 border-slate-700">
  10597. </div>
  10598. </div>
  10599. <div class="grid grid-cols-3 items-center gap-3">
  10600. <label class="text-slate-400 font-medium">Comment</label>
  10601. <div class="col-span-2">
  10602. <input type="text" id="modal-ospf-snbr-comment" placeholder="Optional comment" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white">
  10603. </div>
  10604. </div>
  10605. <div class="grid grid-cols-3 items-center gap-3">
  10606. <label class="text-slate-400 font-medium">Address *</label>
  10607. <div class="col-span-2">
  10608. <input type="text" id="modal-ospf-snbr-address" required placeholder="10.42.62.249" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10609. </div>
  10610. </div>
  10611. <div class="grid grid-cols-3 items-center gap-3">
  10612. <label class="text-slate-400 font-medium">Area</label>
  10613. <div class="col-span-2">
  10614. <select id="modal-ospf-snbr-area" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10615. <option value="Default">Default</option>
  10616. </select>
  10617. </div>
  10618. </div>
  10619. <div class="grid grid-cols-3 items-center gap-3">
  10620. <label class="text-slate-400 font-medium">Poll Interval</label>
  10621. <div class="col-span-2">
  10622. <input type="number" id="modal-ospf-snbr-poll" min="1" max="65535" placeholder="120" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10623. </div>
  10624. </div>
  10625. <div class="grid grid-cols-3 items-center gap-3">
  10626. <label class="text-slate-400 font-medium">Cost</label>
  10627. <div class="col-span-2">
  10628. <input type="number" id="modal-ospf-snbr-cost" min="1" max="65535" placeholder="1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-1.5 text-white font-mono">
  10629. </div>
  10630. </div>
  10631. </div>
  10632. <!-- Sidebar Actions -->
  10633. <div class="w-28 flex flex-col gap-2 border-l border-slate-800 pl-4 select-none">
  10634. <button type="button" onclick="deleteCurrentOspfStaticNbr()" id="btn-ospf-snbr-modal-remove" class="text-xs text-left px-2 py-1.5 rounded hover:bg-rose-950/40 text-rose-400">✕ Remove</button>
  10635. </div>
  10636. </div>
  10637. <!-- Footer -->
  10638. <div class="px-6 py-3 border-t border-slate-800 bg-slate-950/40 flex justify-end gap-2.5">
  10639. <button type="button" onclick="closeOspfStaticNbrModal()" class="px-4 py-1.5 rounded-lg text-xs font-semibold text-slate-400 hover:text-white hover:bg-slate-800 transition">Cancel</button>
  10640. <button type="button" onclick="applyOspfStaticNbrForm()" class="px-4 py-1.5 rounded-lg text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-sky-300 border border-slate-700 transition">Apply</button>
  10641. <button type="submit" class="px-5 py-1.5 rounded-lg text-xs font-bold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-600/30 transition">OK</button>
  10642. </div>
  10643. </form>
  10644. </div>
  10645. </div>
  10646. <!-- ================= MODAL: ADD / EDIT ROUTING FILTER RULE ================= -->
  10647. <div id="modal-routing-filter" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10648. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-2xl shadow-2xl overflow-hidden animate-in">
  10649. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10650. <div class="flex items-center gap-2">
  10651. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  10652. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
  10653. </svg>
  10654. <div>
  10655. <h3 id="modal-rf-title" class="text-sm font-bold text-white">Routing Filter Rule</h3>
  10656. <p class="text-[11px] text-slate-400">Procedural filter syntax: if (...) { ... } else { ... }</p>
  10657. </div>
  10658. </div>
  10659. <button onclick="closeRoutingFilterModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10660. </div>
  10661. <form id="routing-filter-form" onsubmit="submitRoutingFilter(event)" class="p-6 space-y-4 text-xs max-h-[85vh] overflow-y-auto">
  10662. <input type="hidden" id="modal-rf-id">
  10663. <div class="grid grid-cols-3 gap-3">
  10664. <div class="col-span-2">
  10665. <label class="block font-semibold text-slate-300 mb-1">Chain *</label>
  10666. <input type="text" id="modal-rf-chain" list="dl-routing-chains" required placeholder="e.g. bgp-in, bgp-out, awmn" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:border-emerald-500">
  10667. <datalist id="dl-routing-chains"></datalist>
  10668. </div>
  10669. <div>
  10670. <label class="block font-semibold text-slate-300 mb-1">Rule Order *</label>
  10671. <input type="number" id="modal-rf-order" value="10" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:border-emerald-500">
  10672. </div>
  10673. </div>
  10674. <!-- Script Rule Editor -->
  10675. <div>
  10676. <div class="flex items-center justify-between mb-1.5">
  10677. <label class="font-semibold text-slate-300 flex items-center gap-2">
  10678. <span>Filter Rule Script *</span>
  10679. <span class="text-[10px] text-emerald-400 font-mono bg-emerald-950/60 border border-emerald-800/40 px-1.5 py-0.5 rounded">v7 Script Engine</span>
  10680. </label>
  10681. <span class="text-[10px] text-slate-400">Terminates with accept; or reject;</span>
  10682. </div>
  10683. <!-- Quick Templates Bar -->
  10684. <div class="flex flex-wrap items-center gap-1.5 mb-2 p-2 bg-slate-950/60 border border-slate-800 rounded-lg">
  10685. <span class="text-[10px] text-slate-400 font-medium mr-1">Quick Templates:</span>
  10686. <button type="button" onclick="insertRuleSnippet('accept-default')" class="px-2 py-0.5 rounded text-[10px] bg-slate-800 hover:bg-slate-700 text-cyan-300 border border-slate-700">0.0.0.0/0</button>
  10687. <button type="button" onclick="insertRuleSnippet('drop-rfc1918')" class="px-2 py-0.5 rounded text-[10px] bg-slate-800 hover:bg-slate-700 text-rose-300 border border-slate-700">Drop RFC1918</button>
  10688. <button type="button" onclick="insertRuleSnippet('len-24')" class="px-2 py-0.5 rounded text-[10px] bg-slate-800 hover:bg-slate-700 text-amber-300 border border-slate-700">Len &le; /24</button>
  10689. <button type="button" onclick="insertRuleSnippet('local-pref-150')" class="px-2 py-0.5 rounded text-[10px] bg-slate-800 hover:bg-slate-700 text-emerald-300 border border-slate-700">Loc-Pref 150</button>
  10690. <button type="button" onclick="insertRuleSnippet('as-prepend-2')" class="px-2 py-0.5 rounded text-[10px] bg-slate-800 hover:bg-slate-700 text-purple-300 border border-slate-700">AS-Prepend 2x</button>
  10691. <button type="button" onclick="insertRuleSnippet('set-comm')" class="px-2 py-0.5 rounded text-[10px] bg-slate-800 hover:bg-slate-700 text-indigo-300 border border-slate-700">Add Comm</button>
  10692. <button type="button" onclick="insertRuleSnippet('match-comm')" class="px-2 py-0.5 rounded text-[10px] bg-slate-800 hover:bg-slate-700 text-indigo-300 border border-slate-700">Match Comm</button>
  10693. <button type="button" onclick="insertRuleSnippet('reject-all')" class="px-2 py-0.5 rounded text-[10px] bg-rose-950/60 hover:bg-rose-900 text-rose-300 border border-rose-900/50">Reject</button>
  10694. </div>
  10695. <textarea id="modal-rf-rule" rows="4" required placeholder="if (dst in 10.0.0.0/8 &amp;&amp; dst-len &gt;= 24) { reject; }&#10;if (dst in 10.0.0.0/8) { set bgp-local-pref 150; accept; }" class="w-full bg-slate-950 border border-slate-700 rounded-lg p-3 text-emerald-400 font-mono text-xs focus:outline-none focus:border-emerald-500"></textarea>
  10696. </div>
  10697. <!-- Optional Visual Builder Collapsible Accordion -->
  10698. <details class="bg-slate-950/40 border border-slate-800 rounded-xl p-3 group">
  10699. <summary class="font-semibold text-slate-300 cursor-pointer flex items-center justify-between text-xs select-none">
  10700. <span class="flex items-center gap-1.5 text-cyan-400">
  10701. <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  10702. <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
  10703. </svg>
  10704. <span>Visual Rule Wizard (Form Assistant)</span>
  10705. </span>
  10706. <span class="text-[10px] text-slate-500 group-open:rotate-180 transition-transform">▼</span>
  10707. </summary>
  10708. <div class="pt-3 space-y-3">
  10709. <div class="grid grid-cols-2 gap-3">
  10710. <div>
  10711. <label class="block font-medium text-slate-400 mb-1">Prefix (CIDR)</label>
  10712. <input type="text" id="modal-rf-prefix" placeholder="10.0.0.0/8" class="w-full bg-slate-800 border border-slate-700 rounded px-2.5 py-1.5 text-white font-mono">
  10713. </div>
  10714. <div>
  10715. <label class="block font-medium text-slate-400 mb-1">Prefix Length (Range)</label>
  10716. <input type="text" id="modal-rf-prefix-len" placeholder="24-32 or ge 24" class="w-full bg-slate-800 border border-slate-700 rounded px-2.5 py-1.5 text-white font-mono">
  10717. </div>
  10718. </div>
  10719. <div class="grid grid-cols-3 gap-3">
  10720. <div>
  10721. <label class="block font-medium text-slate-400 mb-1">Protocol</label>
  10722. <select id="modal-rf-protocol" class="w-full bg-slate-800 border border-slate-700 rounded px-2 py-1.5 text-white font-mono">
  10723. <option value="any">any</option>
  10724. <option value="bgp">bgp</option>
  10725. <option value="ospf">ospf</option>
  10726. <option value="connected">connected</option>
  10727. <option value="static">static</option>
  10728. </select>
  10729. </div>
  10730. <div>
  10731. <label class="block font-medium text-slate-400 mb-1">AS-Path</label>
  10732. <input type="text" id="modal-rf-as-path" placeholder="65000" class="w-full bg-slate-800 border border-slate-700 rounded px-2.5 py-1.5 text-white font-mono">
  10733. </div>
  10734. <div>
  10735. <label class="block font-medium text-slate-400 mb-1">Community</label>
  10736. <input type="text" id="modal-rf-community" placeholder="65000:100" class="w-full bg-slate-800 border border-slate-700 rounded px-2.5 py-1.5 text-white font-mono">
  10737. </div>
  10738. </div>
  10739. <div class="grid grid-cols-3 gap-3">
  10740. <div>
  10741. <label class="block font-medium text-slate-400 mb-1">Action</label>
  10742. <select id="modal-rf-action" class="w-full bg-slate-800 border border-slate-700 rounded px-2 py-1.5 text-white font-mono">
  10743. <option value="accept">accept</option>
  10744. <option value="reject">reject</option>
  10745. <option value="passthrough">passthrough</option>
  10746. </select>
  10747. </div>
  10748. <div>
  10749. <label class="block font-medium text-slate-400 mb-1">Set Local-Pref</label>
  10750. <input type="number" id="modal-rf-set-local-pref" placeholder="150" class="w-full bg-slate-800 border border-slate-700 rounded px-2.5 py-1.5 text-white font-mono">
  10751. </div>
  10752. <div>
  10753. <label class="block font-medium text-slate-400 mb-1">Set Prepend</label>
  10754. <input type="number" id="modal-rf-set-as-prepend" placeholder="2" class="w-full bg-slate-800 border border-slate-700 rounded px-2.5 py-1.5 text-white font-mono">
  10755. </div>
  10756. </div>
  10757. <div class="flex justify-end">
  10758. <button type="button" onclick="generateScriptFromWizard()" class="px-3 py-1 bg-cyan-600 hover:bg-cyan-500 text-white rounded font-semibold text-xs flex items-center gap-1">
  10759. <span>Generate Script Expression &uarr;</span>
  10760. </button>
  10761. </div>
  10762. </div>
  10763. </details>
  10764. <div class="grid grid-cols-3 gap-3">
  10765. <div class="col-span-2">
  10766. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  10767. <input type="text" id="modal-rf-comment" placeholder="Filter customer routes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  10768. </div>
  10769. <div class="flex items-center pt-6">
  10770. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  10771. <input type="checkbox" id="modal-rf-disabled" class="rounded text-emerald-600 bg-slate-800 border-slate-700">
  10772. <span class="font-medium">Disabled (X)</span>
  10773. </label>
  10774. </div>
  10775. </div>
  10776. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  10777. <button type="button" onclick="closeRoutingFilterModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  10778. <button type="button" onclick="applyRoutingFilterForm()" class="px-4 py-1.5 rounded-lg font-semibold bg-slate-800 hover:bg-slate-700 text-emerald-300 border border-slate-700">Apply</button>
  10779. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">OK</button>
  10780. </div>
  10781. </form>
  10782. </div>
  10783. </div>
  10784. <!-- ================= MODAL: COMMUNITY LIST ================= -->
  10785. <div id="modal-rf-community-list" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10786. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  10787. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10788. <div>
  10789. <h3 id="modal-rf-cl-title" class="text-sm font-bold text-white">BGP Community List</h3>
  10790. <p class="text-[11px] text-slate-400">Named community sets for matching in filter rules</p>
  10791. </div>
  10792. <button onclick="closeCommunityListModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10793. </div>
  10794. <form onsubmit="submitCommunityList(event)" class="p-6 space-y-4 text-xs">
  10795. <input type="hidden" id="modal-rf-cl-id">
  10796. <div>
  10797. <label class="block font-semibold text-slate-300 mb-1">List Name *</label>
  10798. <input type="text" id="modal-rf-cl-name" required placeholder="e.g. awmn-communities, blackhole" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10799. </div>
  10800. <div>
  10801. <label class="block font-semibold text-slate-300 mb-1">Communities (comma-separated) *</label>
  10802. <textarea id="modal-rf-cl-communities" rows="3" required placeholder="65000:100, 65000:200, no-export" class="w-full bg-slate-950 border border-slate-700 rounded-lg p-3 text-cyan-300 font-mono text-xs"></textarea>
  10803. <p class="text-[10px] text-slate-500 mt-1">Format: AS:value (e.g. 65000:100) or well-known keywords (no-export, blackhole)</p>
  10804. </div>
  10805. <div>
  10806. <label class="block font-semibold text-slate-300 mb-1">RegExp (Optional)</label>
  10807. <input type="text" id="modal-rf-cl-regexp" placeholder="e.g. ^65000:" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10808. </div>
  10809. <div class="grid grid-cols-3 gap-3">
  10810. <div class="col-span-2">
  10811. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  10812. <input type="text" id="modal-rf-cl-comment" placeholder="Community list note" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  10813. </div>
  10814. <div class="flex items-center pt-6">
  10815. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  10816. <input type="checkbox" id="modal-rf-cl-disabled" class="rounded text-emerald-600 bg-slate-800 border-slate-700">
  10817. <span class="font-medium">Disabled (X)</span>
  10818. </label>
  10819. </div>
  10820. </div>
  10821. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  10822. <button type="button" onclick="closeCommunityListModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  10823. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">OK</button>
  10824. </div>
  10825. </form>
  10826. </div>
  10827. </div>
  10828. <!-- ================= MODAL: AS LIST ================= -->
  10829. <div id="modal-rf-as-list" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10830. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  10831. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10832. <div>
  10833. <h3 id="modal-rf-al-title" class="text-sm font-bold text-white">AS Number List</h3>
  10834. <p class="text-[11px] text-slate-400">Named AS numbers or numeric ranges for AS-Path matching</p>
  10835. </div>
  10836. <button onclick="closeASListModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10837. </div>
  10838. <form onsubmit="submitASList(event)" class="p-6 space-y-4 text-xs">
  10839. <input type="hidden" id="modal-rf-al-id">
  10840. <div>
  10841. <label class="block font-semibold text-slate-300 mb-1">List Name *</label>
  10842. <input type="text" id="modal-rf-al-name" required placeholder="e.g. tier1-asns, my-peers" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10843. </div>
  10844. <div>
  10845. <label class="block font-semibold text-slate-300 mb-1">Range / AS Numbers *</label>
  10846. <input type="text" id="modal-rf-al-range" required placeholder="e.g. 65000-65010, 6610, 17244" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10847. <p class="text-[10px] text-slate-500 mt-1">Specify numeric range (e.g. 65000-65010) or comma-separated ASNs</p>
  10848. </div>
  10849. <div class="grid grid-cols-3 gap-3">
  10850. <div class="col-span-2">
  10851. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  10852. <input type="text" id="modal-rf-al-comment" placeholder="AS list note" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  10853. </div>
  10854. <div class="flex items-center pt-6">
  10855. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  10856. <input type="checkbox" id="modal-rf-al-disabled" class="rounded text-emerald-600 bg-slate-800 border-slate-700">
  10857. <span class="font-medium">Disabled (X)</span>
  10858. </label>
  10859. </div>
  10860. </div>
  10861. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  10862. <button type="button" onclick="closeASListModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  10863. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">OK</button>
  10864. </div>
  10865. </form>
  10866. </div>
  10867. </div>
  10868. <!-- ================= MODAL: RIP INSTANCE ================= -->
  10869. <div id="rip-instance-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10870. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-xl shadow-2xl overflow-hidden animate-in">
  10871. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10872. <h3 id="rip-instance-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  10873. <span class="text-rose-400">⚡</span> <span>RIP Instance</span>
  10874. </h3>
  10875. <button onclick="closeRipInstanceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10876. </div>
  10877. <form onsubmit="submitRipInstance(event)" class="p-6 space-y-4 text-xs max-h-[85vh] overflow-y-auto">
  10878. <input type="hidden" id="modal-rip-instance-id">
  10879. <div class="grid grid-cols-2 gap-4">
  10880. <div>
  10881. <label class="block font-semibold text-slate-300 mb-1">Instance Name *</label>
  10882. <input type="text" id="modal-rip-instance-name" required placeholder="rip1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10883. </div>
  10884. <div>
  10885. <label class="block font-semibold text-slate-300 mb-1">Protocol Version</label>
  10886. <select id="modal-rip-instance-version" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10887. <option value="rip2">RIPv2 (IPv4)</option>
  10888. <option value="ripng">RIPng (IPv6)</option>
  10889. </select>
  10890. </div>
  10891. </div>
  10892. <div class="grid grid-cols-3 gap-3">
  10893. <div>
  10894. <label class="block font-semibold text-slate-300 mb-1">Update Time (s)</label>
  10895. <input type="number" id="modal-rip-instance-update" value="30" min="1" max="3600" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10896. </div>
  10897. <div>
  10898. <label class="block font-semibold text-slate-300 mb-1">Timeout (s)</label>
  10899. <input type="number" id="modal-rip-instance-timeout" value="180" min="1" max="3600" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10900. </div>
  10901. <div>
  10902. <label class="block font-semibold text-slate-300 mb-1">Garbage (s)</label>
  10903. <input type="number" id="modal-rip-instance-garbage" value="120" min="1" max="3600" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10904. </div>
  10905. </div>
  10906. <div class="grid grid-cols-2 gap-4">
  10907. <div>
  10908. <label class="block font-semibold text-slate-300 mb-1">Default Metric</label>
  10909. <input type="number" id="modal-rip-instance-metric" value="1" min="1" max="16" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10910. </div>
  10911. <div>
  10912. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  10913. <input type="text" id="modal-rip-instance-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  10914. </div>
  10915. </div>
  10916. <div class="border-t border-slate-800 pt-3">
  10917. <label class="block font-semibold text-slate-300 mb-2">Route Redistribution</label>
  10918. <div class="grid grid-cols-3 gap-2 text-slate-300">
  10919. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-rip-instance-redist-conn" class="rounded text-rose-500 bg-slate-800 border-slate-700"> Connected</label>
  10920. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-rip-instance-redist-static" class="rounded text-rose-500 bg-slate-800 border-slate-700"> Static</label>
  10921. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-rip-instance-redist-bgp" class="rounded text-rose-500 bg-slate-800 border-slate-700"> BGP</label>
  10922. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-rip-instance-redist-ospf" class="rounded text-rose-500 bg-slate-800 border-slate-700"> OSPF</label>
  10923. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-rip-instance-redist-default" class="rounded text-rose-500 bg-slate-800 border-slate-700"> Default Route</label>
  10924. </div>
  10925. </div>
  10926. <div class="pt-2 flex items-center gap-2">
  10927. <label class="flex items-center gap-2 text-slate-300">
  10928. <input type="checkbox" id="modal-rip-instance-enabled" checked class="rounded text-rose-500 bg-slate-800 border-slate-700">
  10929. <span class="font-semibold">Enabled</span>
  10930. </label>
  10931. </div>
  10932. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  10933. <button type="button" onclick="closeRipInstanceModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  10934. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-rose-600 hover:bg-rose-500 text-white shadow-lg shadow-rose-500/20">OK</button>
  10935. </div>
  10936. </form>
  10937. </div>
  10938. </div>
  10939. <!-- ================= MODAL: RIP INTERFACE ================= -->
  10940. <div id="rip-interface-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  10941. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  10942. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  10943. <h3 id="rip-interface-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  10944. <span class="text-rose-400">⚡</span> <span>RIP Interface Template</span>
  10945. </h3>
  10946. <button onclick="closeRipInterfaceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  10947. </div>
  10948. <form onsubmit="submitRipInterface(event)" class="p-6 space-y-4 text-xs">
  10949. <input type="hidden" id="modal-rip-iface-id">
  10950. <div class="grid grid-cols-2 gap-4">
  10951. <div>
  10952. <label class="block font-semibold text-slate-300 mb-1">Interface Pattern *</label>
  10953. <input type="text" id="modal-rip-iface-pattern" required placeholder="e.g. eth0, lan*, or *" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10954. </div>
  10955. <div>
  10956. <label class="block font-semibold text-slate-300 mb-1">RIP Instance</label>
  10957. <input type="text" id="modal-rip-iface-instance" placeholder="rip1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10958. </div>
  10959. </div>
  10960. <div class="grid grid-cols-2 gap-4">
  10961. <div>
  10962. <label class="block font-semibold text-slate-300 mb-1">Mode</label>
  10963. <select id="modal-rip-iface-mode" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10964. <option value="multicast">Multicast (224.0.0.9)</option>
  10965. <option value="broadcast">Broadcast</option>
  10966. </select>
  10967. </div>
  10968. <div>
  10969. <label class="block font-semibold text-slate-300 mb-1">Metric Cost</label>
  10970. <input type="number" id="modal-rip-iface-metric" value="1" min="1" max="16" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  10971. </div>
  10972. </div>
  10973. <div>
  10974. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  10975. <input type="text" id="modal-rip-iface-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  10976. </div>
  10977. <div class="flex items-center gap-6 pt-2">
  10978. <label class="flex items-center gap-2 text-slate-300">
  10979. <input type="checkbox" id="modal-rip-iface-enabled" checked class="rounded text-rose-500 bg-slate-800 border-slate-700">
  10980. <span class="font-semibold">Enabled</span>
  10981. </label>
  10982. <label class="flex items-center gap-2 text-slate-300">
  10983. <input type="checkbox" id="modal-rip-iface-passive" class="rounded text-rose-500 bg-slate-800 border-slate-700">
  10984. <span>Passive Interface</span>
  10985. </label>
  10986. <label class="flex items-center gap-2 text-slate-300">
  10987. <input type="checkbox" id="modal-rip-iface-bfd" class="rounded text-rose-500 bg-slate-800 border-slate-700">
  10988. <span>Use BFD</span>
  10989. </label>
  10990. </div>
  10991. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  10992. <button type="button" onclick="closeRipInterfaceModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  10993. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-rose-600 hover:bg-rose-500 text-white shadow-lg shadow-rose-500/20">OK</button>
  10994. </div>
  10995. </form>
  10996. </div>
  10997. </div>
  10998. <!-- ================= MODAL: BFD INTERFACE ================= -->
  10999. <div id="bfd-interface-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11000. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11001. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  11002. <h3 id="bfd-interface-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  11003. <span class="text-amber-400">⏱</span> <span>BFD Interface Profile</span>
  11004. </h3>
  11005. <button onclick="closeBfdInterfaceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11006. </div>
  11007. <form onsubmit="submitBfdInterface(event)" class="p-6 space-y-4 text-xs">
  11008. <input type="hidden" id="modal-bfd-iface-id">
  11009. <div>
  11010. <label class="block font-semibold text-slate-300 mb-1">Interface Pattern *</label>
  11011. <input type="text" id="modal-bfd-iface-pattern" required placeholder="e.g. eth*, wg*, or *" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11012. </div>
  11013. <div class="grid grid-cols-2 gap-4">
  11014. <div>
  11015. <label class="block font-semibold text-slate-300 mb-1">Min RX Interval (ms)</label>
  11016. <input type="number" id="modal-bfd-iface-min-rx" value="100" min="10" max="60000" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11017. </div>
  11018. <div>
  11019. <label class="block font-semibold text-slate-300 mb-1">Min TX Interval (ms)</label>
  11020. <input type="number" id="modal-bfd-iface-min-tx" value="100" min="10" max="60000" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11021. </div>
  11022. </div>
  11023. <div class="grid grid-cols-2 gap-4">
  11024. <div>
  11025. <label class="block font-semibold text-slate-300 mb-1">Idle TX Interval (ms)</label>
  11026. <input type="number" id="modal-bfd-iface-idle-tx" value="1000" min="100" max="60000" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11027. </div>
  11028. <div>
  11029. <label class="block font-semibold text-slate-300 mb-1">Multiplier</label>
  11030. <input type="number" id="modal-bfd-iface-multiplier" value="3" min="2" max="255" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11031. </div>
  11032. </div>
  11033. <div>
  11034. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  11035. <input type="text" id="modal-bfd-iface-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11036. </div>
  11037. <div class="flex items-center gap-6 pt-2">
  11038. <label class="flex items-center gap-2 text-slate-300">
  11039. <input type="checkbox" id="modal-bfd-iface-enabled" checked class="rounded text-amber-500 bg-slate-800 border-slate-700">
  11040. <span class="font-semibold">Enabled</span>
  11041. </label>
  11042. <label class="flex items-center gap-2 text-slate-300">
  11043. <input type="checkbox" id="modal-bfd-iface-passive" class="rounded text-amber-500 bg-slate-800 border-slate-700">
  11044. <span>Passive</span>
  11045. </label>
  11046. </div>
  11047. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  11048. <button type="button" onclick="closeBfdInterfaceModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11049. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20">OK</button>
  11050. </div>
  11051. </form>
  11052. </div>
  11053. </div>
  11054. <!-- ================= MODAL: BFD PEER ================= -->
  11055. <div id="bfd-peer-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11056. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  11057. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  11058. <h3 id="bfd-peer-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  11059. <span class="text-amber-400">⏱</span> <span>Static BFD Peer</span>
  11060. </h3>
  11061. <button onclick="closeBfdPeerModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11062. </div>
  11063. <form onsubmit="submitBfdPeer(event)" class="p-6 space-y-4 text-xs">
  11064. <input type="hidden" id="modal-bfd-peer-id">
  11065. <div>
  11066. <label class="block font-semibold text-slate-300 mb-1">Neighbor IP Address *</label>
  11067. <input type="text" id="modal-bfd-peer-address" required placeholder="e.g. 192.168.1.1 or 2001:db8::1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11068. </div>
  11069. <div>
  11070. <label class="block font-semibold text-slate-300 mb-1">Interface (Device)</label>
  11071. <input type="text" id="modal-bfd-peer-interface" placeholder="Optional e.g. eth0" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11072. </div>
  11073. <div>
  11074. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  11075. <input type="text" id="modal-bfd-peer-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11076. </div>
  11077. <div class="flex items-center gap-6 pt-2">
  11078. <label class="flex items-center gap-2 text-slate-300">
  11079. <input type="checkbox" id="modal-bfd-peer-enabled" checked class="rounded text-amber-500 bg-slate-800 border-slate-700">
  11080. <span class="font-semibold">Enabled</span>
  11081. </label>
  11082. <label class="flex items-center gap-2 text-slate-300">
  11083. <input type="checkbox" id="modal-bfd-peer-multihop" class="rounded text-amber-500 bg-slate-800 border-slate-700">
  11084. <span>Multihop</span>
  11085. </label>
  11086. </div>
  11087. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  11088. <button type="button" onclick="closeBfdPeerModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11089. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-500/20">OK</button>
  11090. </div>
  11091. </form>
  11092. </div>
  11093. </div>
  11094. <!-- ================= MODAL: BABEL INSTANCE ================= -->
  11095. <div id="babel-instance-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11096. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-xl shadow-2xl overflow-hidden animate-in">
  11097. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  11098. <h3 id="babel-instance-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  11099. <span class="text-teal-400">🌐</span> <span>Babel Instance</span>
  11100. </h3>
  11101. <button onclick="closeBabelInstanceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11102. </div>
  11103. <form onsubmit="submitBabelInstance(event)" class="p-6 space-y-4 text-xs max-h-[85vh] overflow-y-auto">
  11104. <input type="hidden" id="modal-babel-instance-id">
  11105. <div class="grid grid-cols-2 gap-4">
  11106. <div>
  11107. <label class="block font-semibold text-slate-300 mb-1">Instance Name *</label>
  11108. <input type="text" id="modal-babel-instance-name" required placeholder="babel1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11109. </div>
  11110. <div>
  11111. <label class="block font-semibold text-slate-300 mb-1">Router ID</label>
  11112. <input type="text" id="modal-babel-instance-routerid" placeholder="Auto or e.g. 10.42.0.1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11113. </div>
  11114. </div>
  11115. <div class="grid grid-cols-2 gap-4">
  11116. <div>
  11117. <label class="block font-semibold text-slate-300 mb-1">Default Metric</label>
  11118. <input type="number" id="modal-babel-instance-metric" value="10" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11119. </div>
  11120. <div>
  11121. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  11122. <input type="text" id="modal-babel-instance-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11123. </div>
  11124. </div>
  11125. <div class="border-t border-slate-800 pt-3">
  11126. <label class="block font-semibold text-slate-300 mb-2">Route Redistribution</label>
  11127. <div class="grid grid-cols-3 gap-2 text-slate-300">
  11128. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-babel-instance-redist-conn" class="rounded text-teal-500 bg-slate-800 border-slate-700"> Connected</label>
  11129. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-babel-instance-redist-static" class="rounded text-teal-500 bg-slate-800 border-slate-700"> Static</label>
  11130. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-babel-instance-redist-bgp" class="rounded text-teal-500 bg-slate-800 border-slate-700"> BGP</label>
  11131. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-babel-instance-redist-ospf" class="rounded text-teal-500 bg-slate-800 border-slate-700"> OSPF</label>
  11132. <label class="flex items-center gap-1.5"><input type="checkbox" id="modal-babel-instance-redist-default" class="rounded text-teal-500 bg-slate-800 border-slate-700"> Default Route</label>
  11133. </div>
  11134. </div>
  11135. <div class="pt-2 flex items-center gap-2">
  11136. <label class="flex items-center gap-2 text-slate-300">
  11137. <input type="checkbox" id="modal-babel-instance-enabled" checked class="rounded text-teal-500 bg-slate-800 border-slate-700">
  11138. <span class="font-semibold">Enabled</span>
  11139. </label>
  11140. </div>
  11141. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  11142. <button type="button" onclick="closeBabelInstanceModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11143. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-teal-600 hover:bg-teal-500 text-white shadow-lg shadow-teal-500/20">OK</button>
  11144. </div>
  11145. </form>
  11146. </div>
  11147. </div>
  11148. <!-- ================= MODAL: BABEL INTERFACE ================= -->
  11149. <div id="babel-interface-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11150. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11151. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  11152. <h3 id="babel-interface-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  11153. <span class="text-teal-400">🌐</span> <span>Babel Interface Template</span>
  11154. </h3>
  11155. <button onclick="closeBabelInterfaceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11156. </div>
  11157. <form onsubmit="submitBabelInterface(event)" class="p-6 space-y-4 text-xs">
  11158. <input type="hidden" id="modal-babel-iface-id">
  11159. <div class="grid grid-cols-2 gap-4">
  11160. <div>
  11161. <label class="block font-semibold text-slate-300 mb-1">Interface Pattern *</label>
  11162. <input type="text" id="modal-babel-iface-pattern" required placeholder="e.g. eth0, wlan*, or *" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11163. </div>
  11164. <div>
  11165. <label class="block font-semibold text-slate-300 mb-1">Link Type</label>
  11166. <select id="modal-babel-iface-type" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11167. <option value="wired">Wired (Ethernet / Tunnel)</option>
  11168. <option value="wireless">Wireless (Mesh)</option>
  11169. </select>
  11170. </div>
  11171. </div>
  11172. <div class="grid grid-cols-3 gap-3">
  11173. <div>
  11174. <label class="block font-semibold text-slate-300 mb-1">RX Cost</label>
  11175. <input type="number" id="modal-babel-iface-rxcost" value="10" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11176. </div>
  11177. <div>
  11178. <label class="block font-semibold text-slate-300 mb-1">Hello Interval (s)</label>
  11179. <input type="number" id="modal-babel-iface-hello" value="4" min="1" max="3600" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11180. </div>
  11181. <div>
  11182. <label class="block font-semibold text-slate-300 mb-1">Update Interval (s)</label>
  11183. <input type="number" id="modal-babel-iface-update" value="16" min="1" max="3600" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11184. </div>
  11185. </div>
  11186. <div>
  11187. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  11188. <input type="text" id="modal-babel-iface-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11189. </div>
  11190. <div class="pt-2">
  11191. <label class="flex items-center gap-2 text-slate-300">
  11192. <input type="checkbox" id="modal-babel-iface-enabled" checked class="rounded text-teal-500 bg-slate-800 border-slate-700">
  11193. <span class="font-semibold">Enabled</span>
  11194. </label>
  11195. </div>
  11196. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  11197. <button type="button" onclick="closeBabelInterfaceModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11198. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-teal-600 hover:bg-teal-500 text-white shadow-lg shadow-teal-500/20">OK</button>
  11199. </div>
  11200. </form>
  11201. </div>
  11202. </div>
  11203. <!-- ================= MODAL: RADV INSTANCE ================= -->
  11204. <div id="radv-instance-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11205. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  11206. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  11207. <h3 id="radv-instance-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  11208. <span class="text-blue-400">📢</span> <span>RAdv Instance</span>
  11209. </h3>
  11210. <button onclick="closeRAdvInstanceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11211. </div>
  11212. <form onsubmit="submitRAdvInstance(event)" class="p-6 space-y-4 text-xs">
  11213. <input type="hidden" id="modal-radv-instance-id">
  11214. <div>
  11215. <label class="block font-semibold text-slate-300 mb-1">Instance Name *</label>
  11216. <input type="text" id="modal-radv-instance-name" required placeholder="radv1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11217. </div>
  11218. <div>
  11219. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  11220. <input type="text" id="modal-radv-instance-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11221. </div>
  11222. <div class="pt-2">
  11223. <label class="flex items-center gap-2 text-slate-300">
  11224. <input type="checkbox" id="modal-radv-instance-enabled" checked class="rounded text-blue-500 bg-slate-800 border-slate-700">
  11225. <span class="font-semibold">Enabled</span>
  11226. </label>
  11227. </div>
  11228. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  11229. <button type="button" onclick="closeRAdvInstanceModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11230. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-blue-600 hover:bg-blue-500 text-white shadow-lg shadow-blue-500/20">OK</button>
  11231. </div>
  11232. </form>
  11233. </div>
  11234. </div>
  11235. <!-- ================= MODAL: RADV INTERFACE ================= -->
  11236. <div id="radv-interface-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11237. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11238. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/60">
  11239. <h3 id="radv-interface-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  11240. <span class="text-blue-400">📢</span> <span>RAdv Interface Profile</span>
  11241. </h3>
  11242. <button onclick="closeRAdvInterfaceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11243. </div>
  11244. <form onsubmit="submitRAdvInterface(event)" class="p-6 space-y-4 text-xs max-h-[85vh] overflow-y-auto">
  11245. <input type="hidden" id="modal-radv-iface-id">
  11246. <div class="grid grid-cols-2 gap-4">
  11247. <div>
  11248. <label class="block font-semibold text-slate-300 mb-1">Interface *</label>
  11249. <input type="text" id="modal-radv-iface-interface" required placeholder="e.g. eth0 or lan0" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11250. </div>
  11251. <div>
  11252. <label class="block font-semibold text-slate-300 mb-1">Instance</label>
  11253. <input type="text" id="modal-radv-iface-instance" placeholder="radv1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11254. </div>
  11255. </div>
  11256. <div class="grid grid-cols-2 gap-4">
  11257. <div>
  11258. <label class="block font-semibold text-slate-300 mb-1">Min RA Interval (s)</label>
  11259. <input type="number" id="modal-radv-iface-min-ra" value="200" min="3" max="1800" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11260. </div>
  11261. <div>
  11262. <label class="block font-semibold text-slate-300 mb-1">Max RA Interval (s)</label>
  11263. <input type="number" id="modal-radv-iface-max-ra" value="600" min="4" max="1800" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11264. </div>
  11265. </div>
  11266. <div class="grid grid-cols-2 gap-4">
  11267. <div>
  11268. <label class="block font-semibold text-slate-300 mb-1">Default Lifetime (s)</label>
  11269. <input type="number" id="modal-radv-iface-lifetime" value="1800" min="0" max="9000" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11270. </div>
  11271. <div>
  11272. <label class="block font-semibold text-slate-300 mb-1">Hop Limit</label>
  11273. <input type="number" id="modal-radv-iface-hoplimit" value="64" min="0" max="255" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11274. </div>
  11275. </div>
  11276. <div>
  11277. <label class="block font-semibold text-slate-300 mb-1">Advertised IPv6 Prefixes (comma-separated)</label>
  11278. <input type="text" id="modal-radv-iface-prefixes" placeholder="e.g. 2001:db8:1::/64, fd00::/64" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11279. </div>
  11280. <div>
  11281. <label class="block font-semibold text-slate-300 mb-1">RDNSS IPv6 DNS Servers (comma-separated)</label>
  11282. <input type="text" id="modal-radv-iface-rdnss" placeholder="e.g. 2001:4860:4860::8888, 2606:4700:4700::1111" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11283. </div>
  11284. <div>
  11285. <label class="block font-semibold text-slate-300 mb-1">Comment</label>
  11286. <input type="text" id="modal-radv-iface-comment" placeholder="Optional notes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11287. </div>
  11288. <div class="pt-2">
  11289. <label class="flex items-center gap-2 text-slate-300">
  11290. <input type="checkbox" id="modal-radv-iface-enabled" checked class="rounded text-blue-500 bg-slate-800 border-slate-700">
  11291. <span class="font-semibold">Enabled</span>
  11292. </label>
  11293. </div>
  11294. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-2">
  11295. <button type="button" onclick="closeRAdvInterfaceModal()" class="px-4 py-1.5 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11296. <button type="submit" class="px-5 py-1.5 rounded-lg font-bold bg-blue-600 hover:bg-blue-500 text-white shadow-lg shadow-blue-500/20">OK</button>
  11297. </div>
  11298. </form>
  11299. </div>
  11300. </div>
  11301. <!-- ================= MODAL: ADD / EDIT DHCP CLIENT ================= -->
  11302. <div id="modal-dhcp-client" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11303. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  11304. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11305. <div class="flex items-center gap-2">
  11306. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-amber-500/20 text-amber-300 border border-amber-500/30 tracking-wide uppercase">DHCP Client</span>
  11307. <h3 id="modal-dhcp-title" class="text-sm font-bold text-white">Add DHCP Client</h3>
  11308. </div>
  11309. <button onclick="closeDHCPClientModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11310. </div>
  11311. <form onsubmit="submitSaveDHCPClient(event)" class="p-6 space-y-4 text-xs">
  11312. <input type="hidden" id="modal-dhcp-id">
  11313. <div>
  11314. <label class="block font-semibold text-slate-300 mb-1.5">Interface *</label>
  11315. <select id="modal-dhcp-iface" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  11316. <option value="">Select interface...</option>
  11317. </select>
  11318. <p class="text-[11px] text-slate-500 mt-1">Physical port, VLAN sub-interface, or Bridge to acquire dynamic lease on.</p>
  11319. </div>
  11320. <div class="grid grid-cols-2 gap-4">
  11321. <div>
  11322. <label class="block font-semibold text-slate-300 mb-1.5">Default Route Distance</label>
  11323. <input type="number" id="modal-dhcp-distance" value="1" min="1" max="255" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-amber-500">
  11324. </div>
  11325. <div>
  11326. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11327. <input type="text" id="modal-dhcp-comment" placeholder="e.g. ISP WAN DHCP" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-amber-500">
  11328. </div>
  11329. </div>
  11330. <div class="bg-slate-950/60 border border-slate-800/80 rounded-xl p-3 space-y-2.5">
  11331. <div class="text-[11px] font-bold text-slate-400 uppercase tracking-wider">Gateway & DNS Options</div>
  11332. <label class="flex items-center space-x-2.5 text-slate-300 cursor-pointer">
  11333. <input type="checkbox" id="modal-dhcp-def-route" checked class="rounded text-amber-500 bg-slate-800 border-slate-700 focus:ring-amber-500">
  11334. <span>Add Default Route (default gateway from DHCP)</span>
  11335. </label>
  11336. <label class="flex items-center space-x-2.5 text-slate-300 cursor-pointer">
  11337. <input type="checkbox" id="modal-dhcp-peer-dns" checked class="rounded text-amber-500 bg-slate-800 border-slate-700 focus:ring-amber-500">
  11338. <span>Use Peer DNS (DNS servers from DHCP)</span>
  11339. </label>
  11340. <label class="flex items-center space-x-2.5 text-slate-300 cursor-pointer">
  11341. <input type="checkbox" id="modal-dhcp-peer-ntp" checked class="rounded text-amber-500 bg-slate-800 border-slate-700 focus:ring-amber-500">
  11342. <span>Use Peer NTP (NTP servers from DHCP)</span>
  11343. </label>
  11344. <label class="flex items-center space-x-2.5 text-slate-300 cursor-pointer pt-1 border-t border-slate-800/80">
  11345. <input type="checkbox" id="modal-dhcp-enabled" checked class="rounded text-emerald-500 bg-slate-800 border-slate-700 focus:ring-emerald-500">
  11346. <span class="font-semibold text-white">Enable DHCP Client immediately</span>
  11347. </label>
  11348. </div>
  11349. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11350. <button type="button" onclick="closeDHCPClientModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800 transition">Cancel</button>
  11351. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-amber-500 hover:bg-amber-400 text-slate-950 shadow-lg shadow-amber-500/20 transition">Save DHCP Client</button>
  11352. </div>
  11353. </form>
  11354. </div>
  11355. </div>
  11356. <!-- ================= MODAL: ADD / EDIT DHCP SERVER INSTANCE ================= -->
  11357. <div id="dhcp-server-instance-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11358. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11359. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11360. <div class="flex items-center gap-2">
  11361. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 tracking-wide uppercase">Core Network Service</span>
  11362. <h3 id="dhcp-server-modal-title" class="text-sm font-bold text-white">Add DHCP Server</h3>
  11363. </div>
  11364. <button onclick="closeDHCPServerModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11365. </div>
  11366. <form onsubmit="submitSaveDHCPServer(event)" class="p-6 space-y-4 text-xs font-sans">
  11367. <input type="hidden" id="dhcp-server-modal-id">
  11368. <div class="grid grid-cols-2 gap-4">
  11369. <div>
  11370. <label class="block font-semibold text-slate-300 mb-1.5">Name *</label>
  11371. <input type="text" id="dhcp-server-modal-name" placeholder="dhcp1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11372. </div>
  11373. <div>
  11374. <label class="block font-semibold text-slate-300 mb-1.5">Interface *</label>
  11375. <select id="dhcp-server-modal-iface" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11376. <option value="">Select interface...</option>
  11377. </select>
  11378. </div>
  11379. </div>
  11380. <div class="grid grid-cols-2 gap-4">
  11381. <div>
  11382. <label class="block font-semibold text-slate-300 mb-1.5">Assigned Network (Subnet) *</label>
  11383. <select id="dhcp-server-modal-network" onchange="onDHCPServerNetworkChange()" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11384. <option value="">Select network subnet...</option>
  11385. </select>
  11386. <span class="text-[10px] text-slate-400">Distributes Gateway, DNS, NTP & Options</span>
  11387. </div>
  11388. <div>
  11389. <label class="block font-semibold text-slate-300 mb-1.5">Address Pool (Range) *</label>
  11390. <input type="text" id="dhcp-server-modal-pool" placeholder="192.168.88.10-192.168.88.254" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11391. <span class="text-[10px] text-slate-400">Format: startIP-endIP</span>
  11392. </div>
  11393. </div>
  11394. <div class="grid grid-cols-2 gap-4">
  11395. <div>
  11396. <label class="block font-semibold text-slate-300 mb-1.5">Lease Time</label>
  11397. <input type="text" id="dhcp-server-modal-leasetime" value="12h" placeholder="12h, 1d, 30m" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11398. <span class="text-[10px] text-slate-400">Default: 12h</span>
  11399. </div>
  11400. <div>
  11401. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11402. <input type="text" id="dhcp-server-modal-comment" placeholder="LAN Office Pool" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-emerald-500">
  11403. </div>
  11404. </div>
  11405. <div class="flex items-center justify-between pt-2 border-t border-slate-800/80">
  11406. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  11407. <input type="checkbox" id="dhcp-server-modal-authoritative" checked class="rounded bg-slate-800 border-slate-700 text-emerald-600">
  11408. <span>Authoritative Mode (Fast NAK for stale clients)</span>
  11409. </label>
  11410. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  11411. <input type="checkbox" id="dhcp-server-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-600">
  11412. <span>Disabled (X)</span>
  11413. </label>
  11414. </div>
  11415. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11416. <button type="button" onclick="closeDHCPServerModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11417. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">Save Server</button>
  11418. </div>
  11419. </form>
  11420. </div>
  11421. </div>
  11422. <!-- ================= MODAL: ADD / EDIT DHCP NETWORK ================= -->
  11423. <div id="dhcp-network-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11424. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-2xl shadow-2xl overflow-hidden animate-in">
  11425. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11426. <div class="flex items-center gap-2">
  11427. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 tracking-wide uppercase">Subnet Network</span>
  11428. <h3 id="dhcp-network-modal-title" class="text-sm font-bold text-white">Add DHCP Network</h3>
  11429. </div>
  11430. <button onclick="closeDHCPNetworkModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11431. </div>
  11432. <form onsubmit="submitSaveDHCPNetwork(event)" class="p-6 space-y-4 text-xs font-sans max-h-[80vh] overflow-y-auto">
  11433. <input type="hidden" id="dhcp-network-modal-id">
  11434. <div class="grid grid-cols-2 gap-4">
  11435. <div>
  11436. <label class="block font-semibold text-slate-300 mb-1.5">Subnet Address (CIDR) *</label>
  11437. <input type="text" id="dhcp-network-modal-address" placeholder="192.168.88.0/24" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11438. <span class="text-[10px] text-slate-400">Network address with CIDR mask</span>
  11439. </div>
  11440. <div>
  11441. <label class="block font-semibold text-slate-300 mb-1.5">Gateway (Router - Option 3) *</label>
  11442. <input type="text" id="dhcp-network-modal-gateway" placeholder="192.168.88.1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11443. <span class="text-[10px] text-slate-400">Default gateway advertised to clients</span>
  11444. </div>
  11445. </div>
  11446. <div class="grid grid-cols-2 gap-4">
  11447. <div>
  11448. <label class="block font-semibold text-slate-300 mb-1.5">DNS Servers (Option 6)</label>
  11449. <input type="text" id="dhcp-network-modal-dns" placeholder="192.168.88.1, 1.1.1.1, 8.8.8.8" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11450. <span class="text-[10px] text-slate-400">Comma-separated list of DNS IPs</span>
  11451. </div>
  11452. <div>
  11453. <label class="block font-semibold text-slate-300 mb-1.5">Domain Name (Option 15)</label>
  11454. <input type="text" id="dhcp-network-modal-domain" placeholder="lan or company.local" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11455. <span class="text-[10px] text-slate-400">Domain search suffix</span>
  11456. </div>
  11457. </div>
  11458. <div class="grid grid-cols-2 gap-4">
  11459. <div>
  11460. <label class="block font-semibold text-slate-300 mb-1.5">Netmask (Optional Override)</label>
  11461. <input type="text" id="dhcp-network-modal-netmask" placeholder="255.255.255.0" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11462. <span class="text-[10px] text-slate-400">Leave blank to use CIDR mask</span>
  11463. </div>
  11464. <div>
  11465. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11466. <input type="text" id="dhcp-network-modal-comment" placeholder="e.g. Office LAN Subnet" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-emerald-500">
  11467. <span class="text-[10px] text-slate-400">Informational label</span>
  11468. </div>
  11469. </div>
  11470. <div class="p-3.5 bg-slate-950/60 border border-slate-800 rounded-xl space-y-2">
  11471. <div class="flex items-center justify-between">
  11472. <label class="block font-semibold text-slate-300">Linked Custom DHCP Options</label>
  11473. <button type="button" onclick="closeDHCPNetworkModal(); switchDHCPServerTab('options')" class="text-[10px] text-cyan-400 hover:underline">Manage Options</button>
  11474. </div>
  11475. <p class="text-[11px] text-slate-400">Select any DHCP options (NTP #42, TFTP #66, PXE Boot #67, WINS #44, UniFi #43, CAPWAP #138, VoIP #150, WPAD #252) to distribute to clients on this subnet:</p>
  11476. <div id="dhcp-network-modal-options-list" class="grid grid-cols-2 gap-2 p-2 bg-slate-900 border border-slate-800 rounded-lg max-h-40 overflow-y-auto font-mono text-[11px]">
  11477. <!-- Dynamically populated options checkboxes -->
  11478. </div>
  11479. </div>
  11480. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11481. <button type="button" onclick="closeDHCPNetworkModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11482. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">Save Network</button>
  11483. </div>
  11484. </form>
  11485. </div>
  11486. </div>
  11487. <!-- ================= MODAL: ADD / EDIT STATIC LEASE ================= -->
  11488. <div id="dhcp-static-lease-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11489. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11490. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11491. <div class="flex items-center gap-2">
  11492. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-purple-500/20 text-purple-300 border border-purple-500/30 tracking-wide uppercase">Static Lease</span>
  11493. <h3 id="dhcp-static-lease-modal-title" class="text-sm font-bold text-white">Add Static Lease Reservation</h3>
  11494. </div>
  11495. <button onclick="closeDHCPStaticLeaseModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11496. </div>
  11497. <form onsubmit="submitSaveDHCPStaticLease(event)" class="p-6 space-y-4 text-xs font-sans">
  11498. <input type="hidden" id="dhcp-static-lease-modal-id">
  11499. <div class="grid grid-cols-2 gap-4">
  11500. <div>
  11501. <label class="block font-semibold text-slate-300 mb-1.5">MAC Address *</label>
  11502. <input type="text" id="dhcp-static-lease-modal-mac" placeholder="AA:BB:CC:DD:EE:FF" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-purple-500">
  11503. </div>
  11504. <div>
  11505. <label class="block font-semibold text-slate-300 mb-1.5">Reserved IP Address *</label>
  11506. <input type="text" id="dhcp-static-lease-modal-ip" placeholder="192.168.88.100" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-purple-500">
  11507. </div>
  11508. </div>
  11509. <div class="grid grid-cols-2 gap-4">
  11510. <div>
  11511. <label class="block font-semibold text-slate-300 mb-1.5">Host Name</label>
  11512. <input type="text" id="dhcp-static-lease-modal-hostname" placeholder="printer, nas, camera" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-purple-500">
  11513. </div>
  11514. <div>
  11515. <label class="block font-semibold text-slate-300 mb-1.5">Server</label>
  11516. <input type="text" id="dhcp-static-lease-modal-server" value="all" placeholder="all or server name" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-purple-500">
  11517. </div>
  11518. </div>
  11519. <div>
  11520. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11521. <input type="text" id="dhcp-static-lease-modal-comment" placeholder="LaserJet Pro Printer" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-purple-500">
  11522. </div>
  11523. <div class="pt-2 border-t border-slate-800/80">
  11524. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  11525. <input type="checkbox" id="dhcp-static-lease-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-600">
  11526. <span>Disabled (X)</span>
  11527. </label>
  11528. </div>
  11529. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11530. <button type="button" onclick="closeDHCPStaticLeaseModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11531. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-purple-600 hover:bg-purple-500 text-white shadow-lg shadow-purple-500/20">Save Reservation</button>
  11532. </div>
  11533. </form>
  11534. </div>
  11535. </div>
  11536. <!-- ================= MODAL: ADD / EDIT DHCP OPTION ================= -->
  11537. <div id="dhcp-option-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11538. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-in">
  11539. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11540. <div class="flex items-center gap-2">
  11541. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-cyan-500/20 text-cyan-300 border border-cyan-500/30 tracking-wide uppercase">DHCP Option</span>
  11542. <h3 id="dhcp-option-modal-title" class="text-sm font-bold text-white">Add DHCP Option</h3>
  11543. </div>
  11544. <button onclick="closeDHCPOptionModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11545. </div>
  11546. <form onsubmit="submitSaveDHCPOption(event)" class="p-6 space-y-4 text-xs font-sans">
  11547. <input type="hidden" id="dhcp-option-modal-id">
  11548. <div>
  11549. <label class="block font-semibold text-slate-300 mb-1.5">Option Name *</label>
  11550. <input type="text" id="dhcp-option-modal-name" placeholder="unifi-inform" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  11551. </div>
  11552. <div>
  11553. <label class="block font-semibold text-slate-300 mb-1.5">Option Code (1 - 254) *</label>
  11554. <input type="number" id="dhcp-option-modal-code" min="1" max="254" placeholder="43" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  11555. <span class="text-[10px] text-slate-400">e.g. 42 (NTP), 43 (Vendor), 66 (TFTP), 67 (Bootfile), 121 (Routes), 150 (VoIP)</span>
  11556. </div>
  11557. <div>
  11558. <label class="block font-semibold text-slate-300 mb-1.5">Option Value *</label>
  11559. <input type="text" id="dhcp-option-modal-value" placeholder="01:04:c0:a8:58:0a or 192.168.1.10" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-cyan-500">
  11560. <span class="text-[10px] text-slate-400">Format: IP address, comma-separated list, text in quotes, or raw colon-separated hex</span>
  11561. </div>
  11562. <div>
  11563. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11564. <input type="text" id="dhcp-option-modal-comment" placeholder="UniFi Network Controller inform IP" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-cyan-500">
  11565. </div>
  11566. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11567. <button type="button" onclick="closeDHCPOptionModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11568. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg shadow-cyan-500/20">Save Option</button>
  11569. </div>
  11570. </form>
  11571. </div>
  11572. </div>
  11573. <!-- ================= MODAL: QUICK DHCP SETUP WIZARD ================= -->
  11574. <div id="dhcp-setup-wizard-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11575. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11576. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11577. <div class="flex items-center gap-2">
  11578. <span class="px-2 py-0.5 rounded text-[10px] font-mono bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 font-semibold">DHCP Setup</span>
  11579. <h3 class="text-sm font-bold text-white">DHCP Server Setup Wizard</h3>
  11580. </div>
  11581. <button onclick="closeDHCPServerSetupModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11582. </div>
  11583. <form onsubmit="submitDHCPServerSetupWizard(event)" class="p-6 space-y-4 text-xs font-sans">
  11584. <p class="text-slate-400 text-xs">
  11585. Select an interface to automatically configure an enterprise DHCP Server, network subnet, IP address pool, default gateway, and DNS.
  11586. </p>
  11587. <div>
  11588. <label class="block font-semibold text-slate-300 mb-1.5">DHCP Server Interface *</label>
  11589. <select id="dhcp-wizard-iface" onchange="onDHCPWizardIfaceChange()" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11590. <option value="">Select interface...</option>
  11591. </select>
  11592. </div>
  11593. <div class="grid grid-cols-2 gap-4">
  11594. <div>
  11595. <label class="block font-semibold text-slate-300 mb-1.5">DHCP Address Space</label>
  11596. <input type="text" id="dhcp-wizard-space" placeholder="192.168.88.0/24" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11597. </div>
  11598. <div>
  11599. <label class="block font-semibold text-slate-300 mb-1.5">Gateway for DHCP Network</label>
  11600. <input type="text" id="dhcp-wizard-gateway" placeholder="192.168.88.1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11601. </div>
  11602. </div>
  11603. <div>
  11604. <label class="block font-semibold text-slate-300 mb-1.5">Addresses to Give Out (Address Pool)</label>
  11605. <input type="text" id="dhcp-wizard-pool" placeholder="192.168.88.10-192.168.88.254" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11606. </div>
  11607. <div class="grid grid-cols-2 gap-4">
  11608. <div>
  11609. <label class="block font-semibold text-slate-300 mb-1.5">DNS Servers</label>
  11610. <input type="text" id="dhcp-wizard-dns" placeholder="192.168.88.1, 1.1.1.1" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11611. </div>
  11612. <div>
  11613. <label class="block font-semibold text-slate-300 mb-1.5">Lease Time</label>
  11614. <input type="text" id="dhcp-wizard-leasetime" value="12h" placeholder="12h" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11615. </div>
  11616. </div>
  11617. <div class="grid grid-cols-2 gap-4 pt-1">
  11618. <div>
  11619. <label class="block font-semibold text-slate-300 mb-1">NTP Server (Optional)</label>
  11620. <input type="text" id="dhcp-wizard-ntp" placeholder="192.168.88.1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11621. </div>
  11622. <div>
  11623. <label class="block font-semibold text-slate-300 mb-1">TFTP Server / PXE Boot (Optional)</label>
  11624. <input type="text" id="dhcp-wizard-tftp" placeholder="192.168.88.50" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11625. </div>
  11626. </div>
  11627. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11628. <button type="button" onclick="closeDHCPServerSetupModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11629. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">Finish Setup</button>
  11630. </div>
  11631. </form>
  11632. </div>
  11633. </div>
  11634. <!-- ================= MODAL: ADD / EDIT BRIDGE ================= -->
  11635. <div id="bridge-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11636. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11637. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11638. <h3 id="bridge-modal-title" class="text-sm font-bold text-white">Add Bridge Interface</h3>
  11639. <button onclick="closeBridgeModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11640. </div>
  11641. <form onsubmit="submitSaveBridge(event)" class="p-6 space-y-4 text-xs">
  11642. <input type="hidden" id="bridge-modal-id">
  11643. <div class="grid grid-cols-2 gap-4">
  11644. <div>
  11645. <label class="block font-semibold text-slate-300 mb-1.5">Bridge Name *</label>
  11646. <input type="text" id="bridge-modal-name" required placeholder="e.g. bridge1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11647. </div>
  11648. <div>
  11649. <label class="block font-semibold text-slate-300 mb-1.5">MTU</label>
  11650. <input type="number" id="bridge-modal-mtu" value="1500" min="68" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11651. </div>
  11652. </div>
  11653. <div>
  11654. <label class="block font-semibold text-slate-300 mb-1.5">Admin MAC Address (Optional)</label>
  11655. <input type="text" id="bridge-modal-mac" placeholder="Leave blank for auto MAC" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11656. </div>
  11657. <div class="grid grid-cols-2 gap-4">
  11658. <div>
  11659. <label class="block font-semibold text-slate-300 mb-1.5">STP Protocol</label>
  11660. <select id="bridge-modal-stp" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11661. <option value="rstp">RSTP (requires mstpd)</option>
  11662. <option value="stp" selected>STP (IEEE 802.1D)</option>
  11663. <option value="none">none (Disable STP)</option>
  11664. </select>
  11665. </div>
  11666. <div>
  11667. <label class="block font-semibold text-slate-300 mb-1.5">STP Priority</label>
  11668. <input type="number" id="bridge-modal-priority" value="32768" min="0" max="65535" step="4096" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11669. </div>
  11670. </div>
  11671. <!-- VLAN Filtering Box -->
  11672. <div class="p-3.5 bg-slate-950/60 border border-slate-800 rounded-xl space-y-3">
  11673. <label class="flex items-center space-x-2.5 cursor-pointer">
  11674. <input type="checkbox" id="bridge-modal-vlanfiltering" onchange="toggleBridgeModalVlanFiltering()" class="rounded bg-slate-800 border-slate-700 text-sky-500">
  11675. <span class="font-semibold text-white">VLAN Filtering (802.1Q Aware)</span>
  11676. </label>
  11677. <div id="bridge-modal-pvid-box" class="grid grid-cols-2 gap-3 pl-6">
  11678. <div>
  11679. <label class="block text-[11px] text-slate-400 mb-1">Default PVID</label>
  11680. <input type="number" id="bridge-modal-pvid" value="1" min="1" max="4094" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-2.5 py-1 text-white font-mono text-xs">
  11681. </div>
  11682. <div class="flex items-center pt-4">
  11683. <label class="flex items-center space-x-2 text-slate-400 cursor-pointer text-xs">
  11684. <input type="checkbox" id="bridge-modal-fastforward" checked class="rounded bg-slate-800 border-slate-700 text-sky-500">
  11685. <span>Fast Forward</span>
  11686. </label>
  11687. </div>
  11688. </div>
  11689. </div>
  11690. <div>
  11691. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11692. <input type="text" id="bridge-modal-comment" placeholder="e.g. LAN Bridge with EoIP" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11693. </div>
  11694. <div class="pt-1">
  11695. <label class="flex items-center space-x-2 cursor-pointer">
  11696. <input type="checkbox" id="bridge-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  11697. <span class="font-medium text-slate-300">Disabled (X)</span>
  11698. </label>
  11699. </div>
  11700. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11701. <button type="button" onclick="closeBridgeModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11702. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-500/20">Save Bridge</button>
  11703. </div>
  11704. </form>
  11705. </div>
  11706. </div>
  11707. <!-- ================= MODAL: ADD / EDIT BRIDGE PORT ================= -->
  11708. <div id="bridge-port-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11709. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11710. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11711. <h3 id="bridge-port-modal-title" class="text-sm font-bold text-white">Add Port to Bridge</h3>
  11712. <button onclick="closeBridgePortModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11713. </div>
  11714. <form onsubmit="submitSaveBridgePort(event)" class="p-6 space-y-4 text-xs">
  11715. <input type="hidden" id="bridge-port-modal-id">
  11716. <div class="grid grid-cols-2 gap-4">
  11717. <div>
  11718. <label class="block font-semibold text-slate-300 mb-1.5">Target Bridge *</label>
  11719. <select id="bridge-port-modal-bridge" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  11720. </div>
  11721. <div>
  11722. <label class="block font-semibold text-slate-300 mb-1.5">Interface *</label>
  11723. <select id="bridge-port-modal-interface" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  11724. </div>
  11725. </div>
  11726. <div class="grid grid-cols-3 gap-3">
  11727. <div>
  11728. <label class="block font-semibold text-slate-300 mb-1.5">PVID</label>
  11729. <input type="number" id="bridge-port-modal-pvid" value="1" min="1" max="4094" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11730. </div>
  11731. <div>
  11732. <label class="block font-semibold text-slate-300 mb-1.5">Path Cost</label>
  11733. <input type="number" id="bridge-port-modal-pathcost" value="10" min="1" max="65535" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11734. </div>
  11735. <div>
  11736. <label class="block font-semibold text-slate-300 mb-1.5">Horizon (Split)</label>
  11737. <input type="number" id="bridge-port-modal-horizon" value="0" min="0" max="65535" title="0=none. Ports with identical horizon > 0 cannot communicate (split-horizon isolation for EoIP / VPLS)" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11738. </div>
  11739. </div>
  11740. <div class="grid grid-cols-2 gap-4">
  11741. <div>
  11742. <label class="block font-semibold text-slate-300 mb-1.5">Frame Types</label>
  11743. <select id="bridge-port-modal-frametypes" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11744. <option value="admit-all" selected>admit all</option>
  11745. <option value="admit-only-vlan-tagged">admit only VLAN tagged</option>
  11746. <option value="admit-only-untagged-and-priority-tagged">admit only untagged</option>
  11747. </select>
  11748. </div>
  11749. <div>
  11750. <label class="block font-semibold text-slate-300 mb-1.5">Priority</label>
  11751. <input type="number" id="bridge-port-modal-priority" value="128" min="0" max="240" step="16" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11752. </div>
  11753. </div>
  11754. <div class="grid grid-cols-2 gap-3 p-3 bg-slate-950/60 border border-slate-800 rounded-xl">
  11755. <label class="flex items-center space-x-2 cursor-pointer">
  11756. <input type="checkbox" id="bridge-port-modal-learn" checked class="rounded bg-slate-800 border-slate-700 text-sky-500">
  11757. <span class="text-white font-semibold">MAC Learning</span>
  11758. </label>
  11759. <label class="flex items-center space-x-2 cursor-pointer">
  11760. <input type="checkbox" id="bridge-port-modal-ingressfiltering" disabled class="rounded bg-slate-800 border-slate-700 text-sky-500">
  11761. <span class="text-white font-semibold">Ingress filtering (controlled by bridge VLAN filtering)</span>
  11762. </label>
  11763. </div>
  11764. <div>
  11765. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11766. <input type="text" id="bridge-port-modal-comment" placeholder="e.g. Uplink to switch" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11767. </div>
  11768. <div class="pt-1">
  11769. <label class="flex items-center space-x-2 cursor-pointer">
  11770. <input type="checkbox" id="bridge-port-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  11771. <span class="font-medium text-slate-300">Disabled (X)</span>
  11772. </label>
  11773. </div>
  11774. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11775. <button type="button" onclick="closeBridgePortModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11776. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-500/20">Save Port</button>
  11777. </div>
  11778. </form>
  11779. </div>
  11780. </div>
  11781. <!-- ================= MODAL: ADD / EDIT BRIDGE VLAN ================= -->
  11782. <div id="bridge-vlan-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11783. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11784. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11785. <h3 id="bridge-vlan-modal-title" class="text-sm font-bold text-white">Add Bridge VLAN Entry</h3>
  11786. <button onclick="closeBridgeVlanModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11787. </div>
  11788. <form onsubmit="submitSaveBridgeVLAN(event)" class="p-6 space-y-4 text-xs">
  11789. <input type="hidden" id="bridge-vlan-modal-id">
  11790. <div class="grid grid-cols-2 gap-4">
  11791. <div>
  11792. <label class="block font-semibold text-slate-300 mb-1.5">Target Bridge *</label>
  11793. <select id="bridge-vlan-modal-bridge" onchange="updateBridgeVlanPortCheckboxes()" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono"></select>
  11794. </div>
  11795. <div>
  11796. <label class="block font-semibold text-slate-300 mb-1.5">VLAN IDs *</label>
  11797. <input type="text" id="bridge-vlan-modal-vlanids" required placeholder="e.g. 10 or 10,20,30 or 10-20" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11798. </div>
  11799. </div>
  11800. <div>
  11801. <label class="block font-semibold text-slate-300 mb-1.5">Tagged Member Ports (Trunk)</label>
  11802. <div id="bridge-vlan-tagged-ports" class="max-h-36 overflow-y-auto p-2.5 bg-slate-950/60 border border-slate-800 rounded-xl space-y-1 font-mono text-slate-300">
  11803. <span class="text-slate-500 text-xs font-sans">Select a bridge above to view member ports</span>
  11804. </div>
  11805. </div>
  11806. <div>
  11807. <label class="block font-semibold text-slate-300 mb-1.5">Untagged Member Ports (Access)</label>
  11808. <div id="bridge-vlan-untagged-ports" class="max-h-36 overflow-y-auto p-2.5 bg-slate-950/60 border border-slate-800 rounded-xl space-y-1 font-mono text-slate-300">
  11809. <span class="text-slate-500 text-xs font-sans">Select a bridge above to view member ports</span>
  11810. </div>
  11811. </div>
  11812. <div>
  11813. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11814. <input type="text" id="bridge-vlan-modal-comment" placeholder="e.g. Office & Guest VLANs" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white">
  11815. </div>
  11816. <div class="pt-1">
  11817. <label class="flex items-center space-x-2 cursor-pointer">
  11818. <input type="checkbox" id="bridge-vlan-modal-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-500">
  11819. <span class="font-medium text-slate-300">Disabled (X)</span>
  11820. </label>
  11821. </div>
  11822. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11823. <button type="button" onclick="closeBridgeVlanModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11824. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-500/20">Save VLAN Entry</button>
  11825. </div>
  11826. </form>
  11827. </div>
  11828. </div>
  11829. <!-- ================= MODAL: ADD IP ADDRESS TO BRIDGE ================= -->
  11830. <div id="bridge-ip-modal" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11831. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-sm shadow-2xl overflow-hidden animate-in">
  11832. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11833. <div>
  11834. <h3 class="text-sm font-bold text-white">Assign IP Address to Bridge</h3>
  11835. <span id="bridge-ip-modal-sub" class="text-[11px] font-mono text-sky-400">bridge1</span>
  11836. </div>
  11837. <button onclick="closeBridgeIPModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11838. </div>
  11839. <form onsubmit="submitAddBridgeIP(event)" class="p-6 space-y-4 text-xs">
  11840. <input type="hidden" id="bridge-ip-modal-id">
  11841. <div>
  11842. <label class="block font-semibold text-slate-300 mb-1.5">IPv4 / Subnet (CIDR) *</label>
  11843. <input type="text" id="bridge-ip-modal-cidr" required placeholder="e.g. 192.168.88.1/24" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono">
  11844. </div>
  11845. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11846. <button type="button" onclick="closeBridgeIPModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11847. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-sky-600 hover:bg-sky-500 text-white shadow-lg shadow-sky-500/20">Assign IP</button>
  11848. </div>
  11849. </form>
  11850. </div>
  11851. </div>
  11852. <!-- ================= MODAL: ADD / EDIT VRRP INSTANCE ================= -->
  11853. <div id="modal-vrrp-instance" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11854. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden animate-in">
  11855. <div class="px-6 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/40">
  11856. <div class="flex items-center gap-2">
  11857. <span class="px-2.5 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 tracking-wide uppercase">VRRP</span>
  11858. <h3 id="modal-vrrp-title" class="text-sm font-bold text-white">Add VRRP Instance</h3>
  11859. </div>
  11860. <button onclick="closeVRRPModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11861. </div>
  11862. <form onsubmit="submitSaveVRRP(event)" class="p-6 space-y-4 text-xs font-sans">
  11863. <input type="hidden" id="modal-vrrp-id">
  11864. <div class="grid grid-cols-2 gap-4">
  11865. <div>
  11866. <label class="block font-semibold text-slate-300 mb-1.5">Instance Name *</label>
  11867. <input type="text" id="modal-vrrp-name" placeholder="vrrp-lan" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11868. </div>
  11869. <div>
  11870. <label class="block font-semibold text-slate-300 mb-1.5">Interface *</label>
  11871. <select id="modal-vrrp-iface" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11872. <option value="">Select interface...</option>
  11873. </select>
  11874. </div>
  11875. </div>
  11876. <div class="grid grid-cols-2 gap-4">
  11877. <div>
  11878. <label class="block font-semibold text-slate-300 mb-1.5">Virtual Router ID (VRID 1-255) *</label>
  11879. <input type="number" id="modal-vrrp-vrid" min="1" max="255" value="10" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11880. <span class="text-[10px] text-slate-400">Must match on Master & Backup routers</span>
  11881. </div>
  11882. <div>
  11883. <label class="block font-semibold text-slate-300 mb-1.5">Priority (1-255) *</label>
  11884. <input type="number" id="modal-vrrp-priority" min="1" max="255" value="100" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11885. <span class="text-[10px] text-slate-400">Higher priority wins Master (e.g. 200 vs 100)</span>
  11886. </div>
  11887. </div>
  11888. <div>
  11889. <label class="block font-semibold text-slate-300 mb-1.5">Virtual IP Address (VIP) *</label>
  11890. <input type="text" id="modal-vrrp-vip" placeholder="192.168.88.254/24" required class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11891. <span class="text-[10px] text-slate-400">Shared gateway IP address handed over during failover (CIDR)</span>
  11892. </div>
  11893. <div class="grid grid-cols-2 gap-4">
  11894. <div>
  11895. <label class="block font-semibold text-slate-300 mb-1.5">Protocol Version</label>
  11896. <select id="modal-vrrp-version" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11897. <option value="3" selected>VRRPv3 (RFC 5798 - Recommended)</option>
  11898. <option value="2">VRRPv2 (RFC 3768 - Legacy)</option>
  11899. </select>
  11900. </div>
  11901. <div>
  11902. <label class="block font-semibold text-slate-300 mb-1.5">Advertisement Interval (sec)</label>
  11903. <input type="number" id="modal-vrrp-interval" min="1" max="255" value="1" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11904. </div>
  11905. </div>
  11906. <div class="grid grid-cols-2 gap-4">
  11907. <div>
  11908. <label class="block font-semibold text-slate-300 mb-1.5">Authentication (Optional)</label>
  11909. <select id="modal-vrrp-authtype" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11910. <option value="none" selected>None</option>
  11911. <option value="pass">Simple Password (PASS)</option>
  11912. </select>
  11913. </div>
  11914. <div>
  11915. <label class="block font-semibold text-slate-300 mb-1.5">Auth Password</label>
  11916. <input type="password" id="modal-vrrp-authpass" placeholder="Secret password" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white font-mono focus:outline-none focus:border-emerald-500">
  11917. </div>
  11918. </div>
  11919. <div>
  11920. <label class="block font-semibold text-slate-300 mb-1.5">Comment</label>
  11921. <input type="text" id="modal-vrrp-comment" placeholder="LAN Gateway High Availability" class="w-full bg-slate-800 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-emerald-500">
  11922. </div>
  11923. <div class="flex items-center justify-between pt-2 border-t border-slate-800/80">
  11924. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  11925. <input type="checkbox" id="modal-vrrp-preempt" checked class="rounded bg-slate-800 border-slate-700 text-emerald-600">
  11926. <span>Preemption Mode (Master re-takes role when recovered)</span>
  11927. </label>
  11928. <label class="flex items-center space-x-2 text-slate-300 cursor-pointer">
  11929. <input type="checkbox" id="modal-vrrp-disabled" class="rounded bg-slate-800 border-slate-700 text-rose-600">
  11930. <span>Disabled (X)</span>
  11931. </label>
  11932. </div>
  11933. <div class="pt-4 border-t border-slate-800 flex justify-end space-x-3">
  11934. <button type="button" onclick="closeVRRPModal()" class="px-4 py-2 rounded-lg text-slate-400 hover:bg-slate-800">Cancel</button>
  11935. <button type="submit" class="px-5 py-2 rounded-lg font-semibold bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-500/20">Save VRRP</button>
  11936. </div>
  11937. </form>
  11938. </div>
  11939. </div>
  11940. <!-- ================= MODAL: BANDWIDTH TEST SERVER SETTINGS ================= -->
  11941. <div id="modal-bwtest-server" class="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center hidden p-4">
  11942. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md overflow-hidden shadow-2xl">
  11943. <div class="p-5 border-b border-slate-800 flex items-center justify-between">
  11944. <h3 class="text-base font-bold text-white flex items-center gap-2">
  11945. <span>⚙ Bandwidth Test Server Settings</span>
  11946. </h3>
  11947. <button type="button" onclick="closeBwTestServerModal()" class="text-slate-400 hover:text-white transition">✕</button>
  11948. </div>
  11949. <form onsubmit="submitBwTestServerSettings(event)" class="p-6 space-y-4">
  11950. <div class="bg-slate-950/80 border border-slate-800/80 rounded-xl p-4 space-y-3">
  11951. <label class="flex items-center justify-between cursor-pointer">
  11952. <div>
  11953. <span class="text-xs font-bold text-white block">Enable Bandwidth Test Server</span>
  11954. <span class="text-[11px] text-slate-400 block mt-0.5">Allows remote routers and clients to run bandwidth tests against this appliance</span>
  11955. </div>
  11956. <input type="checkbox" id="bwtest-server-enable-toggle" checked class="w-4 h-4 rounded text-emerald-500 bg-slate-800 border-slate-700">
  11957. </label>
  11958. </div>
  11959. <div>
  11960. <label class="block text-xs font-semibold text-slate-300 mb-1">Server Listen Port</label>
  11961. <input type="number" id="bwtest-server-port-input" value="2000" min="1" max="65535" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-white font-mono text-xs focus:outline-none focus:border-emerald-500">
  11962. <p class="text-[10px] text-slate-500 mt-1">Default port is 2000 (binds TCP control channel and UDP data port).</p>
  11963. </div>
  11964. <div class="bg-slate-950/50 p-3 rounded-lg border border-slate-800/60 font-mono text-[11px] space-y-1 text-slate-400">
  11965. <div class="flex justify-between"><span>Active Sessions:</span> <span id="bwtest-modal-active-sessions" class="text-white font-bold">0</span></div>
  11966. <div class="flex justify-between"><span>Total Served:</span> <span id="bwtest-modal-total-served" class="text-white font-bold">0</span></div>
  11967. <div class="flex justify-between"><span>Processed Data:</span> <span id="bwtest-modal-total-bytes" class="text-white font-bold">0 B</span></div>
  11968. </div>
  11969. <div class="flex justify-end gap-3 pt-3">
  11970. <button type="button" onclick="closeBwTestServerModal()" class="px-4 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs font-semibold transition">Cancel</button>
  11971. <button type="submit" class="px-5 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-white text-xs font-bold transition shadow-lg shadow-emerald-500/20">Save Settings</button>
  11972. </div>
  11973. </form>
  11974. </div>
  11975. </div>
  11976. <!-- ================= MODAL: EDIT SERVICE PORT ================= -->
  11977. <div id="modal-service-edit" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  11978. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-sm shadow-2xl overflow-hidden">
  11979. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  11980. <h3 id="modal-service-title" class="text-sm font-bold text-white flex items-center gap-2">
  11981. <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/></svg>
  11982. <span>Edit Service Port</span>
  11983. </h3>
  11984. <button type="button" onclick="closeEditServiceModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  11985. </div>
  11986. <form id="form-service-edit" onsubmit="submitSaveServicePort(event)" class="p-5 space-y-4">
  11987. <input type="hidden" id="modal-service-name">
  11988. <input type="hidden" id="modal-service-disabled">
  11989. <div>
  11990. <label class="block text-xs font-semibold text-slate-300 mb-1">Service</label>
  11991. <div id="modal-service-display-name" class="font-mono text-xs font-bold text-cyan-400 bg-slate-950/80 border border-slate-800 rounded-lg px-3 py-2 uppercase">SSH</div>
  11992. </div>
  11993. <div>
  11994. <label class="block text-xs font-semibold text-slate-300 mb-1">Listening Port (1 - 65535) *</label>
  11995. <input type="number" id="modal-service-port" min="1" max="65535" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  11996. <p id="modal-service-port-hint" class="text-[10px] text-slate-500 mt-1"></p>
  11997. </div>
  11998. <div id="modal-service-cert-group" class="hidden">
  11999. <label class="block text-xs font-semibold text-slate-300 mb-1">SSL/TLS Certificate *</label>
  12000. <select id="modal-service-cert-select" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  12001. <option value="">-- No Certificate Selected --</option>
  12002. </select>
  12003. <p class="text-[10px] text-slate-500 mt-1">Select an issued or imported certificate from the Certificates section.</p>
  12004. </div>
  12005. <div id="modal-service-warning" class="hidden p-3 rounded-lg bg-amber-500/10 border border-amber-500/30 text-amber-300 text-xs">
  12006. ⚠️ <strong>Προσοχή:</strong> Η αλλαγή της πόρτας του Web UI θα αλλάξει τη διεύθυνση URL με την οποία συνδέεσαι.
  12007. </div>
  12008. <div class="pt-2 flex items-center justify-end space-x-2">
  12009. <button type="button" onclick="closeEditServiceModal()" class="px-3.5 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Cancel</button>
  12010. <button type="submit" class="px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-xs font-semibold text-white shadow transition">Save Settings</button>
  12011. </div>
  12012. </form>
  12013. </div>
  12014. </div>
  12015. <!-- ================= MODAL: CREATE CA ================= -->
  12016. <div id="modal-create-ca" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12017. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden">
  12018. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12019. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  12020. <span class="text-blue-400">🏛</span>
  12021. <span>Create Certificate Authority (CA)</span>
  12022. </h3>
  12023. <button type="button" onclick="closeCreateCAModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12024. </div>
  12025. <form id="form-create-ca" onsubmit="submitCreateCA(event)" class="p-5 space-y-4">
  12026. <div>
  12027. <label class="block text-xs font-semibold text-slate-300 mb-1">CA Identifier / Name *</label>
  12028. <input type="text" id="ca-name" placeholder="e.g. root-ca or debtik-ca" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  12029. </div>
  12030. <div>
  12031. <label class="block text-xs font-semibold text-slate-300 mb-1">Common Name (CN) *</label>
  12032. <input type="text" id="ca-cn" placeholder="e.g. DebTik Root Certificate Authority" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  12033. </div>
  12034. <div class="grid grid-cols-2 gap-3">
  12035. <div>
  12036. <label class="block text-xs font-semibold text-slate-300 mb-1">Organization</label>
  12037. <input type="text" id="ca-org" value="DebTik Network Appliance" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  12038. </div>
  12039. <div>
  12040. <label class="block text-xs font-semibold text-slate-300 mb-1">Country</label>
  12041. <input type="text" id="ca-country" value="GR" maxlength="2" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  12042. </div>
  12043. </div>
  12044. <div class="grid grid-cols-2 gap-3">
  12045. <div>
  12046. <label class="block text-xs font-semibold text-slate-300 mb-1">Validity (Days)</label>
  12047. <input type="number" id="ca-days" value="3650" min="30" max="36500" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  12048. </div>
  12049. <div>
  12050. <label class="block text-xs font-semibold text-slate-300 mb-1">Key Algorithm</label>
  12051. <select id="ca-key-type" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-blue-500">
  12052. <option value="RSA-2048">RSA 2048-bit (Standard)</option>
  12053. <option value="RSA-4096">RSA 4096-bit (High Security)</option>
  12054. <option value="ECDSA-P256">ECDSA P-256 (Fast/Modern)</option>
  12055. </select>
  12056. </div>
  12057. </div>
  12058. <div class="pt-2 flex items-center justify-end space-x-2">
  12059. <button type="button" onclick="closeCreateCAModal()" class="px-3.5 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Cancel</button>
  12060. <button type="submit" class="px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-500 text-xs font-semibold text-white shadow transition">Generate CA</button>
  12061. </div>
  12062. </form>
  12063. </div>
  12064. </div>
  12065. <!-- ================= MODAL: ISSUE CERTIFICATE ================= -->
  12066. <div id="modal-create-cert" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12067. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden">
  12068. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12069. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  12070. <span class="text-cyan-400">📜</span>
  12071. <span>Issue TLS Certificate</span>
  12072. </h3>
  12073. <button type="button" onclick="closeCreateCertModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12074. </div>
  12075. <form id="form-create-cert" onsubmit="submitCreateCert(event)" class="p-5 space-y-4">
  12076. <div>
  12077. <label class="block text-xs font-semibold text-slate-300 mb-1">Certificate Identifier / Name *</label>
  12078. <input type="text" id="cert-name" placeholder="e.g. web-cert or router-lan" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  12079. </div>
  12080. <div>
  12081. <label class="block text-xs font-semibold text-slate-300 mb-1">Common Name (CN) * (Hostname, Domain, or IP)</label>
  12082. <input type="text" id="cert-cn" placeholder="e.g. router.lan or 192.168.1.1" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  12083. </div>
  12084. <div>
  12085. <label class="block text-xs font-semibold text-slate-300 mb-1">Sign with Certificate Authority (CA)</label>
  12086. <select id="cert-ca-select" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  12087. <option value="">[ Self-Signed Certificate ]</option>
  12088. </select>
  12089. </div>
  12090. <div>
  12091. <label class="block text-xs font-semibold text-slate-300 mb-1">Subject Alternative Names (SANs) - comma separated</label>
  12092. <input type="text" id="cert-sans" placeholder="e.g. 192.168.1.1, router.local, router.lan, 127.0.0.1" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  12093. <p class="text-[10px] text-slate-500 mt-1">Browser validation requires matching SANs for all IPs or domains used to access the router.</p>
  12094. </div>
  12095. <div class="grid grid-cols-2 gap-3">
  12096. <div>
  12097. <label class="block text-xs font-semibold text-slate-300 mb-1">Validity (Days)</label>
  12098. <input type="number" id="cert-days" value="730" min="1" max="7300" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  12099. </div>
  12100. <div>
  12101. <label class="block text-xs font-semibold text-slate-300 mb-1">Key Algorithm</label>
  12102. <select id="cert-key-type" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  12103. <option value="RSA-2048">RSA 2048-bit (Standard)</option>
  12104. <option value="RSA-4096">RSA 4096-bit (High Security)</option>
  12105. <option value="ECDSA-P256">ECDSA P-256 (Fast/Modern)</option>
  12106. </select>
  12107. </div>
  12108. </div>
  12109. <div class="pt-2 flex items-center justify-end space-x-2">
  12110. <button type="button" onclick="closeCreateCertModal()" class="px-3.5 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Cancel</button>
  12111. <button type="submit" class="px-4 py-2 rounded-lg bg-cyan-600 hover:bg-cyan-500 text-xs font-semibold text-white shadow transition">Issue Certificate</button>
  12112. </div>
  12113. </form>
  12114. </div>
  12115. </div>
  12116. <!-- ================= MODAL: LET'S ENCRYPT ================= -->
  12117. <div id="modal-letsencrypt" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12118. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden">
  12119. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12120. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  12121. <span class="text-emerald-400">🔒</span>
  12122. <span>Let's Encrypt Automated Certificate</span>
  12123. </h3>
  12124. <button type="button" onclick="closeLetsEncryptModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12125. </div>
  12126. <form id="form-letsencrypt" onsubmit="submitLetsEncrypt(event)" class="p-5 space-y-4">
  12127. <div class="p-3 bg-emerald-500/10 border border-emerald-500/30 rounded-xl text-emerald-300 text-xs space-y-1">
  12128. <div class="font-bold">Automated HTTP-01 Validation</div>
  12129. <div class="text-[11px] text-emerald-300/80">To issue a trusted Let's Encrypt certificate, your domain name must publicly point to this router on port 80. The router will automatically answer the ACME challenge.</div>
  12130. </div>
  12131. <div>
  12132. <label class="block text-xs font-semibold text-slate-300 mb-1">Domain Name (FQDN) *</label>
  12133. <input type="text" id="le-domain" placeholder="e.g. router.mydomain.com" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-emerald-500">
  12134. </div>
  12135. <div>
  12136. <label class="block text-xs font-semibold text-slate-300 mb-1">Contact Email *</label>
  12137. <input type="email" id="le-email" placeholder="e.g. admin@mydomain.com" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-emerald-500">
  12138. </div>
  12139. <div>
  12140. <label class="block text-xs font-semibold text-slate-300 mb-1">Certificate Name / Alias</label>
  12141. <input type="text" id="le-name" placeholder="Leave blank to use domain name" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-emerald-500">
  12142. </div>
  12143. <div class="flex items-center gap-2 pt-1">
  12144. <input type="checkbox" id="le-staging" class="rounded bg-slate-950 border-slate-800 text-emerald-500">
  12145. <label for="le-staging" class="text-xs text-slate-300 cursor-pointer">Use Let's Encrypt Staging API (for testing without rate limits)</label>
  12146. </div>
  12147. <div class="pt-2 flex items-center justify-end space-x-2">
  12148. <button type="button" onclick="closeLetsEncryptModal()" class="px-3.5 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Cancel</button>
  12149. <button type="submit" id="btn-submit-letsencrypt" class="px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-500 text-xs font-semibold text-white shadow transition flex items-center gap-2">
  12150. <span>Request Certificate</span>
  12151. </button>
  12152. </div>
  12153. </form>
  12154. </div>
  12155. </div>
  12156. <!-- ================= MODAL: IMPORT CERTIFICATE ================= -->
  12157. <div id="modal-import-cert" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12158. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden">
  12159. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12160. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  12161. <span class="text-slate-400">📥</span>
  12162. <span>Import PEM Certificate & Private Key</span>
  12163. </h3>
  12164. <button type="button" onclick="closeImportCertModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12165. </div>
  12166. <form id="form-import-cert" onsubmit="submitImportCert(event)" class="p-5 space-y-4">
  12167. <div>
  12168. <label class="block text-xs font-semibold text-slate-300 mb-1">Certificate Identifier / Name *</label>
  12169. <input type="text" id="import-cert-name" placeholder="e.g. wildcard-cert" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-cyan-500">
  12170. </div>
  12171. <div>
  12172. <label class="block text-xs font-semibold text-slate-300 mb-1">Certificate (PEM Format) *</label>
  12173. <textarea id="import-cert-pem" rows="5" placeholder="-----BEGIN CERTIFICATE-----&#10;...&#10;-----END CERTIFICATE-----" required class="w-full bg-slate-950 border border-slate-800 rounded-lg p-2.5 text-xs text-white font-mono focus:outline-none focus:border-cyan-500"></textarea>
  12174. </div>
  12175. <div>
  12176. <label class="block text-xs font-semibold text-slate-300 mb-1">Private Key (PEM Format) *</label>
  12177. <textarea id="import-key-pem" rows="5" placeholder="-----BEGIN PRIVATE KEY-----&#10;...&#10;-----END PRIVATE KEY-----" required class="w-full bg-slate-950 border border-slate-800 rounded-lg p-2.5 text-xs text-white font-mono focus:outline-none focus:border-cyan-500"></textarea>
  12178. </div>
  12179. <div class="pt-2 flex items-center justify-end space-x-2">
  12180. <button type="button" onclick="closeImportCertModal()" class="px-3.5 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Cancel</button>
  12181. <button type="submit" class="px-4 py-2 rounded-lg bg-cyan-600 hover:bg-cyan-500 text-xs font-semibold text-white shadow transition">Import Certificate</button>
  12182. </div>
  12183. </form>
  12184. </div>
  12185. </div>
  12186. <!-- ================= MODAL: VIEW / EXPORT CERTIFICATE ================= -->
  12187. <div id="modal-view-cert" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12188. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-xl shadow-2xl overflow-hidden">
  12189. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12190. <h3 id="view-cert-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  12191. <span>Certificate Details</span>
  12192. </h3>
  12193. <button type="button" onclick="closeViewCertModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12194. </div>
  12195. <div class="p-5 space-y-4">
  12196. <div class="grid grid-cols-2 gap-3 text-xs font-mono bg-slate-950/60 p-3 rounded-xl border border-slate-800/80">
  12197. <div><span class="text-slate-500">Common Name:</span> <span id="view-cert-cn" class="text-white font-bold">-</span></div>
  12198. <div><span class="text-slate-500">Issuer:</span> <span id="view-cert-issuer" class="text-cyan-400">-</span></div>
  12199. <div><span class="text-slate-500">Type / Key:</span> <span id="view-cert-type" class="text-emerald-400">-</span></div>
  12200. <div><span class="text-slate-500">Valid Until:</span> <span id="view-cert-validto" class="text-white">-</span></div>
  12201. <div class="col-span-2 truncate"><span class="text-slate-500">SANs:</span> <span id="view-cert-sans" class="text-slate-300">-</span></div>
  12202. <div class="col-span-2 truncate"><span class="text-slate-500">SHA256 Fingerprint:</span> <span id="view-cert-fp" class="text-amber-400 text-[10px]">-</span></div>
  12203. </div>
  12204. <div>
  12205. <div class="flex items-center justify-between mb-1">
  12206. <span class="text-xs font-semibold text-slate-300">Certificate (PEM)</span>
  12207. <button onclick="copyCertPEM()" class="text-[11px] text-cyan-400 hover:text-cyan-300 font-mono">Copy Cert</button>
  12208. </div>
  12209. <textarea id="view-cert-pem-text" readonly rows="6" class="w-full bg-slate-950 border border-slate-800 rounded-lg p-2 text-[11px] text-slate-300 font-mono"></textarea>
  12210. </div>
  12211. <div>
  12212. <div class="flex items-center justify-between mb-1">
  12213. <span class="text-xs font-semibold text-slate-300">Private Key (PEM)</span>
  12214. <button onclick="copyKeyPEM()" class="text-[11px] text-cyan-400 hover:text-cyan-300 font-mono">Copy Key</button>
  12215. </div>
  12216. <textarea id="view-key-pem-text" readonly rows="4" class="w-full bg-slate-950 border border-slate-800 rounded-lg p-2 text-[11px] text-slate-300 font-mono"></textarea>
  12217. </div>
  12218. <div class="flex justify-end pt-2">
  12219. <button type="button" onclick="closeViewCertModal()" class="px-4 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Close</button>
  12220. </div>
  12221. </div>
  12222. </div>
  12223. </div>
  12224. <!-- ================= MODAL: ADD / EDIT REVERSE PROXY RULE ================= -->
  12225. <div id="modal-proxy-rule" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12226. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-xl shadow-2xl overflow-hidden max-h-[90vh] flex flex-col">
  12227. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12228. <h3 id="proxy-rule-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  12229. <svg class="w-4 h-4 text-violet-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" /></svg>
  12230. <span>Add Reverse Proxy Rule</span>
  12231. </h3>
  12232. <button type="button" onclick="closeProxyRuleModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12233. </div>
  12234. <form id="proxy-rule-form" onsubmit="saveProxyRule(event)" class="p-5 space-y-4 overflow-y-auto flex-1">
  12235. <input type="hidden" id="proxy-rule-id">
  12236. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  12237. <div>
  12238. <label class="block text-xs font-semibold text-slate-300 mb-1">Rule Name *</label>
  12239. <input type="text" id="proxy-rule-name" placeholder="e.g. Nextcloud or WebApp" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12240. </div>
  12241. <div>
  12242. <label class="block text-xs font-semibold text-slate-300 mb-1">Protocol</label>
  12243. <select id="proxy-rule-proto" onchange="onProxyProtoChange()" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12244. <option value="both">HTTP & HTTPS (Both)</option>
  12245. <option value="https">HTTPS Only</option>
  12246. <option value="http">HTTP Only</option>
  12247. </select>
  12248. </div>
  12249. </div>
  12250. <div>
  12251. <label class="block text-xs font-semibold text-slate-300 mb-1">Inbound Domain / Hostname / IP</label>
  12252. <input type="text" id="proxy-rule-domains" placeholder="e.g. example.com, app.example.com, 192.168.1.100, or * for any" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12253. <p class="text-[10px] text-slate-500 mt-1">Leave empty or specify * to match all hostnames and IPs on the router.</p>
  12254. </div>
  12255. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  12256. <div>
  12257. <label class="block text-xs font-semibold text-slate-300 mb-1">Inbound Path Prefix *</label>
  12258. <input type="text" id="proxy-rule-path" value="/" required placeholder="e.g. /app1 or /" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12259. <p class="text-[10px] text-slate-500 mt-1">Subfolders (e.g. /app1/module1/attribute1) are automatically routed.</p>
  12260. </div>
  12261. <div>
  12262. <label class="block text-xs font-semibold text-slate-300 mb-1">SSL/TLS Certificate</label>
  12263. <select id="proxy-rule-cert" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12264. <option value="">(None / Use Default Cert)</option>
  12265. </select>
  12266. <p class="text-[10px] text-slate-500 mt-1">Loaded dynamically from Certificates module.</p>
  12267. </div>
  12268. </div>
  12269. <div>
  12270. <label class="block text-xs font-semibold text-slate-300 mb-1">Target Backend URL *</label>
  12271. <input type="text" id="proxy-rule-target" placeholder="e.g. http://10.0.0.5:8080 or http://example.com/app1" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12272. <p class="text-[10px] text-slate-500 mt-1">Can be internal IP or external domain, with or without subfolder.</p>
  12273. </div>
  12274. <div class="grid grid-cols-1 sm:grid-cols-2 gap-3 pt-1">
  12275. <label class="flex items-center gap-2 cursor-pointer bg-slate-950/60 border border-slate-800/80 p-2.5 rounded-xl">
  12276. <input type="checkbox" id="proxy-rule-strip-path" class="rounded bg-slate-800 border-slate-700 text-violet-600 focus:ring-0">
  12277. <div>
  12278. <span class="text-xs font-semibold text-slate-300 block">Strip Path Prefix</span>
  12279. <span class="text-[10px] text-slate-500">Strip inbound path prefix before sending to target</span>
  12280. </div>
  12281. </label>
  12282. <label class="flex items-center gap-2 cursor-pointer bg-slate-950/60 border border-slate-800/80 p-2.5 rounded-xl">
  12283. <input type="checkbox" id="proxy-rule-http-to-https" class="rounded bg-slate-800 border-slate-700 text-violet-600 focus:ring-0">
  12284. <div>
  12285. <span class="text-xs font-semibold text-slate-300 block">Force HTTPS</span>
  12286. <span class="text-[10px] text-slate-500">Redirect plain HTTP requests to HTTPS (301)</span>
  12287. </div>
  12288. </label>
  12289. <label class="flex items-center gap-2 cursor-pointer bg-slate-950/60 border border-slate-800/80 p-2.5 rounded-xl">
  12290. <input type="checkbox" id="proxy-rule-preserve-host" class="rounded bg-slate-800 border-slate-700 text-violet-600 focus:ring-0">
  12291. <div>
  12292. <span class="text-xs font-semibold text-slate-300 block">Preserve Host Header</span>
  12293. <span class="text-[10px] text-slate-500">Pass original client Host instead of target Host</span>
  12294. </div>
  12295. </label>
  12296. <label class="flex items-center gap-2 cursor-pointer bg-slate-950/60 border border-slate-800/80 p-2.5 rounded-xl">
  12297. <input type="checkbox" id="proxy-rule-insecure-tls" class="rounded bg-slate-800 border-slate-700 text-violet-600 focus:ring-0">
  12298. <div>
  12299. <span class="text-xs font-semibold text-slate-300 block">Insecure Backend TLS</span>
  12300. <span class="text-[10px] text-slate-500">Allow self-signed certificates on backend</span>
  12301. </div>
  12302. </label>
  12303. </div>
  12304. <div>
  12305. <label class="block text-xs font-semibold text-slate-300 mb-1">Description / Comment</label>
  12306. <input type="text" id="proxy-rule-comment" placeholder="Optional notes for this route" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white focus:outline-none focus:border-violet-500">
  12307. </div>
  12308. <div class="flex items-center gap-2 pt-1">
  12309. <input type="checkbox" id="proxy-rule-enabled" checked class="rounded bg-slate-800 border-slate-700 text-emerald-500 focus:ring-0">
  12310. <label for="proxy-rule-enabled" class="text-xs font-semibold text-slate-300 cursor-pointer">Enable this rule immediately</label>
  12311. </div>
  12312. <div class="flex justify-end gap-2 pt-3 border-t border-slate-800">
  12313. <button type="button" onclick="closeProxyRuleModal()" class="px-4 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Cancel</button>
  12314. <button type="submit" class="px-4 py-2 rounded-lg bg-violet-600 hover:bg-violet-500 text-xs font-semibold text-white shadow-lg shadow-violet-600/30 transition">Save Rule</button>
  12315. </div>
  12316. </form>
  12317. </div>
  12318. </div>
  12319. <!-- ================= MODAL: REVERSE PROXY GLOBAL CONFIG ================= -->
  12320. <div id="modal-proxy-config" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12321. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden">
  12322. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12323. <h3 class="text-sm font-bold text-white flex items-center gap-2">
  12324. <svg class="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
  12325. <span>Reverse Proxy Engine Settings</span>
  12326. </h3>
  12327. <button type="button" onclick="closeProxyConfigModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12328. </div>
  12329. <form id="proxy-config-form" onsubmit="saveProxyConfig(event)" class="p-5 space-y-4">
  12330. <div class="flex items-center gap-2 p-3 rounded-xl bg-slate-950/60 border border-slate-800">
  12331. <input type="checkbox" id="proxy-cfg-enabled" class="rounded bg-slate-800 border-slate-700 text-emerald-500 focus:ring-0">
  12332. <label for="proxy-cfg-enabled" class="text-xs font-semibold text-white cursor-pointer">
  12333. Master Switch: Enable Reverse Proxy Engine
  12334. </label>
  12335. </div>
  12336. <div class="grid grid-cols-2 gap-3">
  12337. <div>
  12338. <label class="block text-xs font-semibold text-slate-300 mb-1">HTTP Port</label>
  12339. <input type="number" id="proxy-cfg-http-port" min="1" max="65535" value="80" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12340. </div>
  12341. <div>
  12342. <label class="block text-xs font-semibold text-slate-300 mb-1">HTTPS Port</label>
  12343. <input type="number" id="proxy-cfg-https-port" min="1" max="65535" value="443" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12344. </div>
  12345. </div>
  12346. <div>
  12347. <label class="block text-xs font-semibold text-slate-300 mb-1">Default Fallback SSL Certificate</label>
  12348. <select id="proxy-cfg-default-cert" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-violet-500">
  12349. <option value="">(None / First Available Cert)</option>
  12350. </select>
  12351. <p class="text-[10px] text-slate-500 mt-1">Used for HTTPS clients when no rule-specific certificate matches.</p>
  12352. </div>
  12353. <div class="flex justify-end gap-2 pt-3 border-t border-slate-800">
  12354. <button type="button" onclick="closeProxyConfigModal()" class="px-4 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Cancel</button>
  12355. <button type="submit" class="px-4 py-2 rounded-lg bg-violet-600 hover:bg-violet-500 text-xs font-semibold text-white shadow-lg shadow-violet-600/30 transition">Save Settings</button>
  12356. </div>
  12357. </form>
  12358. </div>
  12359. </div>
  12360. <!-- ================= MODAL: SD-WAN GROUP ================= -->
  12361. <div id="modal-sdwan-group" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12362. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden max-h-[90vh] flex flex-col">
  12363. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12364. <h3 id="sdwan-group-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  12365. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
  12366. <span>Create SD-WAN Group</span>
  12367. </h3>
  12368. <button type="button" onclick="closeSDWANGroupModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12369. </div>
  12370. <form id="sdwan-group-form" onsubmit="saveSDWANGroup(event)" class="p-5 space-y-4 overflow-y-auto flex-1">
  12371. <input type="hidden" id="sdwan-group-id">
  12372. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  12373. <div>
  12374. <label class="block text-xs font-semibold text-slate-300 mb-1">Group Interface Name *</label>
  12375. <input type="text" id="sdwan-group-name" placeholder="e.g. sdwan0 or sdwan-internet" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12376. <p class="text-[10px] text-slate-500 mt-1">Acts as unified interface in Firewall.</p>
  12377. </div>
  12378. <div>
  12379. <label class="block text-xs font-semibold text-slate-300 mb-1">Distribution Strategy *</label>
  12380. <select id="sdwan-group-mode" onchange="onSDWANModeChange()" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12381. <option value="weighted">Weighted Percentage (Load Balance)</option>
  12382. <option value="pcc">PCC (Per-Connection Classifier Hashing)</option>
  12383. <option value="lowest-latency">Lowest Latency SLA (Minimum RTT)</option>
  12384. <option value="lowest-loss">Lowest Loss SLA (Minimum Drop)</option>
  12385. <option value="failover">Active-Backup (Priority Failover)</option>
  12386. </select>
  12387. </div>
  12388. </div>
  12389. <div id="sdwan-group-pcc-container" class="hidden">
  12390. <label class="block text-xs font-semibold text-slate-300 mb-1">PCC Flow Hash Fields</label>
  12391. <select id="sdwan-group-hash-fields" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12392. <option value="both-addresses-and-ports">Both Addresses & Ports (Recommended)</option>
  12393. <option value="src-address">Source Address Only</option>
  12394. <option value="both-addresses">Both Addresses (Source & Dest)</option>
  12395. <option value="dst-address">Destination Address Only</option>
  12396. </select>
  12397. </div>
  12398. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  12399. <div>
  12400. <label class="block text-xs font-semibold text-slate-300 mb-1">Probe Interval (seconds)</label>
  12401. <input type="number" id="sdwan-group-probe-interval" value="3" min="1" max="60" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12402. </div>
  12403. <div class="flex items-center pt-6">
  12404. <label class="flex items-center gap-2 cursor-pointer bg-slate-950/60 border border-slate-800/80 p-2.5 rounded-xl w-full">
  12405. <input type="checkbox" id="sdwan-group-auto-nat" checked class="rounded bg-slate-800 border-slate-700 text-amber-600 focus:ring-0">
  12406. <div>
  12407. <span class="text-xs font-semibold text-white block">Auto-NAT Masquerade</span>
  12408. <span class="text-[10px] text-slate-400 block">Masquerade egress WAN links automatically</span>
  12409. </div>
  12410. </label>
  12411. </div>
  12412. </div>
  12413. <div>
  12414. <label class="block text-xs font-semibold text-slate-300 mb-1">Comment</label>
  12415. <input type="text" id="sdwan-group-comment" placeholder="Optional description" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white focus:outline-none focus:border-amber-500">
  12416. </div>
  12417. <div class="px-5 py-3 -mx-5 -mb-5 bg-slate-950/50 border-t border-slate-800 flex items-center justify-end gap-2">
  12418. <button type="button" onclick="closeSDWANGroupModal()" class="px-4 py-2 rounded-xl text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-slate-300 transition">Cancel</button>
  12419. <button type="submit" class="px-4 py-2 rounded-xl text-xs font-semibold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-600/20 transition">Save Group</button>
  12420. </div>
  12421. </form>
  12422. </div>
  12423. </div>
  12424. <!-- ================= MODAL: SD-WAN MEMBER LINK ================= -->
  12425. <div id="modal-sdwan-member" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12426. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden max-h-[90vh] flex flex-col">
  12427. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12428. <h3 id="sdwan-member-modal-title" class="text-sm font-bold text-white flex items-center gap-2">
  12429. <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" /></svg>
  12430. <span>Add Member Link</span>
  12431. </h3>
  12432. <button type="button" onclick="closeSDWANMemberModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12433. </div>
  12434. <form id="sdwan-member-form" onsubmit="saveSDWANMember(event)" class="p-5 space-y-4 overflow-y-auto flex-1">
  12435. <input type="hidden" id="sdwan-member-id">
  12436. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  12437. <div>
  12438. <label class="block text-xs font-semibold text-slate-300 mb-1">Target SD-WAN Group *</label>
  12439. <select id="sdwan-member-group-id" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500"></select>
  12440. </div>
  12441. <div>
  12442. <label class="block text-xs font-semibold text-slate-300 mb-1">Member Interface *</label>
  12443. <select id="sdwan-member-interface" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500"></select>
  12444. </div>
  12445. </div>
  12446. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  12447. <div>
  12448. <label class="block text-xs font-semibold text-slate-300 mb-1">Gateway IPv4</label>
  12449. <input type="text" id="sdwan-member-gateway" placeholder="e.g. 192.168.1.1 (leave blank for auto)" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12450. <p class="text-[10px] text-slate-500 mt-1">Next-hop router for this link.</p>
  12451. </div>
  12452. <div>
  12453. <label class="block text-xs font-semibold text-slate-300 mb-1">Weight Ratio (1 - 100)</label>
  12454. <input type="number" id="sdwan-member-weight" value="50" min="1" max="100" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12455. <p class="text-[10px] text-slate-500 mt-1">Relative percentage ratio (e.g. 70 vs 30).</p>
  12456. </div>
  12457. </div>
  12458. <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
  12459. <div>
  12460. <label class="block text-xs font-semibold text-slate-300 mb-1">Failover Priority</label>
  12461. <input type="number" id="sdwan-member-priority" value="1" min="1" max="10" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12462. <p class="text-[10px] text-slate-500 mt-1">1 = Highest Primary.</p>
  12463. </div>
  12464. <div>
  12465. <label class="block text-xs font-semibold text-slate-300 mb-1">Health Ping Target</label>
  12466. <input type="text" id="sdwan-member-probe" value="1.1.1.1" required class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12467. <p class="text-[10px] text-slate-500 mt-1">e.g. 1.1.1.1 or 8.8.8.8.</p>
  12468. </div>
  12469. <div>
  12470. <label class="block text-xs font-semibold text-slate-300 mb-1">Max SLA Latency (ms)</label>
  12471. <input type="number" id="sdwan-member-max-lat" value="250" min="5" max="2000" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12472. </div>
  12473. </div>
  12474. <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  12475. <div>
  12476. <label class="block text-xs font-semibold text-slate-300 mb-1">Max Packet Loss SLA (%)</label>
  12477. <input type="number" id="sdwan-member-max-loss" value="20" min="0" max="100" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white font-mono focus:outline-none focus:border-amber-500">
  12478. </div>
  12479. <div>
  12480. <label class="block text-xs font-semibold text-slate-300 mb-1">Comment</label>
  12481. <input type="text" id="sdwan-member-comment" placeholder="e.g. Primary Fiber ISP" class="w-full bg-slate-950 border border-slate-800 rounded-lg px-3 py-2 text-xs text-white focus:outline-none focus:border-amber-500">
  12482. </div>
  12483. </div>
  12484. <div class="px-5 py-3 -mx-5 -mb-5 bg-slate-950/50 border-t border-slate-800 flex items-center justify-end gap-2">
  12485. <button type="button" onclick="closeSDWANMemberModal()" class="px-4 py-2 rounded-xl text-xs font-semibold bg-slate-800 hover:bg-slate-700 text-slate-300 transition">Cancel</button>
  12486. <button type="submit" class="px-4 py-2 rounded-xl text-xs font-semibold bg-amber-600 hover:bg-amber-500 text-white shadow-lg shadow-amber-600/20 transition">Save Member</button>
  12487. </div>
  12488. </form>
  12489. </div>
  12490. </div>
  12491. <!-- ================= MODAL: RESTORE BACKUP CONFIRMATION ================= -->
  12492. <div id="modal-restore-confirm" class="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
  12493. <div class="bg-slate-900 border border-slate-800 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden">
  12494. <div class="px-5 py-4 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
  12495. <h3 class="text-sm font-bold text-amber-400 flex items-center gap-2">
  12496. <svg class="w-5 h-5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>
  12497. <span>Confirm Configuration Restore</span>
  12498. </h3>
  12499. <button type="button" onclick="closeRestoreModal()" class="text-slate-400 hover:text-white p-1 rounded-lg">✕</button>
  12500. </div>
  12501. <div class="p-5 space-y-4">
  12502. <p class="text-xs text-slate-300 leading-relaxed">
  12503. Are you sure you want to restore this configuration? All current routing, firewall, tunnel, certificate, and service settings will be replaced.
  12504. </p>
  12505. <div id="modal-restore-details" class="p-3 bg-slate-950 rounded-xl border border-slate-800 text-xs font-mono text-slate-400 space-y-1">
  12506. </div>
  12507. <div class="p-3 bg-amber-500/10 border border-amber-500/20 rounded-xl text-amber-300 text-[11px]">
  12508. DebTik will create an automated fallback backup and reload immediately. Any active management sessions will briefly reconnect.
  12509. </div>
  12510. <div id="modal-restore-progress" class="hidden p-3 bg-cyan-500/10 border border-cyan-500/30 rounded-xl text-cyan-300 text-xs flex items-center gap-2">
  12511. <span class="w-3.5 h-3.5 rounded-full border-2 border-cyan-400 border-t-transparent animate-spin"></span>
  12512. <span id="modal-restore-progress-text">Restoring configuration and reloading DebTik...</span>
  12513. </div>
  12514. </div>
  12515. <div class="flex justify-end gap-2 px-5 py-4 border-t border-slate-800 bg-slate-950/30">
  12516. <button type="button" id="btn-cancel-restore" onclick="closeRestoreModal()" class="px-4 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 text-xs font-medium text-slate-300 transition">Cancel</button>
  12517. <button type="button" id="btn-execute-restore" onclick="executeRestoreBackup()" class="px-4 py-2 rounded-lg bg-amber-600 hover:bg-amber-500 text-xs font-semibold text-white shadow-lg shadow-amber-600/30 transition">Yes, Restore & Reload</button>
  12518. </div>
  12519. </div>
  12520. </div>
  12521. <datalist id="fw-addrlist-datalist"></datalist>
  12522. <script src="app.js?v=22.0"></script>
  12523. </body>
  12524. </html>