init
This commit is contained in:
commit
594f5222ef
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCaSua4bKKMn8hX
|
||||
zoH8afGUjdAd5WuLBZk8X4L98LxMN4fPcy7NwOPlEb6JUNnBcrbtOYbA++bOuXho
|
||||
LMNLPQJhXpSPV2Bdyu9ttDZ3HSEiTl8aauQixNec2LxBaXdVl9BptPYPy9ME0uMR
|
||||
lk/RZgUmQHs6+NmZS02MLUH1H8hOWgcse15MXLdb5ZRSwqsjsLnuAKeyrbjbYdAb
|
||||
Xjv9hQRQMeFwfI/dByPzv1TZ5CbJJ8hRO4unzXLCx5XTVfyFbhaChCAJi32FCZSW
|
||||
wtPEIX3/Vxu/SeAO3Z8PSdDEPPirFUYkEV+JB3m3Ts0dsYCFo78STa/KhmMkGEtA
|
||||
dK+Pe2zWd8Z5cLg2DHtQROyLJ+iGX767kPOB04PlG+rJTDYHOZBAMgKv4DxiDMRK
|
||||
hRNF4l3CDiKtnA35au6bjuWgiseTfgQuRuJt9GK7tnN+r6RcUPRVs1Ys/1EvJDLd
|
||||
4e9VAiZraELnFprE3N/oh3F6TVDTUr/bCY++RGLCEFOY5iUQ/4FnJHONbFYRdYa8
|
||||
ga2gfZYA+X2x26thdn0rjoj5Opl5K/AQ7l7DYz2VJjG+ZTYAV0Y7Frb9HDEsoZa3
|
||||
/oqDlbB7jk6t3ZbxN9LpDODITqfid8hLlURm2F9uW/wU4GVWc0D0nnZnaoXPL0nA
|
||||
bQWZuwpK06QiIIxDKs/MsIQfaIlNKwIDAQABAoICAEeB/kMsCfvdk8bqLQE0gqOk
|
||||
Q/ePG9P/IMFDzNxvRX2XYOiKy/7M7nH8Us+mOn228kOonST0ukhF2iGB2XMVz/Qb
|
||||
lwOF7eIaSvQPiQyUYgZZxybhsmDQ1NLa+gjg3c88AHn1RfMVNnPuXxGIFe8I9sr/
|
||||
KH2w1nx+025rjT2TYMy7WcbKWG5QrCQ5lw/yK8nrPoipIg+kBuAaCY6dknURQGoA
|
||||
bCireitog0eU/bjMThN9ThAj5jo7c/wE7xHWsKWQW3tay9RQozhUs9pZajbkhNYh
|
||||
43i4vtB08yJtRgWk8mSsA+l0adACuGz15wdjfBqzxLqxaY8V0H3qinnReoonnE1i
|
||||
wpBpK34DFL8NIWJZ2SVpnn847kW/cP0jxeLDDgQZMPC8WDSQsu1gOO+n98ACr9Iv
|
||||
WuXp3TcIsHSXNQm6KATH9opiNM7dqxR2KklGYNBC+Vr2Z1J8nszLUtNH+VeU30qE
|
||||
Q/4p/M9Akqy6fI4Gx52a98f1hyeG0VGJ247qA56Mlg1lUMZoBjr57xpHgoVBc3Bp
|
||||
YvTuN9eNUJWgkSFWwiET+sjbVLLnwyH3pDCjohl9PLUze2RqMhb3WWHy/ZelzEnX
|
||||
k71XuCseduCO4jgpQeGy3bg8k1qJaj0veiuon6+WVvZ333iwO4cWF/bxPJkgLS0X
|
||||
pRf2e8iGBppXFR641YxhAoIBAQDXGa+2t9tFq6EQBgvYMVxP+b0y2DCJAt2mq3ZT
|
||||
sABSExbg6/o9ZW+wE9CE5G7Vh6Tqu1kgMwCUY4LWBj/hPrb7n817H78e0kkUemHh
|
||||
NehvPSgHnwOPoRl2suQTy60I7Qdr3pWnkrqIixJFGL/HB9ULiBV/+iEfzdiivIqL
|
||||
5mTRtAnRjtPHaPD5pCm9YqI0lFJGs9PNQUnDHt0JgemNCzeqAWwoonI/szcSxvrT
|
||||
0qQJI7688Hgr/fRnG6hAkOYN4QyIpwZ9fF43MzH3WiwkRz3YKVEiEkoZXTLK5X05
|
||||
7/X4H6TY/AG2puFhwBALcLE2CjutHCmsllCi/8W970lL1kqhAoIBAQC3oU+wlnIt
|
||||
bMNsVraTDvTfN+3H96sJ/snh0mDZQYyUxbN22TMnEqcjKJ/afNymvp3UffZT9EiK
|
||||
wm05UxMlBXAXPTiXI+71K9QVgew9lIoXop8/7BoACeLCvzRsFzYNsH0vtDHTz1yO
|
||||
0RGrarb3BsONeDLtUikwfgi+CLtYRbSeRAULvU29yOFr10cHOXw7ItDMBSMwU/hP
|
||||
3aKSEYAn6CVqLqAAPr6T5RwjXuT2daDKoyq3P/X0NkLMVMtGaeSjO41QnLf4pSWb
|
||||
7x1ec0D6l6OEboKLilknh/x/2nedmOwAEiNM4pSIhqSY1JZw6rO+J6Y6FEkdxzy0
|
||||
zWYLLQEdO3BLAoIBAQC0KzHBDHNAAihgcZAOQBogawEMy4Sr3pil+EyegHdkR9UM
|
||||
BNyP/Jz2kUJBbc1gUcPTUaWvkMjVghyfBJERLCS80vecP9UoDd9kpbXCnBLayhDz
|
||||
gT/MWJYplGHyKtH+/fsPYVtdB0vy/voIolhQb8EFbbEEhxCjwRnKBb7Ou+aKuBDZ
|
||||
VJcTGlVt5RGJLkrLW8kmq42nCR6trvZZ9lUX7ONtJM/hoV+s94IT2lNShcccWLJ1
|
||||
M76UqpzCmKnvmu73hR+ofq6zAS8xcVJS61a59Gpa2xC4RzWF1UisJlkj5FjUwjyj
|
||||
+0G8Kj/yTpuh2Xy11RBzSXmmtClKvo51Ly7ntNDBAoIBACwZfmTfTJ5iH3CWRpR8
|
||||
pAwzh1Rdw2LMILmt53plhph3/kiNkv5QOXl1GSbEk80rvAW7FnxFD2LbnJWGwPNE
|
||||
Ig5CsqOBirKwiud7YzvKv3s7n9kfH4Ng3Gd+ud03mdCh0P6y3MCMbSMTTJYJM9WR
|
||||
d0czVa+u6pttuTXCLRPe8aiMl1WhadkpNBHNCo7OnGorS0+j8DZ8BAdSJ+fCci+j
|
||||
TdD5T42fg+9Kt347HOufhjUECI8nui6jFVmzB3pqlfEprR9t87SwHJ8dRsVXEoTc
|
||||
A/YbABj46b5XpykhU1ay0gKWWInvZFKBW2gkrdiCVRZWuVH7ay/OmGKnbVlDvp/B
|
||||
lY8CggEALa9Dw8kMDet9sysx4zC9f3IyQT6iR15RaD6uTc2J2wTBjry5deC+M+pt
|
||||
g277uvfRgr5EGsXidJ03jD7Sc5wNM1i1q1AC8QPAKX4j0l35N7SxjJP+cxIdwll+
|
||||
dQqg0dr1A4mCbgm22IatmlvEH4oGu/WozDDlhz11WLhG2m28npAspdEAacCsvncX
|
||||
DDzaff/4FxjN8BilhqBHaLgZuqQROfDVWJb7vfq/BTO4uZBUjYONLEgG37cA002H
|
||||
HIBqMEzbewdNtLSbi4xCvmgnofZ1WtsyPfAWCHX+A6wjF8Ey8PdU2/0ILHQwx3WO
|
||||
oo7uGCyxw179QUUzXs18xVnordQGvA==
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmkrmuGyijJ/IV86B/Gnx
|
||||
lI3QHeVriwWZPF+C/fC8TDeHz3MuzcDj5RG+iVDZwXK27TmGwPvmzrl4aCzDSz0C
|
||||
YV6Uj1dgXcrvbbQ2dx0hIk5fGmrkIsTXnNi8QWl3VZfQabT2D8vTBNLjEZZP0WYF
|
||||
JkB7OvjZmUtNjC1B9R/ITloHLHteTFy3W+WUUsKrI7C57gCnsq2422HQG147/YUE
|
||||
UDHhcHyP3Qcj879U2eQmySfIUTuLp81ywseV01X8hW4WgoQgCYt9hQmUlsLTxCF9
|
||||
/1cbv0ngDt2fD0nQxDz4qxVGJBFfiQd5t07NHbGAhaO/Ek2vyoZjJBhLQHSvj3ts
|
||||
1nfGeXC4Ngx7UETsiyfohl++u5DzgdOD5RvqyUw2BzmQQDICr+A8YgzESoUTReJd
|
||||
wg4irZwN+Wrum47loIrHk34ELkbibfRiu7Zzfq+kXFD0VbNWLP9RLyQy3eHvVQIm
|
||||
a2hC5xaaxNzf6Idxek1Q01K/2wmPvkRiwhBTmOYlEP+BZyRzjWxWEXWGvIGtoH2W
|
||||
APl9sdurYXZ9K46I+TqZeSvwEO5ew2M9lSYxvmU2AFdGOxa2/RwxLKGWt/6Kg5Ww
|
||||
e45Ord2W8TfS6QzgyE6n4nfIS5VEZthfblv8FOBlVnNA9J52Z2qFzy9JwG0FmbsK
|
||||
StOkIiCMQyrPzLCEH2iJTSsCAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card wide">
|
||||
<h3>ShreeBhattJi</h3>
|
||||
<p>If you’ve ever read the Panchatantra, a few stories begin like this: “Once upon a time, there lived a poor Brahmin in a village.” In this story, that Brahmin is me — ShreeBhattJi, also known as Devdatt Bhatt, the King of Automation and Remote Control from the land of great beginnings, Bhavnagar.
|
||||
<p>I am here to help provide a great viewing experience for users, generate strong commissions for LCOs, MSOs, and ISPs, and create substantial revenue for content owners while contributing fair taxes to the government. I have reason to believe that by upgrading a few devices at the transmission point, both streaming operations and customer viewing experiences can be improved exponentially.
|
||||
<p>We are proud to present Universal Digital Encoder Decoder as all format to all format encoder cum decoder for dedicated hardware , virtual machines and proxmox lxc images .
|
||||
<p><a href="https://urmic.org/trusted-partners/" target="_blank"><button class="red-btn">Meet Our Partners</button></a>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<h3>Bhavnagar :- The Land Of Great Beginnings</h3>
|
||||
<ul>
|
||||
<li>The great country called India began to exist at Bhavnagar. On 15 August 1947, Indian administrative control effectively began with Bhavnagar after Maharaja Krishnakumarsinhji Bhavsinhji Gohil merged his princely state into the independent Union of India.</li>
|
||||
<p>
|
||||
<li>The “bull gift” from Maharaja Krishnakumarsinhji of Bhavnagar to Brazilian cattle baron Celso Garcia Sid helped shape the Brazilian dairy industry, leading to the world-famous Girolando breed. Today, Girolando cows produce over 80% of Brazil’s milk. <a href="https://youtu.be/HLJHFibZ130">DD INDIA REPORT</a></li>
|
||||
<p>
|
||||
<li>The first diamond-cutting industry in India started in Bhavnagar, not Surat. Skilled jewelers and artisans in Bhavnagar (Saurashtra region, Gujarat) began cutting and polishing small rough diamonds imported from Africa. These craftsmen later moved to Surat, which eventually became the world’s largest diamond-cutting and polishing hub.</li>
|
||||
<p>
|
||||
<li>The largest ship-breaking yard in the world, “Alang Ship Breaking Yard,” located in Bhavnagar district, Gujarat, started operations in 1983. It handles over 50% of global ship recycling by tonnage and is a major source of recycled steel for India’s economy.</li>
|
||||
<p>
|
||||
<li>After the Kurukshetra war, the Pandavas sought absolution for the killing that occurred in battle. Following the guidance of Lord Krishna, who gave them a black flag and a black cow, they were instructed to follow the cow until both turned white. Krishna told them that when this happened, they would be forgiven. The Pandavas followed the cow for many years to various places, but the flag and cow remained black. Finally, when they reached Koliyak Beach, both miraculously turned white.</li>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Self Certification</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
background: #f4f6f8;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
background: #0f172a;
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-bar button {
|
||||
background: #2563eb;
|
||||
border: none;
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.top-bar button:hover {
|
||||
background: #1d4ed8;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 24px auto;
|
||||
background: #ffffff;
|
||||
padding: 32px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: bold;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.signature {
|
||||
margin-top: 48px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sign-box {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.sign-line {
|
||||
margin-top: 40px;
|
||||
border-top: 1px solid #000;
|
||||
padding-top: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.top-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
box-shadow: none;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function goHome() {
|
||||
window.location.href = "index.html";
|
||||
}
|
||||
|
||||
function printPage() {
|
||||
window.print();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="top-bar">
|
||||
<button onclick="goHome()">Home</button>
|
||||
<button onclick="printPage()">Print</button>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h1>Self Certification Declaration</h1>
|
||||
|
||||
<p>
|
||||
This document serves as a formal self-certification confirming compliance with all applicable
|
||||
legal, security, and operational requirements related to the provided software, firmware,
|
||||
service, or system.
|
||||
</p>
|
||||
|
||||
<div class="section-title">Compliance & Safety Declaration</div>
|
||||
<p>
|
||||
I hereby declare and guarantee that the delivered software or system contains no malware,
|
||||
spyware, backdoors, or harmful components of any kind. The system does not perform unauthorized
|
||||
surveillance, user behavior tracking, or data collection beyond what is strictly required for
|
||||
its intended operation.
|
||||
</p>
|
||||
|
||||
<div class="section-title">Privacy Assurance</div>
|
||||
<p>
|
||||
No usage tracking, analytics, or hidden monitoring mechanisms are implemented. Any data processed
|
||||
by the system is handled transparently and solely for functional or security-related purposes.
|
||||
</p>
|
||||
|
||||
<div class="section-title">License Verification Notice</div>
|
||||
<p>
|
||||
For licensing and authenticity verification purposes, limited hardware-related identifiers may
|
||||
be securely stored. This information is used exclusively for license validation and protection
|
||||
against unauthorized use, duplication, or redistribution.
|
||||
</p>
|
||||
|
||||
<div class="section-title">Limitation of Liability</div>
|
||||
<p>
|
||||
The developer, author, or distributor shall not be held liable for any direct, indirect,
|
||||
incidental, special, consequential, or punitive damages, including but not limited to loss
|
||||
of data, system failure, business interruption, or financial loss, arising from the use,
|
||||
misuse, or inability to use the software or system.
|
||||
</p>
|
||||
<p>
|
||||
The software or system is provided on an “as-is” and “as-available” basis, without warranties
|
||||
of any kind, whether express or implied. The user assumes full responsibility for deployment,
|
||||
configuration, operation, and compliance with applicable laws and regulations.
|
||||
</p>
|
||||
|
||||
<div class="section-title">Declaration</div>
|
||||
<p>
|
||||
I confirm that the above statements are true and accurate to the best of my knowledge and accept
|
||||
full responsibility for compliance with this declaration.
|
||||
</p>
|
||||
|
||||
<div class="section-title">Device Id</div>
|
||||
<p>
|
||||
certificatecertificatecertificatecertificate
|
||||
</p>
|
||||
|
||||
<div class="signature">
|
||||
<div class="sign-box">
|
||||
Devdatt Bhatt aka ShreeBhattji <br> Bhavnagar
|
||||
<div class="sign-line">Authorized Signature</div>
|
||||
</div>
|
||||
<div class="sign-box">
|
||||
<br>01 Jan , 2026
|
||||
<div class="sign-line">Date</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,54 @@
|
|||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card wide">
|
||||
<h3>How Can We Help ?</h3>
|
||||
<ul>
|
||||
<li>We provide free RTMP and EPG file hosting for Registared Broadcastors.</li>
|
||||
<li>Auto Publishing channel on Cable Tv, IPTV , OTT APP like <a href="https://play.google.com/store/apps/details?id=org.urmic.ott">URMIC OTT.</a></li>
|
||||
<li>LCO and MSO can get local channels via STREAMINGBOX ( One Box 30 channel - No Encoder Needed - QAM is must )</li>
|
||||
<li>ISP will get scalable Intranet OTT for resellers and customers .</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<h3>Say Hello to Us</h3>
|
||||
<p>C-1819 , <br>Radhesyam Soc,<br>Kalyabid, <br>Bhavnagar - 364001<br>INDIA
|
||||
<p>support@urmic.org
|
||||
<br>+91-8000-74-1919
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<div class="social-row" aria-label="Social links">
|
||||
<a class="social-btn" href="https://www.facebook.com/WorldwideDigitalAssociation" target="_blank" rel="noopener" aria-label="Facebook page">
|
||||
<svg viewBox="0 0 23 23" fill="currentColor" aria-hidden="true">
|
||||
<path d="M22 12.07C22 6.47 17.52 2 11.92 2S2 6.47 2 12.07c0 4.99 3.66 9.12 8.44 9.86v-6.98H8.1v-2.88h2.34V9.41c0-2.31 1.37-3.59 3.46-3.59.99 0 2.03.18 2.03.18v2.23h-1.14c-1.12 0-1.47.7-1.47 1.42v1.7h2.5l-.4 2.88h-2.1v6.98C18.34 21.19 22 17.06 22 12.07z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="social-btn" href="https://www.linkedin.com/in/dbhatt-org/" target="_blank" rel="noopener" aria-label="LinkedIn profile">
|
||||
<svg viewBox="0 0 23 23" fill="currentColor" aria-hidden="true">
|
||||
<path d="M4.98 3.5C4.98 4.88 3.86 6 2.5 6S0 4.88 0 3.5 1.12 1 2.5 1 4.98 2.12 4.98 3.5zM.5 8h4v14h-4V8zm7.5 0h3.84v1.93h.05c.54-1.02 1.86-2.1 3.83-2.1 4.1 0 4.86 2.7 4.86 6.21V22h-4V15.2c0-1.61-.03-3.68-2.24-3.68-2.24 0-2.58 1.75-2.58 3.55V22h-4V8z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="social-btn" href="https://wa.me/918000741919?text=Hello%20from%20website" target="_blank" rel="noopener" aria-label="WhatsApp chat">
|
||||
<svg viewBox="0 0 23 23" fill="currentColor" aria-hidden="true">
|
||||
<path d="M20.52 3.48A11.93 11.93 0 0 0 12 0C5.37 0 .05 5.32.05 11.92c0 2.1.55 4.15 1.6 5.95L0 24l6.4-1.68c1.75 1.02 3.77 1.56 5.6 1.56 6.63 0 11.95-5.32 11.95-11.95 0-3.2-1.25-6.2-3.43-8.45zM12 21.95c-1.55 0-3.06-.4-4.36-1.16l-.31-.18-3.8 1 1-3.5-.2-.34A8.72 8.72 0 0 1 3.1 11.9c0-4.85 3.95-8.8 8.8-8.8 2.35 0 4.56.92 6.22 2.6a8.76 8.76 0 0 1-6.13 15.45zM17.03 14.1c-.29-.15-1.7-.84-1.97-.93-.27-.09-.47-.14-.67.14-.2.28-.77.93-.94 1.12-.17.19-.34.21-.63.07-.29-.15-1.23-.45-2.34-1.44-.87-.78-1.46-1.74-1.63-2.03-.17-.29-.02-.45.13-.6.13-.13.29-.34.44-.51.14-.17.19-.29.29-.48.1-.19.05-.36-.02-.51-.07-.15-.67-1.6-.92-2.19-.24-.58-.49-.5-.67-.51l-.57-.01c-.19 0-.5.07-.76.36-.27.29-1.03 1.01-1.03 2.47s1.05 2.87 1.2 3.07c.15.19 2.07 3.19 5.02 4.47 2.95 1.28 3.12.88 3.68.83.56-.05 1.78-.72 2.03-1.41.25-.69.25-1.28.18-1.41-.07-.13-.27-.19-.56-.34z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="social-btn" href="sms:+918000741919?body=Test%20message" aria-label="Send test SMS">
|
||||
<svg viewBox="0 0 23 23" fill="currentColor" aria-hidden="true">
|
||||
<path d="M20 2H4c-1.1 0-2 .9-2 2v14l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="social-btn" href="mailto:hello@urmic.org?subject=Hello&body=Hi%2C%20I%20am%20contacting%20you%20from%20the%20website." aria-label="Send email">
|
||||
<svg viewBox="0 0 23 23" fill="currentColor" aria-hidden="true">
|
||||
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
<?php include 'header.php' ?>
|
||||
<?php
|
||||
|
||||
$jsonFile = __DIR__ . '/firewall.json';
|
||||
|
||||
$defaults = [
|
||||
'80' => '',
|
||||
'443' => '',
|
||||
];
|
||||
|
||||
$data = $defaults;
|
||||
|
||||
if (is_file($jsonFile)) {
|
||||
$stored = json_decode(file_get_contents($jsonFile), true);
|
||||
if (is_array($stored)) {
|
||||
$data = $stored;
|
||||
}
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
exec("echo y | sudo ufw reset");
|
||||
exec("sudo ufw default allow outgoing");
|
||||
exec("sudo ufw default deny incoming");
|
||||
exec("sudo ufw allow proto udp to 224.0.0.0/4");
|
||||
exec("sudo ufw route allow proto udp to 224.0.0.0/4");
|
||||
exec("sudo ufw deny out to 239.255.254.254 port 39000 proto udp");
|
||||
|
||||
foreach ($defaults as $port => $_) {
|
||||
$data[$port] = trim($_POST["port_$port"] ?? '');
|
||||
}
|
||||
|
||||
$tmp = $jsonFile . '.tmp';
|
||||
file_put_contents(
|
||||
$tmp,
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
|
||||
);
|
||||
rename($tmp, $jsonFile);
|
||||
|
||||
foreach ($data as $port => $value) {
|
||||
$tmp = array_filter(
|
||||
array_map('trim', explode(',', (string)$value)),
|
||||
'strlen'
|
||||
);
|
||||
if (count($tmp) > 0) {
|
||||
foreach ($tmp as $ip) {
|
||||
exec("sudo ufw allow from " . $ip." to any port " . $port . " proto tcp");
|
||||
}
|
||||
} else {
|
||||
exec("sudo ufw allow " . $port);
|
||||
}
|
||||
}
|
||||
|
||||
exec("sudo ufw allow from 172.16.111.112 to 172.16.111.111 port 80");
|
||||
exec("sudo ufw allow from 172.16.111.112 to 172.16.111.111 port 443");
|
||||
exec("sudo ufw --force enable");
|
||||
exec("sudo ufw reload");
|
||||
}
|
||||
?>
|
||||
|
||||
?>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, sans-serif;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 520px;
|
||||
margin: 40px auto;
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, .08);
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
width: 100%;
|
||||
padding: 16px 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
|
||||
input[type=text]:invalid {
|
||||
border-color: #d33;
|
||||
}
|
||||
|
||||
small {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
input[type=text]:focus {
|
||||
outline: none;
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 0 0 2px rgba(37, 99, 235, .15);
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 20px;
|
||||
padding: 12px 18px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: #2563eb;
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #1e4ed8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function validateIPs(input) {
|
||||
if (!input.value.trim()) return true;
|
||||
|
||||
const ips = input.value.split(',').map(i => i.trim());
|
||||
|
||||
const ipv4 =
|
||||
/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
|
||||
|
||||
const ipv6 =
|
||||
/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::1|::)$/;
|
||||
|
||||
for (const ip of ips) {
|
||||
if (!(ipv4.test(ip) || ipv6.test(ip))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function attachValidation() {
|
||||
document.querySelectorAll('input[type=text]').forEach(inp => {
|
||||
inp.addEventListener('input', () => {
|
||||
inp.setCustomValidity(
|
||||
validateIPs(inp) ? '' : 'Invalid IPv4 or IPv6 address'
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
window.onload = attachValidation;
|
||||
</script>
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card wide">
|
||||
<h2>Limit Access</h2>
|
||||
|
||||
<form method="post">
|
||||
<?php foreach ($data as $port => $value): ?>
|
||||
<div class="row">
|
||||
<label>Port <?= htmlspecialchars($port) ?></label>
|
||||
<textarea
|
||||
name="port_<?= $port ?>"
|
||||
rows="2"
|
||||
placeholder="IPv4, IPv6 (comma separated)"><?= htmlspecialchars($value) ?></textarea>
|
||||
|
||||
<small>Example: 192.168.1.10/24, 2001:db8::1</small>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<button type="submit">Limit Access</button>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include 'footer.php' ?>
|
||||
|
|
@ -0,0 +1,494 @@
|
|||
<?php
|
||||
|
||||
exec("sudo chmod 444 /sys/class/dmi/id/product_uuid");
|
||||
$version = 1;
|
||||
|
||||
function fail(string $msg): never
|
||||
{
|
||||
fwrite(STDERR, "ERROR: $msg\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
function download(string $url, string $dest): void
|
||||
{
|
||||
$fp = fopen($dest, 'wb');
|
||||
if (!$fp) fail("Cannot write $dest");
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_FILE => $fp,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_FAILONERROR => true,
|
||||
CURLOPT_TIMEOUT => 60,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
]);
|
||||
|
||||
if (!curl_exec($ch)) {
|
||||
fail("Download failed: " . curl_error($ch));
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
|
||||
$device_id = trim(file_get_contents('/sys/class/dmi/id/product_uuid'));
|
||||
|
||||
$publicKey = "-----BEGIN PUBLIC KEY-----
|
||||
MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAm6glpTALuc82R+9Mqb5f
|
||||
HVRC5dScc7USgWoIsYN1tF8YE0d7rhSIFvayVabiyabVmWscjAIlmYf6InSlLsDx
|
||||
avfmapg32ECd92H49ZbsvXQpLqasyOkN7z6FUcuQ6pEMqfPBmBXKGngHazPp420o
|
||||
Iki9hLc7IE9EMlDHfozckuJI8mB+bsd2oqua6SSTBYx5HYuSCbootf9GliSd7PVk
|
||||
H6uir88j49/NFfvmrReicFBiMba959uOdBIhWl9AveZL+iI2NdS5SPw6eWltaMot
|
||||
PSk7/5Z4Vn5Od7sQA0yUqmCj5XNV5EzRlP1jhP7SDv0D6Mpdf3HuKWdCBqepJBe2
|
||||
rCHPQ9KrChQau4eEbJb5LIE3gFDpLxTHk9FEp+50evkpFONj0aAjSb3P4wsEGiOk
|
||||
95Tm56gDRirnbbw/6SzhE7pEvXRUfMl1KO6maYK1z7KNMgEH99C2zCjZhpaXL5Io
|
||||
rywCw009zoMT2qdKPMGOyQ4KPlCLCJYSF0y/rE07WNgl4BupVZR41B5MbXL5L7+X
|
||||
OD0jBpbWI7v2ChP9rn5u6Lqpq6ewvc2RJO8lrAyZtzrNJNZNYmUKHrm7qAHJeJZX
|
||||
Zh7OEf9U/T9JBpzf8l0MzyywlQGUBzb/niG0iZILt3XIpD+Xeyrr6hr+nabeiKXV
|
||||
jHyUcG84zzLjv7sREzWEGoLBrdztMy69rbfd3d0DpjS90xceKZYBDd3vwjn6h0TN
|
||||
KssqUb7BMH+zkCe/LQg6EGdXB13+xUSUjFKLLeBKu1VxMPfd/WmV1QumOodidvee
|
||||
rQAv6yMevq2hVFkiFo7CUpaRv6dvQnQaqX2rHFKZY6zEIzbJXTznl6ZMtCcmcZMk
|
||||
CYcoWZIAUR5tFP221XzIfJmymVRfJGiKTvt+g/SUUFJt6mq8ettu11XS4KSIxtaA
|
||||
l8q2SSxpRQa80NUuaBpQc/3eP293wgcf/EOfzhCjxDLjsHSKV1AkSMyjvCzSsCdG
|
||||
mMEIuT/D7PB7N8vlfhn5qsyt1Sm81/1EZ3u8UqToELhe8j7G26GVl/8ptSxofvZE
|
||||
X0goYwW18PPhtZvkR8CXpZ7qwjqDcL5cQzcCldufjtqJ5GAwN6SrcmnYjQoo2cu9
|
||||
XlWo0InPE8BpjR7vJpKLbppQzwUs9GQYx2bMSTbsrduc8zDXlPT5aOfgkJui/NQa
|
||||
uxttvsXqXd3nNJhbO0BN+wCDT0j4LNRvMlJloWEGrBkY4SA5I1MX8XBL34Csy6Bu
|
||||
bHWxXNBAGYMchcJKly7XN2hA61V4QCCiFz/MP9l1llw/Mk4D5IUTxcfcEDHx7LO0
|
||||
To+pc5kuXS6Aps6lKJdwv6h0Bi9SWtBpFi2RtpQpAc+dVPQ9lwq3VTJV5GZz3AgV
|
||||
KQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
";
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
switch ($_POST['action']) {
|
||||
case 'update':
|
||||
|
||||
$payload['device_id'] = $device_id;
|
||||
$payload['project_id'] = "9163de1b4445588f30149e0c7a3c7b";
|
||||
$payload['version'] = $version;
|
||||
$payload = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
openssl_public_encrypt(
|
||||
$payload,
|
||||
$encrypted,
|
||||
$publicKey,
|
||||
OPENSSL_PKCS1_OAEP_PADDING
|
||||
);
|
||||
|
||||
$postData = [
|
||||
'encrypted' => base64_encode($encrypted)
|
||||
];
|
||||
|
||||
$ch = curl_init('https://account.urmic.org/encoder/update.php');
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $postData,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if ($data['status'] != "valid") {
|
||||
echo '<script>alert("'
|
||||
. htmlspecialchars($data['message'], ENT_QUOTES)
|
||||
. '");</script>';
|
||||
}
|
||||
|
||||
if ($data['status'] == "valid") {
|
||||
echo '
|
||||
<style>
|
||||
#blocker {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.75);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 999999;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
#blockerBox {
|
||||
background: #111827;
|
||||
color: #ffffff;
|
||||
padding: 28px 36px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
font-family: Arial, sans-serif;
|
||||
min-width: 320px;
|
||||
box-shadow: 0 20px 50px rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
#blockerBox .msg {
|
||||
font-size: 18px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#blockerBox .timer {
|
||||
font-size: 34px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="blocker">
|
||||
<div id="blockerBox">
|
||||
<div class="msg">'
|
||||
. htmlspecialchars($data['message'], ENT_QUOTES) .
|
||||
'</div>
|
||||
<div>Refreshing in</div>
|
||||
<div class="timer" id="blockerTimer">100</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let seconds = 100;
|
||||
const timer = document.getElementById("blockerTimer");
|
||||
|
||||
const interval = setInterval(() => {
|
||||
seconds--;
|
||||
timer.textContent = seconds;
|
||||
|
||||
if (seconds <= 0) {
|
||||
clearInterval(interval);
|
||||
blocker.remove();
|
||||
window.location.href = window.location.pathname;
|
||||
}
|
||||
}, 1000);
|
||||
</script>
|
||||
';
|
||||
|
||||
$public_key = "-----BEGIN PUBLIC KEY-----
|
||||
MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAm+7Vl0fEgey2tF6v2mTn
|
||||
3C/FDGn589uY5a9rpDeZLlhjdOdFaTMWL3d8oEhmImCd+aPELpxydQ+xGxVPNOzO
|
||||
WKbF3V/FymwxyU3yCD8rfCPyd05z9ANeicVEZMO2K0CwjLoM1OFpxoo/GRmetHuY
|
||||
Yt2WxDWHPN9DjDDkIMrx2PKFHPqJnyWliyFWJ4aaaK174GH+b4rHRkAm31fUhbaG
|
||||
RBcQWJhWv1gJ+lxz2z3oHi9nI6Q/Hkb+u3B11tcx3j6rScxKXk8T6Bw64vEk3t0l
|
||||
i1kYgnPI4Eya0BXuROMfn+zGG50TNgq+vWntzBoKaWuPVbvvmzTlHK8My9qZUliy
|
||||
otDNd340xhBCmIYqkwxiN2w4g+TAM9X3r9/4lgJYx5ezh3Y0uLGf6mHZ5wFyDAhh
|
||||
uLJxkOCZY0b3zoRW5wqqKR67/FxBCpcLS6Y8wlKSR8UU8y73hr2tGD28JgNr9sjx
|
||||
reRItpdGhQgO8gLZKLK6LhihTFtbt5tiL1l6Fkc11DSac+N/xFyHfRe6K3lIV+cD
|
||||
WMx0+6YX3p8i4cmRXGn59Xu1VdZvmB03Dl5YmIb6wBNMCEPWohRz0bGmamXGW1Ze
|
||||
EZQhGJRUqIFNuTQwc/RI1wPUgefXXXitCOlo52oyahuKWxWuGMN/8Uyw74poK7NK
|
||||
7Tbu+JLNuqMsuPoVkrl7havRUbwQy7xUt93wFew0GFDaOobZzoGIjp3pWGvZiQ7y
|
||||
XMyzklS42/ZC7rJAJTyuLTHxMeUMB4Zt7Qmp7GQ3NaOUq4egPQ6KZUO4qDNtAJaK
|
||||
mvHca0HHmskP20/yb4iVtz65zhj6BWt98SsFuRMrMDDoBDEtcd1T7xIRK4nqfIhX
|
||||
8Nw8z1+m8TVItJM3XxvLx6eXgtnJ8BqWInjRoFkbpzEON56zA1ZwPCFm7MWACKEs
|
||||
m4Gul3+liBwDnpaJvHLLs6+9R4T1/d6nrwwRPDBz9AhBZV2Qz0/Z67qAyGvT2Joh
|
||||
qR6fIHe+jsKlPSW4TBBx8C2H6avKv7W0CH7z4Y9APuDucvMQ2X3CCekTRaejU7nr
|
||||
JOGs8ALAtsL+eXL+KMvU/16zxzcbT4ZW/6kdRFtwkaWlq07Q1yU13s+JQRzenut5
|
||||
7j1GMcmtt1K/CSBzhs2d2UTwiO3fRDs4TCUAj/vq2OlfL1UOAZ3ni8QmfA1vD/BD
|
||||
Xqfivizijmypv83rv8se5b6dr78ti+wiAIEJEDX+/yISmEWuDXGaL+eVATr1Rw+0
|
||||
8vFY2f7lS2/QsSv+X7B6lOs3L18sG7AAYrkFjrfhQ8RC9Lv62ITUAV6B6G/BJ4o0
|
||||
UubReGWsYm092Z9SWEB8KBUlwMWjEMl6Q2f3AfkAKR3EMYBqmNfL8teAcb711xA2
|
||||
EwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
";
|
||||
|
||||
error_log("starting");
|
||||
$tmpDir = sys_get_temp_dir() . '/payload_' . bin2hex(random_bytes(6));
|
||||
$zipFile = $tmpDir . '/payload.zip';
|
||||
$sigFile = $tmpDir . '/payload.zip.sig';
|
||||
$extractDir = $tmpDir . '/extract';
|
||||
error_log("setting up directory");
|
||||
|
||||
mkdir($tmpDir, 0700, true);
|
||||
mkdir($extractDir, 0700, true);
|
||||
error_log("directory created");
|
||||
error_log($tmpDir);
|
||||
|
||||
download($data['link'], $zipFile);
|
||||
download($data['signature'], $sigFile);
|
||||
error_log("download compltete");
|
||||
|
||||
$publicKey = openssl_pkey_get_public($public_key);
|
||||
if (!$publicKey) fail('Invalid public key');
|
||||
|
||||
$data = file_get_contents($zipFile);
|
||||
$signature = file_get_contents($sigFile);
|
||||
error_log("loading zip and sig");
|
||||
|
||||
$verified = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256);
|
||||
|
||||
if ($verified !== 1) {
|
||||
error_log("verification failed");
|
||||
fail('Signature verification FAILED');
|
||||
}
|
||||
error_log("varification complete");
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($zipFile) !== true) {
|
||||
error_log("zip unzip problem");
|
||||
fail('Unable to open ZIP');
|
||||
}
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$name = $zip->getNameIndex($i);
|
||||
if (str_contains($name, '..') || str_starts_with($name, '/')) {
|
||||
fail('Zip traversal detected');
|
||||
}
|
||||
}
|
||||
|
||||
$zip->extractTo($extractDir);
|
||||
$zip->close();
|
||||
$setup = $extractDir . '/setup.sh';
|
||||
|
||||
if (!is_file($setup)) {
|
||||
fail('setup.sh not found');
|
||||
}
|
||||
|
||||
chmod($setup, 0755);
|
||||
|
||||
$descriptorSpec = [
|
||||
1 => ['pipe', 'w'],
|
||||
2 => ['pipe', 'w'],
|
||||
];
|
||||
|
||||
$process = proc_open(
|
||||
['/bin/bash', $setup],
|
||||
$descriptorSpec,
|
||||
$pipes,
|
||||
$extractDir
|
||||
);
|
||||
|
||||
if (!is_resource($process)) {
|
||||
fail('Failed to execute setup.sh');
|
||||
}
|
||||
|
||||
$output = stream_get_contents($pipes[1]);
|
||||
$error = stream_get_contents($pipes[2]);
|
||||
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
|
||||
$exitCode = proc_close($process);
|
||||
}
|
||||
break;
|
||||
case 'reset':
|
||||
$files = glob('/var/www/html/*.json');
|
||||
foreach ($files as $file) {
|
||||
if (is_file($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'reboot':
|
||||
exec('sudo reboot');
|
||||
break;
|
||||
case 'backup':
|
||||
$jsonFiles = [
|
||||
'input.json',
|
||||
'firewall.json',
|
||||
'network.json',
|
||||
];
|
||||
|
||||
$tmpZip = sys_get_temp_dir() . '/backup.zip';
|
||||
$outputFile = __DIR__ . '/universal_transcoder.bin';
|
||||
|
||||
$publicKey = file_get_contents('/var/www/backup_private.pem');
|
||||
$publicKey = file_get_contents('/var/www/backup_public.pem');
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($tmpZip, ZipArchive::CREATE | ZipArchive::OVERWRITE);
|
||||
|
||||
foreach ($jsonFiles as $json) {
|
||||
if (file_exists($json)) {
|
||||
$zip->addFile($json, basename($json));
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
$data = file_get_contents($tmpZip);
|
||||
|
||||
$aesKey = random_bytes(32);
|
||||
$iv = random_bytes(16);
|
||||
|
||||
$encryptedData = openssl_encrypt(
|
||||
$data,
|
||||
'AES-256-CBC',
|
||||
$aesKey,
|
||||
OPENSSL_RAW_DATA,
|
||||
$iv
|
||||
);
|
||||
|
||||
openssl_public_encrypt($aesKey, $encryptedKey, $publicKey);
|
||||
$payload = json_encode([
|
||||
'key' => base64_encode($encryptedKey),
|
||||
'iv' => base64_encode($iv),
|
||||
'data' => base64_encode($encryptedData)
|
||||
]);
|
||||
|
||||
$filename = 'universal_transcoder.bin';
|
||||
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
header('Content-Length: ' . strlen($payload));
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
echo $payload;
|
||||
flush();
|
||||
|
||||
unlink($tmpZip);
|
||||
|
||||
break;
|
||||
|
||||
case 'restore':
|
||||
$jsonFiles = [
|
||||
'input.json',
|
||||
];
|
||||
|
||||
foreach ($jsonFiles as $json) {
|
||||
if (file_exists($json)) {
|
||||
unlink($json);
|
||||
}
|
||||
}
|
||||
|
||||
$tmpZip = sys_get_temp_dir() . '/restore.zip';
|
||||
|
||||
$upload = $_FILES['shree_bhattji_encoder'];
|
||||
|
||||
if ($upload['error'] !== UPLOAD_ERR_OK) {
|
||||
die('Upload failed');
|
||||
}
|
||||
|
||||
if (pathinfo($upload['name'], PATHINFO_EXTENSION) !== 'bin') {
|
||||
die('Invalid file type');
|
||||
}
|
||||
|
||||
$privateKeyPem = file_get_contents('/var/www/backup_private.pem');
|
||||
if (!$privateKeyPem) {
|
||||
die('Private key not found');
|
||||
}
|
||||
|
||||
$privateKey = openssl_pkey_get_private($privateKeyPem);
|
||||
if (!$privateKey) {
|
||||
die('Invalid private key');
|
||||
}
|
||||
|
||||
$payloadRaw = file_get_contents($upload['tmp_name']);
|
||||
$payload = json_decode($payloadRaw, true);
|
||||
|
||||
if (
|
||||
!is_array($payload)
|
||||
|| !isset($payload['key'], $payload['iv'], $payload['data'])
|
||||
) {
|
||||
die('Invalid backup file format');
|
||||
}
|
||||
|
||||
$encryptedKey = base64_decode($payload['key'], true);
|
||||
$iv = base64_decode($payload['iv'], true);
|
||||
$encryptedData = base64_decode($payload['data'], true);
|
||||
|
||||
if ($encryptedKey === false || $iv === false || $encryptedData === false) {
|
||||
die('Corrupt backup data');
|
||||
}
|
||||
|
||||
if (!openssl_private_decrypt($encryptedKey, $aesKey, $privateKey)) {
|
||||
die('Key mismatch or wrong private key');
|
||||
}
|
||||
|
||||
$zipBinary = openssl_decrypt(
|
||||
$encryptedData,
|
||||
'AES-256-CBC',
|
||||
$aesKey,
|
||||
OPENSSL_RAW_DATA,
|
||||
$iv
|
||||
);
|
||||
|
||||
if ($zipBinary === false) {
|
||||
die('Failed to decrypt data');
|
||||
}
|
||||
$tmpZip = sys_get_temp_dir() . '/restore_' . uniqid() . '.zip';
|
||||
file_put_contents($tmpZip, $zipBinary);
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($tmpZip) !== true) {
|
||||
unlink($tmpZip);
|
||||
die('Invalid ZIP archive');
|
||||
}
|
||||
|
||||
$zip->extractTo(__DIR__); // overwrites existing JSON
|
||||
$zip->close();
|
||||
|
||||
unlink($tmpZip);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
|
||||
?>
|
||||
<script>
|
||||
function confirmReboot() {
|
||||
return confirm("Are you sure you want to reboot?");
|
||||
}
|
||||
|
||||
function confirmReset() {
|
||||
return confirm("All settings will be gone . Are you sure you want to reset ?");
|
||||
}
|
||||
|
||||
function confirmUpdate() {
|
||||
return confirm("Newer version will be downloaded and installed Do not turn off power .");
|
||||
}
|
||||
|
||||
function confirmbackup() {
|
||||
return confirm("Are you sure you want to download backup ? ");
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card wide">
|
||||
Device ID :- <?php echo trim(file_get_contents('/sys/class/dmi/id/product_uuid')); ?><br>
|
||||
Project Name :- Urmi Digital Transcoder<br>
|
||||
Software Version :- <?php echo $version; ?> <br>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<form method="post" class="form-center">
|
||||
<button type="submit" name="action" value="backup" class="green-btn">Download Backup File</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<form method="post" class="form-center" onsubmit="return confirmReboot();">
|
||||
<button type="submit" name="action" value="reboot" class="green-btn">Reboot</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<form method="post" class="form-center" onsubmit="return confirmReset();">
|
||||
<button type="submit" name="action" value="reset" class="red-btn">Reset Settings</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<form method="post" class="form-center">
|
||||
<button type="submit" name="action" value="update" class="red-btn">Update Firmware</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<form method="post" class="form-center" enctype="multipart/form-data"
|
||||
onsubmit="return confirm('Are you sure you want to restore using this file ? All settings will be restored as per backup file .')">
|
||||
|
||||
<label>Select restore file (.bin only):</label><br><br>
|
||||
|
||||
<input type="file"
|
||||
name="shree_bhattji_encoder"
|
||||
accept=".bin"
|
||||
required><br><br>
|
||||
|
||||
<button type="submit" name="action" value="restore" class="red-btn">Restore</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<footer class="site-footer">
|
||||
Crafted with ❤️ by ShreeBhattJi ( Devdatt Bhatt ) • +91-8000-74-1919
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,408 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
session_start();
|
||||
|
||||
$ALLOWED_IP = '172.16.111.112';
|
||||
$clientIp = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
|
||||
$usersFile = '/var/www/users.json';
|
||||
|
||||
/* ---------- helpers ---------- */
|
||||
function load_json(string $file): array {
|
||||
return is_file($file) ? json_decode(file_get_contents($file), true) ?: [] : [];
|
||||
}
|
||||
function save_json(string $file, array $data): void {
|
||||
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT), LOCK_EX);
|
||||
}
|
||||
|
||||
/* ---------- CSRF ---------- */
|
||||
if (empty($_SESSION['csrf'])) {
|
||||
$_SESSION['csrf'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
/* ---------- HANDLE RESET ---------- */
|
||||
if ($clientIp === $ALLOWED_IP && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
|
||||
http_response_code(400);
|
||||
die('Invalid request');
|
||||
}
|
||||
|
||||
$username = strtolower(trim($_POST['username'] ?? ''));
|
||||
$password = $_POST['password'] ?? '';
|
||||
$confirm = $_POST['confirm'] ?? '';
|
||||
|
||||
if (!preg_match('/^[a-z0-9_]{3,32}$/', $username)) {
|
||||
$error = 'Invalid username format.';
|
||||
} elseif (strlen($password) < 8) {
|
||||
$error = 'Password must be at least 8 characters.';
|
||||
} elseif ($password !== $confirm) {
|
||||
$error = 'Passwords do not match.';
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$users = load_json($usersFile);
|
||||
$users[$username] = [
|
||||
'password' => password_hash($password, PASSWORD_DEFAULT)
|
||||
];
|
||||
save_json($usersFile, $users);
|
||||
$success = 'Username and password reset successfully.';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>URMIC powred by Shreebhattji</title>
|
||||
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-1: #0f172a;
|
||||
--bg-2: #1d4ed8;
|
||||
--bg-3: #22c55e;
|
||||
--accent: #f97316;
|
||||
--text-main: #f9fafb;
|
||||
--text-muted: #cbd5f5;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
sans-serif;
|
||||
color: var(--text-main);
|
||||
background:
|
||||
radial-gradient(circle at top left, #22c55e33, transparent 60%),
|
||||
radial-gradient(circle at bottom right, #f9731633, transparent 65%),
|
||||
linear-gradient(135deg, var(--bg-1), var(--bg-2));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-wrap {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1.5rem;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.rain-container {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.raindrop {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 70px;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0.9),
|
||||
rgba(255, 255, 255, 0)
|
||||
);
|
||||
opacity: 0.55;
|
||||
border-radius: 999px;
|
||||
filter: blur(0.3px);
|
||||
animation-name: fall;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes fall {
|
||||
0% {
|
||||
transform: translate3d(0, -120px, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(0, 110vh, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 720px;
|
||||
width: 100%;
|
||||
padding: 2.5rem 2rem;
|
||||
border-radius: 1.75rem;
|
||||
border: 1px solid rgba(148, 163, 184, 0.6);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(15, 23, 42, 0.9),
|
||||
rgba(15, 23, 42, 0.7)
|
||||
)
|
||||
border-box;
|
||||
backdrop-filter: blur(16px);
|
||||
box-shadow:
|
||||
0 20px 60px rgba(15, 23, 42, 0.7),
|
||||
0 0 0 1px rgba(15, 23, 42, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.25rem 0.7rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--text-main);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(59, 130, 246, 0.6),
|
||||
rgba(34, 197, 94, 0.75)
|
||||
);
|
||||
}
|
||||
|
||||
.badge-dot {
|
||||
width: 0.35rem;
|
||||
height: 0.35rem;
|
||||
border-radius: 999px;
|
||||
background: #bbf7d0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.1rem, 4vw, 2.8rem);
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
h1 span.highlight {
|
||||
font-size: clamp(1.1rem, 2vw, 1.8rem);
|
||||
background-image: linear-gradient(
|
||||
120deg,
|
||||
#22c55e,
|
||||
#a855f7,
|
||||
#f97316
|
||||
);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p.subtitle {
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-muted);
|
||||
max-width: 40rem;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.pill-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.65rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.pill {
|
||||
padding: 0.35rem 0.8rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.8);
|
||||
font-size: 0.75rem;
|
||||
color: #e5e7eb;
|
||||
background: radial-gradient(
|
||||
circle at top left,
|
||||
rgba(59, 130, 246, 0.22),
|
||||
rgba(15, 23, 42, 0.7)
|
||||
);
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: none;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 1px dashed rgba(148, 163, 184, 0.7);
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: #ffffff;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.card {
|
||||
padding: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.footer {
|
||||
align-items: flex-start;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="rain-container" id="rain"></div>
|
||||
|
||||
<div class="page-wrap">
|
||||
<main class="card">
|
||||
|
||||
<div class="badge">
|
||||
<span class="badge-dot"></span>
|
||||
<span>Password Recovery</span>
|
||||
</div>
|
||||
|
||||
<h1>
|
||||
Universal Encoder / Decoder
|
||||
<span class="highlight">powred by Shreebhattji</span>
|
||||
</h1>
|
||||
|
||||
<?php if ($clientIp !== $ALLOWED_IP): ?>
|
||||
|
||||
<p class="subtitle">
|
||||
Set you computer ip to <strong>172.16.111.112</strong> then
|
||||
Connect USB dongle to encoder and visit
|
||||
<strong>172.16.111.111</strong> for password reset
|
||||
|
||||
</p>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<p style="color:#fca5a5"><?= htmlspecialchars($error) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<p style="color:#86efac"><?= htmlspecialchars($success) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" autocomplete="off" style="display:flex;flex-direction:column;gap:0.75rem">
|
||||
|
||||
<input type="hidden" name="csrf" value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
|
||||
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
placeholder="New Username"
|
||||
required
|
||||
style="padding:0.7rem;border-radius:0.5rem;border:none"
|
||||
/>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="New Password"
|
||||
required
|
||||
style="padding:0.7rem;border-radius:0.5rem;border:none"
|
||||
/>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
name="confirm"
|
||||
placeholder="Confirm Password"
|
||||
required
|
||||
style="padding:0.7rem;border-radius:0.5rem;border:none"
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
style="padding:0.75rem;border-radius:0.6rem;border:none;
|
||||
background:#22c55e;color:#000;font-weight:600">
|
||||
Reset Credentials
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- ===== YOUR RAIN SCRIPT (UNCHANGED) ===== -->
|
||||
<script>
|
||||
(function () {
|
||||
const container = document.getElementById("rain");
|
||||
|
||||
function generateRain() {
|
||||
if (!container) return;
|
||||
container.innerHTML = "";
|
||||
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
let drops = Math.floor(width * 0.16);
|
||||
|
||||
if (window.innerWidth < 600) drops = Math.floor(width * 0.10);
|
||||
else if (window.innerWidth > 1400) drops = Math.floor(width * 0.18);
|
||||
|
||||
for (let i = 0; i < drops; i++) {
|
||||
const drop = document.createElement("span");
|
||||
drop.className = "raindrop";
|
||||
drop.style.left = Math.random() * 100 + "vw";
|
||||
drop.style.top = -120 - Math.random() * height * 0.3 + "px";
|
||||
drop.style.animationDuration = (0.7 + Math.random() * 1.2) + "s";
|
||||
drop.style.animationDelay = (Math.random() * -3) + "s";
|
||||
drop.style.opacity = (0.35 + Math.random() * 0.55).toFixed(2);
|
||||
container.appendChild(drop);
|
||||
}
|
||||
}
|
||||
|
||||
let resizeTimer;
|
||||
window.addEventListener("resize", () => {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(generateRain, 180);
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", generateRain);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,457 @@
|
|||
<?php
|
||||
require 'require_login.php';
|
||||
include 'static.php';
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>ShreeBhattJi</title>
|
||||
<script src="chart.js"></script>
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
:root {
|
||||
--header-h: 64px;
|
||||
--header-bg: linear-gradient(90deg, #1e293b, #0f172a);
|
||||
--header-color: #e6eef8;
|
||||
--footer-h: 52px;
|
||||
--footer-bg: linear-gradient(90deg, #1e293b, #0f172a);
|
||||
--footer-color: #e6eef8;
|
||||
}
|
||||
|
||||
main {
|
||||
height: calc(100vh - var(--header-h) - var(--footer-h));
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: 16px;
|
||||
padding-bottom: calc(var(--footer-h) + 16px);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1100px;
|
||||
margin: 12px auto;
|
||||
padding: 12px;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px rgba(2, 6, 23, 0.06);
|
||||
}
|
||||
|
||||
.containerindex {
|
||||
max-width: 1100px;
|
||||
margin: 12px auto;
|
||||
padding: 12px;
|
||||
padding-top: 160px;
|
||||
/* Adjusted for multiple headers */
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px rgba(2, 6, 23, 0.06);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.card {
|
||||
flex: 1 1 43%;
|
||||
min-width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.02));
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.card.wide {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.card .chart-wrap {
|
||||
flex: 1 1 auto;
|
||||
min-height: 180px;
|
||||
height: 247px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card canvas {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.status-row {
|
||||
margin-top: 12px;
|
||||
color: #9fb2d6;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
:root {
|
||||
--header-h: 56px;
|
||||
--footer-h: 56px;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.card {
|
||||
flex: 1 1 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.card .chart-wrap {
|
||||
height: 220px;
|
||||
min-height: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: var(--footer-h);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 0 32px;
|
||||
background: var(--footer-bg);
|
||||
color: var(--footer-color);
|
||||
z-index: 999;
|
||||
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.18);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* === MULTIPLE TOP HEADERS === */
|
||||
.top-header-1 {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #e6eef8;
|
||||
font-size: 17px;
|
||||
z-index: 1001;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.top-header-2 {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #e6eef8;
|
||||
font-size: 14px;
|
||||
z-index: 1001;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.top-header-1 {
|
||||
top: 0;
|
||||
background: #0f172a;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.top-header-2 {
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: var(--footer-h, 73px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 48px;
|
||||
margin-bottom: 57px;
|
||||
background: linear-gradient(90deg, #0f172a, #1e293b);
|
||||
color: #e6eef8;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.4px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
z-index: 999;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.top-header-2 nav a {
|
||||
margin-left: 28px;
|
||||
color: #e6eef8;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* === MAIN NAV HEADER === */
|
||||
.site-header {
|
||||
position: fixed;
|
||||
top: 90px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: var(--footer-h, 73px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 48px;
|
||||
margin-bottom: 57px;
|
||||
background: linear-gradient(90deg, #0f172a, #1e293b);
|
||||
color: #e6eef8;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.4px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
z-index: 999;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.site-header nav a {
|
||||
margin-left: 28px;
|
||||
color: #e6eef8;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.site-header nav a:hover {
|
||||
color: #38bdf8;
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 7px;
|
||||
}
|
||||
|
||||
.dropdown-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdown select {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
color: black;
|
||||
padding: 12px 40px 12px 16px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.dropdown select:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.dropdown::after {
|
||||
content: "▼";
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dropdown select option {
|
||||
background: #ffffff;
|
||||
color: #1e293b;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
position: relative;
|
||||
width: auto;
|
||||
min-width: 333px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.input-group input {
|
||||
width: 100%;
|
||||
padding: 11px 11px;
|
||||
font-size: 13px;
|
||||
border: 2px solid #cbd5e1;
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
background: white;
|
||||
transition: border-color 0.3s ease;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.input-group input:focus {
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
pointer-events: none;
|
||||
transition: 0.3s ease all;
|
||||
background: white;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.input-group input:focus+label,
|
||||
.input-group input:not(:placeholder-shown)+label {
|
||||
top: -6px;
|
||||
left: 8px;
|
||||
font-size: 12px;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.checkbox-group input[type="checkbox"] {
|
||||
accent-color: #3b82f6;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checkbox-group label {
|
||||
font-size: 13px;
|
||||
color: #1e293b;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.green-btn {
|
||||
background-color: green;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
width: 33%;
|
||||
border-radius: 7px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.red-btn {
|
||||
background-color: red;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
width: 33%;
|
||||
border-radius: 7px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.red-btn:hover {
|
||||
background-color: darkred;
|
||||
}
|
||||
|
||||
.form-center {
|
||||
text-align: center;
|
||||
}
|
||||
.social-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
}
|
||||
.social-btn {
|
||||
width: 67px;
|
||||
height: 67px;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
border-radius: 8px;
|
||||
background: #f3f4f6;
|
||||
text-decoration: none;
|
||||
transition: transform .12s, box-shadow .12s;
|
||||
}
|
||||
.social-btn:hover { transform: translateY(-3px); box-shadow: 0 6px 18px rgba(0,0,0,.08); }
|
||||
.social-btn svg { width: 22px; height: 22px; display: block; }
|
||||
.sr-only { position: absolute; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header class="top-header-1"><a href="index.php" style="color:white; text-decoration:none;">URMI Digital Transcoder</a></header>
|
||||
<header class="top-header-2">
|
||||
<nav aria-label="Top navigation">
|
||||
<a href="https://learn.urmic.org/" target="_blank">Tutorials</a>
|
||||
<a href="about_us.php">About Us</a>
|
||||
<a href="contact_us.php">Contact Us ( Free Service )</a>
|
||||
<a href="premium_service.php">Premium Service</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<header class="site-header">
|
||||
<nav aria-label="Top navigation">
|
||||
<a href="input.php">Input</a>
|
||||
<a href="index.php">Monitor</a>
|
||||
<a href="firewall.php">Firewall</a>
|
||||
<a href="firmware.php">Firmware</a>
|
||||
<a href="password.php">Password</a>
|
||||
<a href="logout.php">Logout</a>
|
||||
</nav>
|
||||
</header>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>URMIC powred by Shreebhattji</title>
|
||||
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-1: #0f172a;
|
||||
--bg-2: #1d4ed8;
|
||||
--bg-3: #22c55e;
|
||||
--accent: #f97316;
|
||||
--text-main: #f9fafb;
|
||||
--text-muted: #cbd5f5;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
sans-serif;
|
||||
color: var(--text-main);
|
||||
background:
|
||||
radial-gradient(circle at top left, #22c55e33, transparent 60%),
|
||||
radial-gradient(circle at bottom right, #f9731633, transparent 65%),
|
||||
linear-gradient(135deg, var(--bg-1), var(--bg-2));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-wrap {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1.5rem;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.rain-container {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.raindrop {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 70px;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0.9),
|
||||
rgba(255, 255, 255, 0)
|
||||
);
|
||||
opacity: 0.55;
|
||||
border-radius: 999px;
|
||||
filter: blur(0.3px);
|
||||
animation-name: fall;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes fall {
|
||||
0% {
|
||||
transform: translate3d(0, -120px, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(0, 110vh, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 720px;
|
||||
width: 100%;
|
||||
padding: 2.5rem 2rem;
|
||||
border-radius: 1.75rem;
|
||||
border: 1px solid rgba(148, 163, 184, 0.6);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(15, 23, 42, 0.9),
|
||||
rgba(15, 23, 42, 0.7)
|
||||
)
|
||||
border-box;
|
||||
backdrop-filter: blur(16px);
|
||||
box-shadow:
|
||||
0 20px 60px rgba(15, 23, 42, 0.7),
|
||||
0 0 0 1px rgba(15, 23, 42, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.25rem 0.7rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--text-main);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(59, 130, 246, 0.6),
|
||||
rgba(34, 197, 94, 0.75)
|
||||
);
|
||||
}
|
||||
|
||||
.badge-dot {
|
||||
width: 0.35rem;
|
||||
height: 0.35rem;
|
||||
border-radius: 999px;
|
||||
background: #bbf7d0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.1rem, 4vw, 2.8rem);
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
h1 span.highlight {
|
||||
font-size: clamp(1.1rem, 2vw, 1.8rem);
|
||||
background-image: linear-gradient(
|
||||
120deg,
|
||||
#22c55e,
|
||||
#a855f7,
|
||||
#f97316
|
||||
);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p.subtitle {
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-muted);
|
||||
max-width: 40rem;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.pill-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.65rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.pill {
|
||||
padding: 0.35rem 0.8rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.8);
|
||||
font-size: 0.75rem;
|
||||
color: #e5e7eb;
|
||||
background: radial-gradient(
|
||||
circle at top left,
|
||||
rgba(59, 130, 246, 0.22),
|
||||
rgba(15, 23, 42, 0.7)
|
||||
);
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: none;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 1px dashed rgba(148, 163, 184, 0.7);
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: #ffffff;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.card {
|
||||
padding: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.footer {
|
||||
align-items: flex-start;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="rain-container" id="rain"></div>
|
||||
|
||||
<div class="page-wrap">
|
||||
<main class="card">
|
||||
<div class="badge">
|
||||
<span class="badge-dot"></span>
|
||||
<span>Any format to any format</span>
|
||||
</div>
|
||||
|
||||
<h1>
|
||||
Universal Transcoder
|
||||
<span class="highlight">powred by Shreebhattji</span>
|
||||
</h1>
|
||||
|
||||
<p class="subtitle">
|
||||
Full Free Taining
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
10 Year Long Term Software Support
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
Industry wide format support
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
Wordwide availibility of hardware via hardware partners
|
||||
</p>
|
||||
|
||||
<div class="pill-row">
|
||||
<span class="pill"><a style="color: #ffffff;" href="https://www.facebook.com/WorldwideDigitalAssociation/">Facebook</a></span>
|
||||
<span class="pill"><a style="color: #ffffff;" href="https://www.linkedin.com/in/dbhatt-org/">Linkedin</a></span>
|
||||
<span class="pill"><a style="color: #ffffff;" href="https://wa.me/+918000741919">What's app</a></span>
|
||||
<span class="pill"><a style="color: #ffffff;" href="mailto:hello@urmic.org">Email</a></span>
|
||||
<span class="pill"><a style="color: #ffffff;" href="tel:+918000741919">Call Us</a></span>
|
||||
<span class="pill"><a style="color: #ffffff;" href="certification.html">Certificate</a></span>
|
||||
</div>
|
||||
<div class="pill-row">
|
||||
<span class="pill"><a style="color: #ffffff;" href="login.php">Login</a></span>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div class="brand">
|
||||
URMIC • <span>Shreebhattji</span>
|
||||
</div>
|
||||
<div class="links">
|
||||
<a href="https://urmic.org/trusted-partners/" class="link">Meet Out Partners</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const container = document.getElementById("rain");
|
||||
|
||||
function generateRain() {
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
// Density: more width -> more raindrops
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
|
||||
const baseDensity = 0.16; // drops per vw
|
||||
let drops = Math.floor(width * baseDensity);
|
||||
|
||||
if (window.innerWidth < 600) {
|
||||
drops = Math.floor(width * 0.10);
|
||||
} else if (window.innerWidth > 1400) {
|
||||
drops = Math.floor(width * 0.18);
|
||||
}
|
||||
|
||||
for (let i = 0; i < drops; i++) {
|
||||
const drop = document.createElement("span");
|
||||
drop.className = "raindrop";
|
||||
|
||||
const left = Math.random() * 100; // vw
|
||||
const delay = Math.random() * -3; // negative so it’s already falling
|
||||
const duration = 0.7 + Math.random() * 1.2; // seconds
|
||||
const offsetY = Math.random() * height * 0.3;
|
||||
|
||||
drop.style.left = left + "vw";
|
||||
drop.style.top = -120 - offsetY + "px";
|
||||
drop.style.animationDuration = duration + "s";
|
||||
drop.style.animationDelay = delay + "s";
|
||||
drop.style.opacity = (0.35 + Math.random() * 0.55).toFixed(2);
|
||||
|
||||
container.appendChild(drop);
|
||||
}
|
||||
}
|
||||
|
||||
// Basic debounce for resize
|
||||
let resizeTimer = null;
|
||||
window.addEventListener("resize", () => {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(generateRain, 180);
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", generateRain);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h3>CPU (%)</h3>
|
||||
<div class="chart-wrap"><canvas id="cpuChart"></canvas></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>RAM (%)</h3>
|
||||
<div class="chart-wrap"><canvas id="ramChart"></canvas></div>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<h3>Network (KB/s)</h3>
|
||||
<div class="chart-wrap"><canvas id="netChart"></canvas></div>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<h3>Disk I/O (KB/s) & Disk %</h3>
|
||||
<div class="chart-wrap"><canvas id="diskChart"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:12px; color:#9fb2d6; display:flex; justify-content:space-between;">
|
||||
<div>Last update: <span id="lastUpdate">—</span></div>
|
||||
<div>CPU: <span id="lastCpu">—</span>% · RAM: <span id="lastRam">—</span>% · In: <span id="lastIn">—</span>KB/s ·
|
||||
Out: <span id="lastOut">—</span>KB/s</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
</div>
|
||||
<script>
|
||||
const POLL_MS = 1000;
|
||||
const JSON_URL = "metrics.json";
|
||||
|
||||
function toKB(v) {
|
||||
return Math.round(v / 1024);
|
||||
}
|
||||
|
||||
const cpuChart = new Chart(document.getElementById('cpuChart').getContext('2d'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'CPU %',
|
||||
data: [],
|
||||
fill: false,
|
||||
tension: 0.2
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
min: 0,
|
||||
max: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const ramChart = new Chart(document.getElementById('ramChart').getContext('2d'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'RAM %',
|
||||
data: [],
|
||||
fill: false,
|
||||
tension: 0.2
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
min: 0,
|
||||
max: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const netChart = new Chart(document.getElementById('netChart').getContext('2d'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Net In (KB/s)',
|
||||
data: [],
|
||||
fill: false,
|
||||
tension: 0.2
|
||||
},
|
||||
{
|
||||
label: 'Net Out (KB/s)',
|
||||
data: [],
|
||||
fill: false,
|
||||
tension: 0.2
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const diskChart = new Chart(document.getElementById('diskChart').getContext('2d'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Disk Read (KB/s)',
|
||||
data: [],
|
||||
fill: false,
|
||||
tension: 0.2
|
||||
},
|
||||
{
|
||||
label: 'Disk Write (KB/s)',
|
||||
data: [],
|
||||
fill: false,
|
||||
tension: 0.2
|
||||
},
|
||||
{
|
||||
label: 'Disk %',
|
||||
data: [],
|
||||
yAxisID: 'percent',
|
||||
fill: false,
|
||||
tension: 0.2
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
position: 'left',
|
||||
beginAtZero: true
|
||||
},
|
||||
percent: {
|
||||
position: 'right',
|
||||
min: 0,
|
||||
max: 100,
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
callback: v => v + '%'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function update() {
|
||||
try {
|
||||
const res = await fetch(JSON_URL + "?_=" + Date.now(), {
|
||||
cache: 'no-store'
|
||||
});
|
||||
if (!res.ok) throw new Error('fetch fail ' + res.status);
|
||||
const j = await res.json();
|
||||
|
||||
const labels = j.timestamps.map(t => new Date(t).toLocaleTimeString());
|
||||
cpuChart.data.labels = labels;
|
||||
cpuChart.data.datasets[0].data = j.cpu_percent;
|
||||
|
||||
ramChart.data.labels = labels;
|
||||
ramChart.data.datasets[0].data = j.ram_percent;
|
||||
|
||||
netChart.data.labels = labels;
|
||||
netChart.data.datasets[0].data = j.net_in_Bps.map(toKB);
|
||||
netChart.data.datasets[1].data = j.net_out_Bps.map(toKB);
|
||||
|
||||
diskChart.data.labels = labels;
|
||||
diskChart.data.datasets[0].data = j.disk_read_Bps.map(toKB);
|
||||
diskChart.data.datasets[1].data = j.disk_write_Bps.map(toKB);
|
||||
diskChart.data.datasets[2].data = j.disk_percent;
|
||||
|
||||
cpuChart.update();
|
||||
ramChart.update();
|
||||
netChart.update();
|
||||
diskChart.update();
|
||||
|
||||
const last = labels.length - 1;
|
||||
if (last >= 0) {
|
||||
document.getElementById('lastUpdate').textContent = labels[last];
|
||||
document.getElementById('lastCpu').textContent = j.cpu_percent[last];
|
||||
document.getElementById('lastRam').textContent = j.ram_percent[last];
|
||||
document.getElementById('lastIn').textContent = toKB(j.net_in_Bps[last]);
|
||||
document.getElementById('lastOut').textContent = toKB(j.net_out_Bps[last]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('update failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(update, POLL_MS);
|
||||
update();
|
||||
</script>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
@ -0,0 +1,747 @@
|
|||
<?php include 'header.php'; ?>
|
||||
<?php
|
||||
$coreFile = "/var/www/core.json";
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
STATE HELPERS
|
||||
--------------------------------------------------------- */
|
||||
|
||||
function loadCoreState(): array
|
||||
{
|
||||
global $coreFile;
|
||||
if (!file_exists($coreFile)) {
|
||||
return ["cursor" => 0, "allocations" => []];
|
||||
}
|
||||
|
||||
$state = json_decode(file_get_contents($coreFile), true);
|
||||
return is_array($state) ? $state : ["cursor" => 0, "allocations" => []];
|
||||
}
|
||||
|
||||
function saveCoreState(array $state): void
|
||||
{
|
||||
global $coreFile;
|
||||
file_put_contents($coreFile, json_encode($state, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
CPU LIST PARSER
|
||||
--------------------------------------------------------- */
|
||||
|
||||
function parseCpuList(string $cpuList): array
|
||||
{
|
||||
$cpus = [];
|
||||
|
||||
foreach (explode(',', $cpuList) as $part) {
|
||||
if (strpos($part, '-') !== false) {
|
||||
[$start, $end] = array_map('intval', explode('-', $part));
|
||||
for ($i = $start; $i <= $end; $i++) {
|
||||
$cpus[] = $i;
|
||||
}
|
||||
} else {
|
||||
$cpus[] = (int)$part;
|
||||
}
|
||||
}
|
||||
|
||||
sort($cpus);
|
||||
return $cpus;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
NUMA PLAN BUILDER (PHYSICAL-FIRST, NODE ROUND-ROBIN)
|
||||
--------------------------------------------------------- */
|
||||
|
||||
function buildSequentialNumaPlan(): array
|
||||
{
|
||||
$nodes = [];
|
||||
$nodePaths = glob('/sys/devices/system/node/node*', GLOB_ONLYDIR);
|
||||
|
||||
foreach ($nodePaths as $nodePath) {
|
||||
$nodeId = (int)str_replace('node', '', basename($nodePath));
|
||||
$cpuList = trim(file_get_contents("$nodePath/cpulist"));
|
||||
$nodes[$nodeId] = parseCpuList($cpuList);
|
||||
}
|
||||
|
||||
ksort($nodes);
|
||||
$nodeIds = array_keys($nodes);
|
||||
|
||||
// Interleave CPUs across nodes: N0,C0 → N1,C0 → N0,C1 → N1,C1 ...
|
||||
$finalPlan = [];
|
||||
$maxCpus = max(array_map('count', $nodes));
|
||||
|
||||
for ($i = 0; $i < $maxCpus; $i++) {
|
||||
foreach ($nodeIds as $nid) {
|
||||
if (isset($nodes[$nid][$i])) {
|
||||
$finalPlan[] = [
|
||||
"node" => $nid,
|
||||
"cpu" => $nodes[$nid][$i],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $finalPlan;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
CORE ALLOCATOR (NUMA SAFE)
|
||||
--------------------------------------------------------- */
|
||||
|
||||
function allocateCore(int $serviceId): array
|
||||
{
|
||||
$state = loadCoreState();
|
||||
|
||||
// Already allocated
|
||||
if (isset($state["allocations"][$serviceId])) {
|
||||
return $state["allocations"][$serviceId];
|
||||
}
|
||||
|
||||
$plan = buildSequentialNumaPlan();
|
||||
$planCount = count($plan);
|
||||
|
||||
// Build occupied set as node:cpu
|
||||
$occupied = [];
|
||||
foreach ($state["allocations"] as $a) {
|
||||
$occupied[$a["node"] . ":" . $a["cpu"]] = true;
|
||||
}
|
||||
|
||||
// GAP FILLING (authoritative)
|
||||
foreach ($plan as $index => $slot) {
|
||||
$key = $slot["node"] . ":" . $slot["cpu"];
|
||||
if (!isset($occupied[$key])) {
|
||||
$state["allocations"][$serviceId] = $slot;
|
||||
$state["cursor"] = ($index + 1) % $planCount;
|
||||
saveCoreState($state);
|
||||
return $slot;
|
||||
}
|
||||
}
|
||||
|
||||
// OVERFLOW (true round-robin)
|
||||
$slot = $plan[$state["cursor"] % $planCount];
|
||||
$state["allocations"][$serviceId] = $slot;
|
||||
$state["cursor"] = ($state["cursor"] + 1) % $planCount;
|
||||
|
||||
saveCoreState($state);
|
||||
return $slot;
|
||||
}
|
||||
|
||||
function freeCore(int $serviceId): void
|
||||
{
|
||||
$state = loadCoreState();
|
||||
if (isset($state["allocations"][$serviceId])) {
|
||||
unset($state["allocations"][$serviceId]);
|
||||
saveCoreState($state);
|
||||
}
|
||||
}
|
||||
|
||||
function all_service_update()
|
||||
{
|
||||
unlink("/var/www/core.json");
|
||||
$script = __DIR__ . "/stop_all_encoders.sh";
|
||||
exec("sudo chmod +x " . $script);
|
||||
exec("sudo {$script} 2>&1", $output, $code);
|
||||
|
||||
$jsonFile = __DIR__ . "/input.json";
|
||||
if (!file_exists($jsonFile)) {
|
||||
die("input.json not found");
|
||||
}
|
||||
$data = json_decode(file_get_contents($jsonFile), true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
die("Invalid JSON format");
|
||||
}
|
||||
|
||||
foreach ($data as &$new) {
|
||||
$alloc = allocateCore($new["id"]);
|
||||
$core = (int)$alloc["cpu"];
|
||||
$node = (int)$alloc["node"];
|
||||
|
||||
$ffmpeg = 'numactl --cpunodebind=' . $node
|
||||
. ' --preferred=' . $node
|
||||
. ' taskset -c ' . $core
|
||||
. ' ffmpeg -hide_banner -loglevel info -thread_queue_size 512 -fflags +genpts+discardcorrupt+nobuffer -readrate 1.0'
|
||||
. ' -i "udp://' . $new["input_udp"] . '?fifo_size=50000&buffer_size=50000&overrun_nonfatal=1"'
|
||||
. ' -c:v copy '
|
||||
. ' -c:a ' . $new["audio_format"] . ' -b:a ' . $new["audio_bitrate"] . 'k -ar 48000 -ac 2 -af "volume=' . $new["volume"] . 'dB" '
|
||||
. ' -metadata service_provider="ShreeBhattJI" ';
|
||||
if ($new["program_id"] !== "") {
|
||||
$ffmpeg .= '-map i:p:' . $new["program_id"] . '" ';
|
||||
}
|
||||
if ($new["service_name"] !== "") {
|
||||
$ffmpeg .= ' -metadata service_name="' . $new["service_name"] . '" ';
|
||||
}
|
||||
$ffmpeg .= ' -f mpegts "udp://' . $new["output_udp"] . '?pkt_size=1316&flush_packets=1"';
|
||||
|
||||
file_put_contents("/var/www/encoder/" . $new["id"] . ".sh", $ffmpeg);
|
||||
|
||||
if ($new["service"] === "enable") {
|
||||
exec("sudo systemctl enable encoder@{$new["id"]}");
|
||||
exec("sudo systemctl restart encoder@{$new["id"]}");
|
||||
}
|
||||
}
|
||||
unset($new);
|
||||
file_put_contents(
|
||||
$jsonFile,
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
|
||||
);
|
||||
}
|
||||
|
||||
function all_service_start()
|
||||
{
|
||||
unlink("/var/www/core.json");
|
||||
$script = __DIR__ . "/stop_all_encoders.sh";
|
||||
exec("sudo chmod +x " . $script);
|
||||
exec("sudo {$script} 2>&1", $output, $code);
|
||||
|
||||
$jsonFile = __DIR__ . "/input.json";
|
||||
if (!file_exists($jsonFile)) {
|
||||
die("input.json not found");
|
||||
}
|
||||
$data = json_decode(file_get_contents($jsonFile), true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
die("Invalid JSON format");
|
||||
}
|
||||
|
||||
foreach ($data as &$new) {
|
||||
$alloc = allocateCore($new["id"]);
|
||||
$core = (int)$alloc["cpu"];
|
||||
$node = (int)$alloc["node"];
|
||||
$new["service"] = "enable";
|
||||
|
||||
$ffmpeg = 'numactl --cpunodebind=' . $node
|
||||
. ' --preferred=' . $node
|
||||
. ' taskset -c ' . $core
|
||||
. ' ffmpeg -hide_banner -loglevel info -thread_queue_size 512 -fflags +genpts+discardcorrupt+nobuffer -readrate 1.0'
|
||||
. ' -i "udp://' . $new["input_udp"] . '?fifo_size=50000&buffer_size=50000&overrun_nonfatal=1"'
|
||||
. ' -c:v copy '
|
||||
. ' -c:a ' . $new["audio_format"] . ' -b:a ' . $new["audio_bitrate"] . 'k -ar 48000 -ac 2 -af "volume=' . $new["volume"] . 'dB" '
|
||||
. ' -metadata service_provider="ShreeBhattJI" ';
|
||||
if ($new["program_id"] !== "") {
|
||||
$ffmpeg .= '-map i:p:' . $new["program_id"] . '" ';
|
||||
}
|
||||
if ($new["service_name"] !== "") {
|
||||
$ffmpeg .= ' -metadata service_name="' . $new["service_name"] . '" ';
|
||||
}
|
||||
$ffmpeg .= ' -f mpegts "udp://' . $new["output_udp"] . '?pkt_size=1316&flush_packets=1"';
|
||||
|
||||
|
||||
file_put_contents("/var/www/encoder/" . $new["id"] . ".sh", $ffmpeg);
|
||||
|
||||
if ($new["service"] === "enable") {
|
||||
exec("sudo systemctl enable encoder@{$new["id"]}");
|
||||
exec("sudo systemctl restart encoder@{$new["id"]}");
|
||||
}
|
||||
}
|
||||
unset($new);
|
||||
file_put_contents(
|
||||
$jsonFile,
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
|
||||
);
|
||||
}
|
||||
|
||||
function all_service_stop()
|
||||
{
|
||||
unlink("/var/www/core.json");
|
||||
$script = __DIR__ . "/stop_all_encoders.sh";
|
||||
exec("sudo chmod +x " . $script);
|
||||
exec("sudo {$script} 2>&1", $output, $code);
|
||||
|
||||
$jsonFile = __DIR__ . "/input.json";
|
||||
if (!file_exists($jsonFile)) {
|
||||
die("input.json not found");
|
||||
}
|
||||
$data = json_decode(file_get_contents($jsonFile), true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
die("Invalid JSON format");
|
||||
}
|
||||
|
||||
foreach ($data as &$new) {
|
||||
if (isset($new["service"]) && $new["service"] === "enable") {
|
||||
$new["service"] = "disable";
|
||||
}
|
||||
}
|
||||
unset($new);
|
||||
file_put_contents(
|
||||
$jsonFile,
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
|
||||
);
|
||||
}
|
||||
|
||||
$jsonFile = __DIR__ . "/input.json";
|
||||
if (!file_exists($jsonFile)) {
|
||||
file_put_contents($jsonFile, json_encode([]));
|
||||
}
|
||||
$data = json_decode(file_get_contents($jsonFile), true);
|
||||
|
||||
foreach ($data as $k => $d) {
|
||||
if (!isset($d["service_name"])) $data[$k]["service_name"] = "";
|
||||
if (!isset($d["volume"])) $data[$k]["volume"] = "0";
|
||||
}
|
||||
file_put_contents($jsonFile, json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
switch ($_POST["action"]) {
|
||||
case "add":
|
||||
$new = [
|
||||
"id" => time(),
|
||||
"service_name" => $_POST["service_name"],
|
||||
"input_udp" => $_POST["input_udp"],
|
||||
"output_udp" => $_POST["output_udp"],
|
||||
"audio_format" => $_POST["audio_format"],
|
||||
"program_id" => $_POST["program_id"],
|
||||
"audio_bitrate" => $_POST["audio_bitrate"],
|
||||
"volume" => $_POST["volume"],
|
||||
"service" => $_POST["service"]
|
||||
];
|
||||
|
||||
$data[] = $new;
|
||||
file_put_contents($jsonFile, json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
$alloc = allocateCore($new["id"]);
|
||||
$core = (int)$alloc["cpu"];
|
||||
$node = (int)$alloc["node"];
|
||||
|
||||
$ffmpeg = 'numactl --cpunodebind=' . $node
|
||||
. ' --preferred=' . $node
|
||||
. ' taskset -c ' . $core
|
||||
. ' ffmpeg -hide_banner -loglevel info -thread_queue_size 512 -fflags +genpts+discardcorrupt+nobuffer -readrate 1.0'
|
||||
. ' -i "udp://' . $new["input_udp"] . '?fifo_size=50000&buffer_size=50000&overrun_nonfatal=1"'
|
||||
. ' -c:v copy '
|
||||
. ' -c:a ' . $new["audio_format"] . ' -b:a ' . $new["audio_bitrate"] . 'k -ar 48000 -ac 2 -af "volume=' . $new["volume"] . 'dB" '
|
||||
. ' -metadata service_provider="ShreeBhattJI" ';
|
||||
if ($new["program_id"] !== "") {
|
||||
$ffmpeg .= '-map i:p:' . $new["program_id"] . '" ';
|
||||
}
|
||||
if ($new["service_name"] !== "") {
|
||||
$ffmpeg .= ' -metadata service_name="' . $new["service_name"] . '" ';
|
||||
}
|
||||
$ffmpeg .= ' -f mpegts "udp://' . $new["output_udp"] . '?pkt_size=1316&flush_packets=1"';
|
||||
|
||||
|
||||
|
||||
file_put_contents("/var/www/encoder/" . $new["id"] . ".sh", $ffmpeg);
|
||||
|
||||
if ($new["service"] === "enable") {
|
||||
exec("sudo systemctl enable encoder@{$new["id"]}");
|
||||
exec("sudo systemctl restart encoder@{$new["id"]}");
|
||||
}
|
||||
echo "OK";
|
||||
exit;
|
||||
break;
|
||||
case "delete":
|
||||
$id = intval($_POST["id"]);
|
||||
$newData = [];
|
||||
|
||||
foreach ($data as $row) {
|
||||
if ($row["id"] != $id) $newData[] = $row;
|
||||
}
|
||||
|
||||
file_put_contents($jsonFile, json_encode($newData, JSON_PRETTY_PRINT));
|
||||
exec("sudo systemctl stop encoder@$id");
|
||||
exec("sudo systemctl disable encoder@$id");
|
||||
freeCore($id);
|
||||
|
||||
if (file_exists("/var/www/encoder/$id.sh")) unlink("/var/www/encoder/$id.sh");
|
||||
|
||||
echo "OK";
|
||||
exit;
|
||||
break;
|
||||
case "edit":
|
||||
|
||||
$id = intval($_POST["id"]);
|
||||
$newData = [];
|
||||
|
||||
foreach ($data as $row) {
|
||||
if ($row["id"] == $id) {
|
||||
|
||||
$row = [
|
||||
"id" => $id,
|
||||
"service_name" => $_POST["service_name"],
|
||||
"input_udp" => $_POST["input_udp"],
|
||||
"output_udp" => $_POST["output_udp"],
|
||||
"video_format" => $_POST["video_format"],
|
||||
"audio_format" => $_POST["audio_format"],
|
||||
"resolution" => $_POST["resolution"],
|
||||
"video_bitrate" => $_POST["video_bitrate"],
|
||||
"audio_bitrate" => $_POST["audio_bitrate"],
|
||||
"volume" => $_POST["volume"],
|
||||
"service" => $_POST["service"]
|
||||
];
|
||||
|
||||
$new = $row;
|
||||
$alloc = allocateCore($new["id"]);
|
||||
$core = (int)$alloc["cpu"];
|
||||
$node = (int)$alloc["node"];
|
||||
|
||||
$ffmpeg = 'numactl --cpunodebind=' . $node
|
||||
. ' --preferred=' . $node
|
||||
. ' taskset -c ' . $core
|
||||
. ' ffmpeg -hide_banner -loglevel info -thread_queue_size 512 -fflags +genpts+discardcorrupt+nobuffer -readrate 1.0'
|
||||
. ' -i "udp://' . $new["input_udp"] . '?fifo_size=50000&buffer_size=50000&overrun_nonfatal=1"'
|
||||
. ' -c:v copy '
|
||||
. ' -c:a ' . $new["audio_format"] . ' -b:a ' . $new["audio_bitrate"] . 'k -ar 48000 -ac 2 -af "volume=' . $new["volume"] . 'dB" '
|
||||
. ' -metadata service_provider="ShreeBhattJI" ';
|
||||
if ($new["program_id"] !== "") {
|
||||
$ffmpeg .= '-map i:p:' . $new["program_id"] . '" ';
|
||||
}
|
||||
if ($new["service_name"] !== "") {
|
||||
$ffmpeg .= ' -metadata service_name="' . $new["service_name"] . '" ';
|
||||
}
|
||||
$ffmpeg .= ' -f mpegts "udp://' . $new["output_udp"] . '?pkt_size=1316&flush_packets=1"';
|
||||
|
||||
file_put_contents("/var/www/encoder/$id.sh", $ffmpeg);
|
||||
|
||||
if ($new["service"] === "enable") {
|
||||
exec("sudo systemctl enable encoder@$id");
|
||||
exec("sudo systemctl restart encoder@$id");
|
||||
} else {
|
||||
exec("sudo systemctl stop encoder@$id");
|
||||
exec("sudo systemctl disable encoder@$id");
|
||||
}
|
||||
}
|
||||
|
||||
$newData[] = $row;
|
||||
}
|
||||
|
||||
file_put_contents($jsonFile, json_encode($newData, JSON_PRETTY_PRINT));
|
||||
echo "OK";
|
||||
exit;
|
||||
break;
|
||||
case "restart":
|
||||
$id = intval($_POST["id"]);
|
||||
exec("sudo systemctl restart encoder@$id");
|
||||
echo "OK";
|
||||
exit;
|
||||
break;
|
||||
case "start_all":
|
||||
all_service_start();
|
||||
break;
|
||||
case "stop_all":
|
||||
all_service_stop();
|
||||
break;
|
||||
case "update_all":
|
||||
all_service_update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.restart-btn {
|
||||
background: #ffaa00;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #b40000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
background: #0066cc;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#popup {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
border: 1px solid #333;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
#overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 6px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
|
||||
<h2>Service List</h2>
|
||||
<div style="margin-top:10px;">
|
||||
<button onclick="openAddPopup()">Add Service</button>
|
||||
<button onclick="submitAction('start_all')">Start All</button>
|
||||
<button onclick="submitAction('stop_all')">Stop All</button>
|
||||
<button onclick="submitAction('update_all')">Update All</button>
|
||||
</div>
|
||||
|
||||
<form id="actionForm" method="post" style="display:none;">
|
||||
<input type="hidden" name="action" id="action">
|
||||
</form>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>ID</th>
|
||||
<th>Service Name</th>
|
||||
<th>Input</th>
|
||||
<th>Output</th>
|
||||
<th>Audio</th>
|
||||
<th>Program Id</th>
|
||||
<th>A-Bitrate</th>
|
||||
<th>Volume (dB)</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
<?php $i = 1; ?>
|
||||
<?php foreach ($data as $row): ?>
|
||||
<tr>
|
||||
<td><?= $i++ ?></td>
|
||||
<td><?= $row['id'] ?></td>
|
||||
<td><?= $row["service_name"] ?></td>
|
||||
<td><?= $row["input_udp"] ?></td>
|
||||
<td><?= $row["output_udp"] ?></td>
|
||||
<td><?= $row["audio_format"] ?></td>
|
||||
<td><?= $row["program_id"] ?></td>
|
||||
<td><?= $row["audio_bitrate"] ?></td>
|
||||
<td><?= $row["volume"] ?> dB</td>
|
||||
<td><?= $row["service"] ?></td>
|
||||
|
||||
<td style="margin-top:3px;">
|
||||
<button class="edit-btn" onclick='openEditPopup(<?= json_encode($row) ?>)'>Edit</button>
|
||||
<button class="restart-btn" onclick="restartService(<?= $row['id'] ?>)">Restart</button>
|
||||
<button class="delete-btn" onclick="deleteService(<?= $row['id'] ?>)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<!-- POPUP -->
|
||||
<div id="overlay"></div>
|
||||
<div id="popup">
|
||||
<h3 id="popup_title">Add Service</h3>
|
||||
|
||||
<input type="hidden" id="service_id">
|
||||
|
||||
<input type="text" id="service_name" placeholder="Service Name">
|
||||
|
||||
<input type="text" id="in_udp" placeholder="Input UDP">
|
||||
<input type="text" id="out_udp" placeholder="Output UDP">
|
||||
|
||||
<select id="audio_format">
|
||||
<option value="mp2" selected>MP2</option>
|
||||
</select>
|
||||
|
||||
<input type="text" id="program_id" placeholder="Program ID">
|
||||
|
||||
<input type="text" id="audio_bitrate" placeholder="Audio Bitrate">
|
||||
|
||||
<select id="volume">
|
||||
<option value="-4">-4 dB</option>
|
||||
<option value="-3">-3 dB</option>
|
||||
<option value="-2">-2 dB</option>
|
||||
<option value="-1">-1 dB</option>
|
||||
<option value="0">0 dB</option>
|
||||
<option value="1">1 dB</option>
|
||||
<option value="2">2 dB</option>
|
||||
<option value="3">3 dB</option>
|
||||
<option value="4">4 dB</option>
|
||||
<option value="5">5 dB</option>
|
||||
<option value="10">10 dB</option>
|
||||
<option value="12">12 dB</option>
|
||||
<option value="15">15 dB</option>
|
||||
</select>
|
||||
|
||||
<select id="service">
|
||||
<option value="enable">Enable</option>
|
||||
<option value="disable">Disable</option>
|
||||
</select>
|
||||
|
||||
<button id="saveBtn" onclick="saveService()">Save</button>
|
||||
<button onclick="closePopup()">Close</button>
|
||||
<br>
|
||||
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openAddPopup() {
|
||||
document.getElementById("popup_title").innerText = "Add Service";
|
||||
document.getElementById("saveBtn").setAttribute("onclick", "saveService()");
|
||||
clearFields();
|
||||
showPopup();
|
||||
}
|
||||
|
||||
function openEditPopup(row) {
|
||||
document.getElementById("popup_title").innerText = "Edit Service";
|
||||
|
||||
service_id.value = row.id;
|
||||
service_name.value = row.service_name;
|
||||
in_udp.value = row.input_udp;
|
||||
out_udp.value = row.output_udp;
|
||||
video_format.value = row.video_format;
|
||||
audio_format.value = row.audio_format;
|
||||
resolution.value = row.resolution;
|
||||
video_bitrate.value = row.video_bitrate;
|
||||
audio_bitrate.value = row.audio_bitrate;
|
||||
volume.value = row.volume;
|
||||
service.value = row.service;
|
||||
|
||||
document.getElementById("saveBtn").setAttribute("onclick", "updateService()");
|
||||
showPopup();
|
||||
}
|
||||
|
||||
function showPopup() {
|
||||
overlay.style.display = "block";
|
||||
popup.style.display = "block";
|
||||
}
|
||||
|
||||
function closePopup() {
|
||||
overlay.style.display = "none";
|
||||
popup.style.display = "none";
|
||||
}
|
||||
|
||||
function clearFields() {
|
||||
service_id.value = "";
|
||||
service_name.value = "";
|
||||
in_udp.value = "";
|
||||
out_udp.value = "";
|
||||
video_format.value = "mpeg2video";
|
||||
audio_format.value = "mp2";
|
||||
resolution.value = "720:576";
|
||||
video_bitrate.value = "3000";
|
||||
audio_bitrate.value = "96";
|
||||
volume.value = "0";
|
||||
service.value = "enable";
|
||||
}
|
||||
|
||||
function saveService() {
|
||||
let form = new FormData();
|
||||
form.append("action", "add");
|
||||
form.append("service_name", service_name.value);
|
||||
form.append("input_udp", in_udp.value);
|
||||
form.append("output_udp", out_udp.value);
|
||||
form.append("video_format", video_format.value);
|
||||
form.append("audio_format", audio_format.value);
|
||||
form.append("resolution", resolution.value);
|
||||
form.append("video_bitrate", video_bitrate.value);
|
||||
form.append("audio_bitrate", audio_bitrate.value);
|
||||
form.append("volume", volume.value);
|
||||
form.append("service", service.value);
|
||||
|
||||
fetch("input.php", {
|
||||
method: "POST",
|
||||
body: form
|
||||
})
|
||||
.then(r => r.text())
|
||||
.then(res => {
|
||||
if (res.includes("OK")) location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function updateService() {
|
||||
let form = new FormData();
|
||||
form.append("action", "edit");
|
||||
form.append("id", service_id.value);
|
||||
|
||||
form.append("service_name", service_name.value);
|
||||
form.append("input_udp", in_udp.value);
|
||||
form.append("output_udp", out_udp.value);
|
||||
form.append("video_format", video_format.value);
|
||||
form.append("audio_format", audio_format.value);
|
||||
form.append("resolution", resolution.value);
|
||||
form.append("video_bitrate", video_bitrate.value);
|
||||
form.append("audio_bitrate", audio_bitrate.value);
|
||||
form.append("volume", volume.value);
|
||||
form.append("service", service.value);
|
||||
|
||||
fetch("input.php", {
|
||||
method: "POST",
|
||||
body: form
|
||||
})
|
||||
.then(r => r.text())
|
||||
.then(res => {
|
||||
if (res.includes("OK")) location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function deleteService(id) {
|
||||
if (!confirm("Delete service?")) return;
|
||||
|
||||
let form = new FormData();
|
||||
form.append("action", "delete");
|
||||
form.append("id", id);
|
||||
|
||||
fetch("input.php", {
|
||||
method: "POST",
|
||||
body: form
|
||||
})
|
||||
.then(r => r.text())
|
||||
.then(res => {
|
||||
if (res.includes("OK")) location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function restartService(id) {
|
||||
if (!confirm("Restart?")) return;
|
||||
|
||||
let form = new FormData();
|
||||
form.append("action", "restart");
|
||||
form.append("id", id);
|
||||
|
||||
fetch("input.php", {
|
||||
method: "POST",
|
||||
body: form
|
||||
})
|
||||
.then(r => r.text())
|
||||
.then(res => {
|
||||
if (res.includes("OK")) alert("Service restarted");
|
||||
});
|
||||
}
|
||||
|
||||
function submitAction(action) {
|
||||
const msg = {
|
||||
start_all: "Are you sure you want to START all services?",
|
||||
stop_all: "Are you sure you want to STOP all services?",
|
||||
update_all: "Are you sure you want to UPDATE all services?"
|
||||
};
|
||||
|
||||
if (!msg[action]) return;
|
||||
|
||||
if (confirm(msg[action])) {
|
||||
document.getElementById('action').value = action;
|
||||
document.getElementById('actionForm').submit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
@ -0,0 +1,441 @@
|
|||
<?php
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
session_start();
|
||||
|
||||
if (!empty($_SESSION['user'])) {
|
||||
header('Location: /index.php', true, 302);
|
||||
exit;
|
||||
}
|
||||
/* ---------- CONFIG ---------- */
|
||||
$usersFile = '/var/www/users.json';
|
||||
$attemptsFile = '/var/www/attempts.json';
|
||||
|
||||
$MAX_ATTEMPTS = 3;
|
||||
$LOCK_TIME = 3600;
|
||||
|
||||
/* ---------- HELPERS ---------- */
|
||||
function client_ip(): string
|
||||
{
|
||||
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
}
|
||||
|
||||
function load_json($file): array
|
||||
{
|
||||
return is_file($file) ? json_decode(file_get_contents($file), true) ?: [] : [];
|
||||
}
|
||||
|
||||
function save_json($file, $data): void
|
||||
{
|
||||
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT), LOCK_EX);
|
||||
}
|
||||
|
||||
/* ---------- CSRF ---------- */
|
||||
if (empty($_SESSION['csrf'])) {
|
||||
$_SESSION['csrf'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
/* ---------- RATE LIMIT ---------- */
|
||||
$ip = client_ip();
|
||||
$attempts = load_json($attemptsFile);
|
||||
|
||||
if (isset($attempts[$ip])) {
|
||||
if (
|
||||
$attempts[$ip]['count'] >= $MAX_ATTEMPTS &&
|
||||
time() - $attempts[$ip]['last'] < $LOCK_TIME
|
||||
) {
|
||||
http_response_code(429);
|
||||
die("Too many attempts. Try again later.");
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- LOGIN ---------- */
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
|
||||
http_response_code(400);
|
||||
die('Invalid request');
|
||||
}
|
||||
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
if (empty($_POST['agree'])) {
|
||||
|
||||
$error = 'You must agree to the Privacy Policy and Terms & Conditions.';
|
||||
echo '<script>alert("'
|
||||
. htmlspecialchars($error, ENT_QUOTES)
|
||||
. '");</script>';
|
||||
}
|
||||
$users = load_json($usersFile);
|
||||
|
||||
$valid = isset($users[$username]) &&
|
||||
password_verify($password, $users[$username]['password']);
|
||||
|
||||
if ($valid) {
|
||||
session_regenerate_id(true);
|
||||
unset($attempts[$ip]);
|
||||
save_json($attemptsFile, $attempts);
|
||||
$_SESSION['user'] = $username;
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Failed login
|
||||
$attempts[$ip]['count'] = ($attempts[$ip]['count'] ?? 0) + 1;
|
||||
$attempts[$ip]['last'] = time();
|
||||
save_json($attemptsFile, $attempts);
|
||||
|
||||
$error = 'Invalid username or password';
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>URMIC powred by Shreebhattji</title>
|
||||
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-1: #0f172a;
|
||||
--bg-2: #1d4ed8;
|
||||
--bg-3: #22c55e;
|
||||
--accent: #f97316;
|
||||
--text-main: #f9fafb;
|
||||
--text-muted: #cbd5f5;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
sans-serif;
|
||||
color: var(--text-main);
|
||||
background:
|
||||
radial-gradient(circle at top left, #22c55e33, transparent 60%),
|
||||
radial-gradient(circle at bottom right, #f9731633, transparent 65%),
|
||||
linear-gradient(135deg, var(--bg-1), var(--bg-2));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-wrap {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1.5rem;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.rain-container {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.raindrop {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 70px;
|
||||
background: linear-gradient(to bottom,
|
||||
rgba(255, 255, 255, 0.9),
|
||||
rgba(255, 255, 255, 0));
|
||||
opacity: 0.55;
|
||||
border-radius: 999px;
|
||||
filter: blur(0.3px);
|
||||
animation-name: fall;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes fall {
|
||||
0% {
|
||||
transform: translate3d(0, -120px, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 110vh, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 720px;
|
||||
width: 100%;
|
||||
padding: 2.5rem 2rem;
|
||||
border-radius: 1.75rem;
|
||||
border: 1px solid rgba(148, 163, 184, 0.6);
|
||||
background: linear-gradient(135deg,
|
||||
rgba(15, 23, 42, 0.9),
|
||||
rgba(15, 23, 42, 0.7)) border-box;
|
||||
backdrop-filter: blur(16px);
|
||||
box-shadow:
|
||||
0 20px 60px rgba(15, 23, 42, 0.7),
|
||||
0 0 0 1px rgba(15, 23, 42, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.25rem 0.7rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--text-main);
|
||||
background: linear-gradient(90deg,
|
||||
rgba(59, 130, 246, 0.6),
|
||||
rgba(34, 197, 94, 0.75));
|
||||
}
|
||||
|
||||
.badge-dot {
|
||||
width: 0.35rem;
|
||||
height: 0.35rem;
|
||||
border-radius: 999px;
|
||||
background: #bbf7d0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.1rem, 4vw, 2.8rem);
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
h1 span.highlight {
|
||||
font-size: clamp(1.1rem, 2vw, 1.8rem);
|
||||
background-image: linear-gradient(120deg,
|
||||
#22c55e,
|
||||
#a855f7,
|
||||
#f97316);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
p.subtitle {
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-muted);
|
||||
max-width: 40rem;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.pill-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.65rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.pill {
|
||||
padding: 0.35rem 0.8rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.8);
|
||||
font-size: 0.75rem;
|
||||
color: #e5e7eb;
|
||||
background: radial-gradient(circle at top left,
|
||||
rgba(59, 130, 246, 0.22),
|
||||
rgba(15, 23, 42, 0.7));
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: none;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 1px dashed rgba(148, 163, 184, 0.7);
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: #ffffff;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.card {
|
||||
padding: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.footer {
|
||||
align-items: flex-start;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="rain-container" id="rain"></div>
|
||||
|
||||
<div class="page-wrap">
|
||||
<main class="card">
|
||||
|
||||
<h1>Welcome</h1>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<p style="color:#fca5a5"><?= htmlspecialchars($error) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf" value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
|
||||
|
||||
<div style="display:flex;flex-direction:column;gap:0.75rem">
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
placeholder="Username"
|
||||
required
|
||||
style="padding:0.7rem;border-radius:0.5rem;border:none" />
|
||||
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
required
|
||||
style="padding:0.7rem;border-radius:0.5rem;border:none" />
|
||||
<label style="display:flex; gap:0.5rem; align-items:flex-start; font-size:0.8rem; color:#e5e7eb;">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="agree"
|
||||
value="1"
|
||||
required
|
||||
style="margin-top:0.15rem">
|
||||
<span>
|
||||
I agree to the
|
||||
<a href="https://urmic.org/2025/12/31/privacy-policy-and-terms-conditions-for-encoder/" target="_blank" class="link">Privacy Policy</a>
|
||||
and
|
||||
<a href="https://urmic.org/2025/12/31/privacy-policy-and-terms-conditions-for-encoder/" target="_blank" class="link">Terms & Conditions</a>
|
||||
</span>
|
||||
</label>
|
||||
<button
|
||||
type="submit"
|
||||
style="padding:0.75rem;border-radius:0.6rem;border:none;
|
||||
background:#22c55e;color:#000;font-weight:600">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div style="margin-top:0.75rem">
|
||||
<a href="forgot.php" class="link">Forgot password?</a>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="brand">
|
||||
URMIC • <span>Shreebhattji</span>
|
||||
</div>
|
||||
<div class="links">
|
||||
<a href="https://urmic.org/trusted-partners/" class="link">Meet Out Partners</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const container = document.getElementById("rain");
|
||||
|
||||
function generateRain() {
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = "";
|
||||
|
||||
// Density: more width -> more raindrops
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
|
||||
const baseDensity = 0.16; // drops per vw
|
||||
let drops = Math.floor(width * baseDensity);
|
||||
|
||||
if (window.innerWidth < 600) {
|
||||
drops = Math.floor(width * 0.10);
|
||||
} else if (window.innerWidth > 1400) {
|
||||
drops = Math.floor(width * 0.18);
|
||||
}
|
||||
|
||||
for (let i = 0; i < drops; i++) {
|
||||
const drop = document.createElement("span");
|
||||
drop.className = "raindrop";
|
||||
|
||||
const left = Math.random() * 100; // vw
|
||||
const delay = Math.random() * -3; // negative so it’s already falling
|
||||
const duration = 0.7 + Math.random() * 1.2; // seconds
|
||||
const offsetY = Math.random() * height * 0.3;
|
||||
|
||||
drop.style.left = left + "vw";
|
||||
drop.style.top = -120 - offsetY + "px";
|
||||
drop.style.animationDuration = duration + "s";
|
||||
drop.style.animationDelay = delay + "s";
|
||||
drop.style.opacity = (0.35 + Math.random() * 0.55).toFixed(2);
|
||||
|
||||
container.appendChild(drop);
|
||||
}
|
||||
}
|
||||
|
||||
// Basic debounce for resize
|
||||
let resizeTimer = null;
|
||||
window.addEventListener("resize", () => {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(generateRain, 180);
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", generateRain);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
session_start();
|
||||
|
||||
$_SESSION = [];
|
||||
|
||||
if (ini_get('session.use_cookies')) {
|
||||
$p = session_get_cookie_params();
|
||||
setcookie(
|
||||
session_name(),
|
||||
'',
|
||||
time() - 42000,
|
||||
$p['path'],
|
||||
$p['domain'],
|
||||
$p['secure'],
|
||||
$p['httponly']
|
||||
);
|
||||
}
|
||||
|
||||
session_destroy();
|
||||
|
||||
header('Location: /login.php');
|
||||
exit;
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
include 'header.php';
|
||||
?>
|
||||
<?php
|
||||
$usersFile = '/var/www/users.json';
|
||||
function load_json(string $file): array
|
||||
{
|
||||
return is_file($file) ? json_decode(file_get_contents($file), true) ?: [] : [];
|
||||
}
|
||||
function save_json(string $file, array $data): void
|
||||
{
|
||||
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT), LOCK_EX);
|
||||
}
|
||||
if (empty($_SESSION['csrf'])) {
|
||||
$_SESSION['csrf'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
$currentUser = $_SESSION['user'];
|
||||
$users = load_json($usersFile);
|
||||
|
||||
if (!isset($users[$currentUser])) {
|
||||
// Safety fallback
|
||||
session_destroy();
|
||||
header('Location: /login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
/* ---------- POST ---------- */
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
|
||||
http_response_code(400);
|
||||
die('Invalid request');
|
||||
}
|
||||
|
||||
$newUsername = strtolower(trim($_POST['new_username'] ?? ''));
|
||||
$currentPass = $_POST['current_password'] ?? '';
|
||||
$newPass = $_POST['new_password'] ?? '';
|
||||
$confirmPass = $_POST['confirm_password'] ?? '';
|
||||
|
||||
// Verify current password
|
||||
if (!password_verify($currentPass, $users[$currentUser]['password'])) {
|
||||
$error = 'Current password is incorrect.';
|
||||
}
|
||||
|
||||
// Validate new password if provided
|
||||
if (!$error && $newPass !== '') {
|
||||
if (strlen($newPass) < 8) {
|
||||
$error = 'New password must be at least 8 characters.';
|
||||
} elseif ($newPass !== $confirmPass) {
|
||||
$error = 'New passwords do not match.';
|
||||
}
|
||||
}
|
||||
|
||||
// Validate new username if provided
|
||||
if (!$error && $newUsername !== '' && $newUsername !== $currentUser) {
|
||||
if (!preg_match('/^[a-z0-9_]{3,32}$/', $newUsername)) {
|
||||
$error = 'Username must be 3–32 chars (a–z, 0–9, underscore).';
|
||||
} elseif (isset($users[$newUsername])) {
|
||||
$error = 'Username already exists.';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
// Apply changes
|
||||
$updatedUser = $currentUser;
|
||||
|
||||
if ($newPass !== '') {
|
||||
$users[$currentUser]['password'] =
|
||||
password_hash($newPass, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
if ($newUsername !== '' && $newUsername !== $currentUser) {
|
||||
$users[$newUsername] = $users[$currentUser];
|
||||
unset($users[$currentUser]);
|
||||
$updatedUser = $newUsername;
|
||||
}
|
||||
|
||||
save_json($usersFile, $users);
|
||||
|
||||
// Update session safely
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user'] = $updatedUser;
|
||||
|
||||
$success = 'Credentials updated successfully.';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card wide">
|
||||
<h3>Change Username / Password</h3>
|
||||
<?php if ($error): ?>
|
||||
<p style="color:#dc2626"><?= htmlspecialchars($error) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<p style="color:#16a34a"><?= htmlspecialchars($success) ?></p>
|
||||
<?php endif; ?>
|
||||
<form method="post" autocomplete="off">
|
||||
<input type="hidden" name="csrf" value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
|
||||
|
||||
<p>
|
||||
<label>New Username (optional)</label><br>
|
||||
<input type="text" name="new_username" placeholder="leave blank to keep current">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>Current Password (required)</label><br>
|
||||
<input type="password" name="current_password" required>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>New Password (optional)</label><br>
|
||||
<input type="password" name="new_password">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>Confirm New Password</label><br>
|
||||
<input type="password" name="confirm_password">
|
||||
</p>
|
||||
|
||||
<button type="submit">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
<?php include 'header.php'; ?>
|
||||
<style>
|
||||
:root {
|
||||
--accent: #0b74de;
|
||||
--muted: #6b7280;
|
||||
--card: #ffffff;
|
||||
--bg: #f3f4f6
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Inter, system-ui, Arial, Helvetica, sans-serif;
|
||||
background: var(--bg);
|
||||
color: #111;
|
||||
margin: 0;
|
||||
padding: 32px
|
||||
}
|
||||
|
||||
.wrap {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 20px
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 16px
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
|
||||
padding: 20px
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--accent)
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
font-size: 13px
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 8px
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #eef2f6;
|
||||
text-align: left;
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
th {
|
||||
background: transparent;
|
||||
font-weight: 600
|
||||
}
|
||||
|
||||
.feature {
|
||||
display: flex;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-block;
|
||||
background: #eef6ff;
|
||||
border-radius: 999px;
|
||||
padding: 6px 10px;
|
||||
font-size: 13px;
|
||||
margin-left: auto
|
||||
}
|
||||
|
||||
.cta {
|
||||
display: inline-block;
|
||||
padding: 10px 14px;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
.cta-primary {
|
||||
background: var(--accent);
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.cta-ghost {
|
||||
border: 1px solid #e6eefa;
|
||||
color: var(--accent)
|
||||
}
|
||||
|
||||
.benefits {
|
||||
margin-top: 18px
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 13px;
|
||||
color: #374151;
|
||||
background: #fff;
|
||||
padding: 12px;
|
||||
border-radius: 8px
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
font-size: 13px;
|
||||
color: var(--muted)
|
||||
}
|
||||
|
||||
@media (max-width:600px) {
|
||||
header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start
|
||||
}
|
||||
|
||||
header h1 {
|
||||
margin-bottom: 8px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<section class="benefits">
|
||||
<div class="card">
|
||||
<h3 style="margin-top:0">Why choose hosted streaming over just buying a static IP from your ISP ?</h3>
|
||||
<ul style="margin:8px 0 0 18px">
|
||||
<li><strong>DDoS & attack protection:</strong> Professional hosts run network-level DDoS mitigation and web application firewalls (WAF) that absorb and block large-scale attacks before they reach your origin server.</li>
|
||||
<li><strong>Scalable bandwidth & CDN:</strong> Hosting + CDN provides globally distributed edge points and the ability to scale to many thousands of viewers without saturating a single home/office link.</li>
|
||||
<li><strong>Higher availability & SLA:</strong> Providers operate redundant infrastructure and SLAs that keep streams online even when single links or hardware fail.</li>
|
||||
<li><strong>Managed SSL, domain & DNS:</strong> Automated SSL issuance/renewal (Let's Encrypt), DNS features and a dedicated domain remove operational friction compared to configuring services on a raw ISP IP.</li>
|
||||
<li><strong>Security isolation:</strong> Dedicated servers and hosting accounts isolate your traffic and services from other customers, reducing risks that come with shared consumer-grade network equipment.</li>
|
||||
<li><strong>Monitoring & support:</strong> 24/7 monitoring, alerting and expert support are part of hosting plans — ISPs rarely provide application-level stream support.</li>
|
||||
<li><strong>Optional reserved (static) IPs:</strong> If you still need a static IP for whitelisting, we can provision a reserved IP on a dedicated plan and keep it behind our mitigation/CDN layer.</li>
|
||||
</ul>
|
||||
|
||||
<div class="note" style="margin-top:12px">
|
||||
<strong>Quick notes:</strong> "Unlimited data for links" refers to stream delivery (no per-GB charge on the plan level for the specified formats). Extremely large egress (multi-TB per month) or abusive usage may require a custom enterprise agreement. CDN bandwidth, archival storage and advanced security may be subject to fair-use or tiered pricing.
|
||||
</div>
|
||||
<div class="note" style="margin-top:12px">
|
||||
<strong>Hosting :</strong> All servers are hosted with our CDN ISP partners. This project aims to transform ISPs into data-center service providers through a hybrid partnership model. All billing is handled directly by the ISP. We found this is lowest letency and stable solutions for broadcastors . Price includes GST and 2 month will be free on yearly payment .
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="cards">
|
||||
<!-- Shared Streaming -->
|
||||
<div class="card">
|
||||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">
|
||||
<div>
|
||||
<div class="muted">Shared Streaming</div>
|
||||
<div class="price">₹2,000 / month</div>
|
||||
</div>
|
||||
<div style="margin-left:auto;text-align:right">
|
||||
<div class="pill">Best for small producers</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Feature</th>
|
||||
<th>Included</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Delivery formats</td>
|
||||
<td>HLS (m3u8), RTMP, SRT, DASH — unlimited data for links</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bandwidth</td>
|
||||
<td>Shared pool — burst-capable (fair-usage policy)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Domain</td>
|
||||
<td>Subdomain (example.customer.example.com)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSL</td>
|
||||
<td>Let's Encrypt (shared certificate)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Support</td>
|
||||
<td>Email & chat (business hours)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uptime SLA</td>
|
||||
<td>99.5%</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<div style="margin-top:12px;display:flex;gap:8px">
|
||||
<a class="cta cta-primary" href="contact_us.php">Contact Us</a>
|
||||
<a class="cta cta-ghost" href="https://urmic.org/trusted-partners/">Our ISP Partners</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Dedicated Streaming -->
|
||||
<div class="card">
|
||||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">
|
||||
<div>
|
||||
<div class="muted">Dedicated Streaming</div>
|
||||
<div class="price">₹4,000 / month</div>
|
||||
</div>
|
||||
<div style="margin-left:auto;text-align:right">
|
||||
<div class="pill">Recommended for events & scale</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Feature</th>
|
||||
<th>Included</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Delivery formats</td>
|
||||
<td>HLS (m3u8), RTMP, SRT, DASH — unlimited data for links</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bandwidth</td>
|
||||
<td>Dedicated bandwidth allocation (higher sustained throughput up to 10gbe spike )</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Domain</td>
|
||||
<td>Dedicated domain included (example: yourbrand.live)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSL</td>
|
||||
<td>Free SSL certificate (Let's Encrypt) + automated renewals</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DDoS / Attack protection</td>
|
||||
<td>Network-level mitigation & WAF</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Static IP</td>
|
||||
<td>Dedicated ip ipv4 and ipv6 available (reserved IP) — useful for whitelisting</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uptime SLA</td>
|
||||
<td>99.9% with priority support</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Support</td>
|
||||
<td>24/7 priority support & onboarding</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<div style="margin-top:12px;display:flex;gap:8px">
|
||||
<a class="cta cta-primary" href="contact_us.php">Contact Us</a>
|
||||
<a class="cta cta-ghost" href="https://urmic.org/trusted-partners/">Our ISP Partners</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<footer>
|
||||
<div class="muted">Need an exportable copy of this pricing page or custom branding? Contact sales for a tailored quote and SLA.</div>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
// auth/require_login.php
|
||||
|
||||
declare(strict_types=1);
|
||||
session_start();
|
||||
|
||||
/* ---------- SECURITY HEADERS (optional but recommended) ---------- */
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('Referrer-Policy: strict-origin-when-cross-origin');
|
||||
|
||||
/* ---------- LOGIN CHECK ---------- */
|
||||
if (empty($_SESSION['user'])) {
|
||||
|
||||
// Prevent cache of protected pages
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: /login.php', true, 302);
|
||||
exit;
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
<?php
|
||||
|
||||
function generateRandomString($length = 16)
|
||||
{
|
||||
$bytes = random_bytes(ceil($length / 2));
|
||||
$randomString = bin2hex($bytes);
|
||||
return substr($randomString, 0, $length);
|
||||
}
|
||||
function setptsFromMs($ms)
|
||||
{
|
||||
// convert ms → seconds
|
||||
$sec = $ms / 1000;
|
||||
|
||||
// format with up to 3 decimals (avoid scientific notation)
|
||||
$secFormatted = number_format($sec, 3, '.', '');
|
||||
|
||||
return 'setpts=PTS+' . $secFormatted . '/TB';
|
||||
}
|
||||
|
||||
function adelayFromMs($ms, $channels = 2)
|
||||
{
|
||||
// build "ms|ms|ms..." pattern for each audio channel
|
||||
$parts = array_fill(0, $channels, (string)$ms);
|
||||
$pattern = implode('|', $parts);
|
||||
|
||||
return 'adelay=' . $pattern;
|
||||
}
|
||||
|
||||
function deleteDir(string $dir): void
|
||||
{
|
||||
if (!is_dir($dir)) return;
|
||||
|
||||
$it = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
|
||||
$files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath());
|
||||
}
|
||||
|
||||
rmdir($dir);
|
||||
}
|
||||
|
||||
function find_first_physical_ethernet(): ?string
|
||||
{
|
||||
foreach (scandir('/sys/class/net') as $iface) {
|
||||
if ($iface === '.' || $iface === '..' || $iface === 'lo') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$net = "/sys/class/net/$iface";
|
||||
|
||||
if (!is_link("$net/device")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = @trim(file_get_contents("$net/type"));
|
||||
if ($type !== '1') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir("$net/wireless")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir("$net/bridge")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$addrAssignType = @trim(file_get_contents("$net/addr_assign_type"));
|
||||
if ($addrAssignType !== '0') {
|
||||
continue;
|
||||
}
|
||||
return $iface;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function build_interface(array $cfg, string $type): array
|
||||
{
|
||||
$out = [];
|
||||
|
||||
/* ---------- IPv4 ---------- */
|
||||
if ($cfg['mode'] === 'dhcp') {
|
||||
$out['dhcp4'] = true;
|
||||
} elseif ($cfg['mode'] === 'static') {
|
||||
$out['dhcp4'] = false;
|
||||
|
||||
if ($cfg["network_{$type}_ip"] !== '') {
|
||||
$out['addresses'][] = $cfg["network_{$type}_ip"]; // already CIDR
|
||||
}
|
||||
|
||||
if ($cfg["network_{$type}_gateway"] !== '') {
|
||||
$out['gateway4'] = $cfg["network_{$type}_gateway"];
|
||||
}
|
||||
|
||||
$dns = array_filter([
|
||||
$cfg["network_{$type}_dns1"],
|
||||
$cfg["network_{$type}_dns2"]
|
||||
]);
|
||||
|
||||
if ($dns) {
|
||||
$out['nameservers']['addresses'] = array_values($dns);
|
||||
}
|
||||
} else {
|
||||
$out['dhcp4'] = false;
|
||||
}
|
||||
|
||||
/* ---------- IPv6 ---------- */
|
||||
if ($cfg['modev6'] === 'auto') {
|
||||
$out['dhcp6'] = true;
|
||||
$out['accept-ra'] = true;
|
||||
} elseif ($cfg['modev6'] === 'dhcpv6') {
|
||||
$out['dhcp6'] = true;
|
||||
$out['accept-ra'] = false;
|
||||
} elseif ($cfg['modev6'] === 'static') {
|
||||
$out['dhcp6'] = false;
|
||||
$out['accept-ra'] = false;
|
||||
|
||||
if (
|
||||
$cfg["network_{$type}_ipv6"] !== '' &&
|
||||
$cfg["network_{$type}_ipv6_prefix"] !== ''
|
||||
) {
|
||||
$out['addresses'][] =
|
||||
$cfg["network_{$type}_ipv6"] . '/' .
|
||||
$cfg["network_{$type}_ipv6_prefix"];
|
||||
}
|
||||
|
||||
if ($cfg["network_{$type}_ipv6_gateway"] !== '') {
|
||||
$out['gateway6'] = $cfg["network_{$type}_ipv6_gateway"];
|
||||
}
|
||||
|
||||
$dns6 = array_filter([
|
||||
$cfg["network_{$type}_ipv6_dns1"],
|
||||
$cfg["network_{$type}_ipv6_dns2"]
|
||||
]);
|
||||
|
||||
if ($dns6) {
|
||||
$out['nameservers']['addresses'] =
|
||||
array_merge($out['nameservers']['addresses'] ?? [], $dns6);
|
||||
}
|
||||
} else {
|
||||
$out['dhcp6'] = false;
|
||||
$out['accept-ra'] = false;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
function generate_netplan(array $data, string $iface): array
|
||||
{
|
||||
$netplan = [
|
||||
'network' => [
|
||||
'version' => 2,
|
||||
'renderer' => 'networkd',
|
||||
'ethernets' => [],
|
||||
'vlans' => []
|
||||
]
|
||||
];
|
||||
|
||||
/* ---------- BASE INTERFACE (PRIMARY FIRST) ---------- */
|
||||
if (
|
||||
$data['primary']['mode'] !== 'disabled' ||
|
||||
$data['primary']['modev6'] !== 'disabled'
|
||||
) {
|
||||
$base_vlan = trim($data['primary']['network_primary_vlan'] ?? '');
|
||||
|
||||
if ($base_vlan === '') {
|
||||
// Configure base NIC
|
||||
$netplan['network']['ethernets'][$iface] =
|
||||
build_interface($data['primary'], 'primary');
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- BASE INTERFACE (SECONDARY ONLY IF NOT SET) ---------- */
|
||||
if (
|
||||
!isset($netplan['network']['ethernets'][$iface]) &&
|
||||
(
|
||||
$data['secondary']['mode'] !== 'disabled' ||
|
||||
$data['secondary']['modev6'] !== 'disabled'
|
||||
)
|
||||
) {
|
||||
$base_vlan = trim($data['secondary']['network_secondary_vlan'] ?? '');
|
||||
|
||||
if ($base_vlan === '') {
|
||||
$netplan['network']['ethernets'][$iface] =
|
||||
build_interface($data['secondary'], 'secondary');
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- VLANs (PRIMARY) ---------- */
|
||||
$p_vlan = trim($data['primary']['network_primary_vlan'] ?? '');
|
||||
if ($p_vlan !== '') {
|
||||
// Ensure base interface exists
|
||||
$netplan['network']['ethernets'][$iface] ??= new stdClass();
|
||||
|
||||
$netplan['network']['vlans']["{$iface}.{$p_vlan}"] =
|
||||
array_merge(
|
||||
['id' => (int)$p_vlan, 'link' => $iface],
|
||||
build_interface($data['primary'], 'primary')
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- VLANs (SECONDARY) ---------- */
|
||||
$s_vlan = trim($data['secondary']['network_secondary_vlan'] ?? '');
|
||||
if ($s_vlan !== '') {
|
||||
$netplan['network']['ethernets'][$iface] ??= new stdClass();
|
||||
|
||||
$netplan['network']['vlans']["{$iface}.{$s_vlan}"] =
|
||||
array_merge(
|
||||
['id' => (int)$s_vlan, 'link' => $iface],
|
||||
build_interface($data['secondary'], 'secondary')
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- Normalize vlans ---------- */
|
||||
if (empty($netplan['network']['vlans'])) {
|
||||
$netplan['network']['vlans'] = new stdClass();
|
||||
}
|
||||
|
||||
return $netplan;
|
||||
}
|
||||
|
||||
function validate_config(array $data): bool
|
||||
{
|
||||
$p_enabled = (
|
||||
$data['primary']['mode'] !== 'disabled' ||
|
||||
$data['primary']['modev6'] !== 'disabled'
|
||||
);
|
||||
|
||||
$s_enabled = (
|
||||
$data['secondary']['mode'] !== 'disabled' ||
|
||||
$data['secondary']['modev6'] !== 'disabled'
|
||||
);
|
||||
|
||||
$p_vlan = trim($data['primary']['network_primary_vlan'] ?? '');
|
||||
$s_vlan = trim($data['secondary']['network_secondary_vlan'] ?? '');
|
||||
|
||||
/* If both enabled → at least one VLAN required */
|
||||
if ($p_enabled && $s_enabled && $p_vlan === '' && $s_vlan === '') {
|
||||
echo "<script>alert('Primary and Secondary are enabled, but no VLAN is defined.');</script>";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Block duplicate VLAN IDs */
|
||||
if ($p_vlan !== '' && $s_vlan !== '' && $p_vlan === $s_vlan) {
|
||||
echo "<script>alert('Primary and Secondary cannot use the same VLAN ID.');</script>";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function netplan_yaml(array $data, int $indent = 0): string
|
||||
{
|
||||
$out = '';
|
||||
$pad = str_repeat(' ', $indent);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
|
||||
if ($value instanceof stdClass) {
|
||||
$out .= "{$pad}{$key}: {}\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_bool($value)) {
|
||||
$out .= "{$pad}{$key}: " . ($value ? 'true' : 'false') . "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($value)) {
|
||||
$out .= "{$pad}{$key}: {$value}\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_keys($value) === range(0, count($value) - 1)) {
|
||||
$out .= "{$pad}{$key}:\n";
|
||||
foreach ($value as $item) {
|
||||
$out .= "{$pad} - {$item}\n";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$out .= "{$pad}{$key}:\n";
|
||||
$out .= netplan_yaml($value, $indent + 1);
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,684 @@
|
|||
<?php include 'header.php'; ?>
|
||||
<?php
|
||||
|
||||
$domain = "";
|
||||
$https = false;
|
||||
|
||||
$jsonFile = __DIR__ . '/domain.json';
|
||||
|
||||
if (file_exists($jsonFile)) {
|
||||
$raw = file_get_contents($jsonFile);
|
||||
$data = json_decode($raw, true);
|
||||
$domain = $data['domain'];
|
||||
$https = $data['https'];
|
||||
} else {
|
||||
$domain = $_SERVER['SERVER_NAME'];
|
||||
}
|
||||
|
||||
$jsonFile = __DIR__ . '/output.json';
|
||||
if (file_exists($jsonFile)) {
|
||||
$raw = file_get_contents($jsonFile);
|
||||
$data = json_decode($raw, true);
|
||||
}
|
||||
|
||||
$service_rtmp0_multiple = $data['service_rtmp0_multiple'];
|
||||
$service_rtmp0_hls = $data['service_rtmp0_hls'];
|
||||
$service_rtmp0_dash = $data['service_rtmp0_dash'];
|
||||
$service_rtmp1_multiple = $data['service_rtmp1_multiple'];
|
||||
$service_rtmp1_hls = $data['service_rtmp1_hls'];
|
||||
$service_rtmp1_dash = $data['service_rtmp1_dash'];
|
||||
$service_srt_multiple = $data['service_srt_multiple'];
|
||||
|
||||
$text = "<h3>Encoder</h3>";
|
||||
$text .= "<h5>http://" . $domain;
|
||||
if ($https) $text .= "<br>https://" . $domain;
|
||||
$text .= "</h5>";
|
||||
|
||||
if ($service_rtmp0_multiple == 'enable') {
|
||||
$text .= "<h5>rtmp://" . $domain . "/shree/bhattji<br>";
|
||||
if ($service_rtmp0_dash == 'enable') {
|
||||
$text .= "http://" . $domain . "/hls/shree/bhattji.m3u8<br>";
|
||||
if ($https) {
|
||||
$text .= "https://" . $domain . "/hls/shree/bhattji.m3u8<br><br>";
|
||||
}
|
||||
}
|
||||
if ($service_rtmp0_dash == 'enable') {
|
||||
$text .= "http://" . $domain . "/dash/shree/bhattji.mpd<br>";
|
||||
if ($https) {
|
||||
$text .= "https://" . $domain . "/dash/shree/bhattji.mpd<br>";
|
||||
}
|
||||
}
|
||||
$text .= "</h5>";
|
||||
}
|
||||
if ($service_rtmp1_multiple == 'enable') {
|
||||
$text .= "<h5>rtmp://" . $domain . "/shreeshree/bhattji<br>";
|
||||
if ($service_rtmp1_dash == 'enable') {
|
||||
$text .= "http://" . $domain . "/hls/shreeshree/bhattji.m3u8<br>";
|
||||
if ($https) {
|
||||
$text .= "https://" . $domain . "/hls/shreeshree/bhattji.m3u8<br><br>";
|
||||
}
|
||||
}
|
||||
if ($service_rtmp1_dash == 'enable') {
|
||||
$text .= "http://" . $domain . "/dash/shreeshree/bhattji.mpd<br>";
|
||||
if ($https) {
|
||||
$text .= "https://" . $domain . "/dash/shreeshree/bhattji.mpd<br>";
|
||||
}
|
||||
}
|
||||
$text .= "</h5>";
|
||||
}
|
||||
|
||||
if($service_srt_multiple){
|
||||
$text .= "<h5>srt://" . $domain . ":1937?streamid=shree/bhatt/ji</h5><br><br>";
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if (isset($_POST['action'])) {
|
||||
$data = explode("_", $_POST['action']);
|
||||
|
||||
switch ($data[0]) {
|
||||
case 'main':
|
||||
switch ($data[1]) {
|
||||
case 'restart':
|
||||
exec('sudo systemctl enable encoder-main');
|
||||
exec('sudo systemctl restart encoder-main');
|
||||
break;
|
||||
case 'enable':
|
||||
exec('sudo systemctl enable encoder-main');
|
||||
exec('sudo systemctl restart encoder-main');
|
||||
break;
|
||||
case 'disable':
|
||||
exec('sudo systemctl stop encoder-main');
|
||||
exec('sudo systemctl disable encoder-main');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'rtmp0':
|
||||
switch ($data[1]) {
|
||||
case 'restart':
|
||||
exec('sudo systemctl enable encoder-rtmp0');
|
||||
exec('sudo systemctl restart encoder-rtmp0');
|
||||
break;
|
||||
case 'enable':
|
||||
exec('sudo systemctl enable encoder-rtmp0');
|
||||
exec('sudo systemctl restart encoder-rtmp0');
|
||||
break;
|
||||
case 'disable':
|
||||
exec('sudo systemctl stop encoder-rtmp0');
|
||||
exec('sudo systemctl disable encoder-rtmp0');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'rtmp1':
|
||||
switch ($data[1]) {
|
||||
case 'restart':
|
||||
exec('sudo systemctl enable encoder-rtmp1');
|
||||
exec('sudo systemctl restart encoder-rtmp1');
|
||||
break;
|
||||
case 'enable':
|
||||
exec('sudo systemctl enable encoder-rtmp1');
|
||||
exec('sudo systemctl restart encoder-rtmp1');
|
||||
break;
|
||||
case 'disable':
|
||||
exec('sudo systemctl stop encoder-rtmp1');
|
||||
exec('sudo systemctl disable encoder-rtmp1');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'srt':
|
||||
switch ($data[1]) {
|
||||
case 'restart':
|
||||
exec('sudo systemctl enable srt');
|
||||
exec('sudo systemctl restart srt');
|
||||
exec('sudo systemctl enable encoder-srt');
|
||||
exec('sudo systemctl restart encoder-srt');
|
||||
break;
|
||||
case 'enable':
|
||||
exec('sudo systemctl enable srt');
|
||||
exec('sudo systemctl restart srt');
|
||||
exec('sudo systemctl enable encoder-srt');
|
||||
exec('sudo systemctl restart encoder-srt');
|
||||
break;
|
||||
case 'disable':
|
||||
exec('sudo systemctl stop encoder-srt');
|
||||
exec('sudo systemctl disable encoder-srt');
|
||||
exec('sudo systemctl stop srt');
|
||||
exec('sudo systemctl disable srt');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'udp0':
|
||||
switch ($data[1]) {
|
||||
case 'restart':
|
||||
exec('sudo systemctl restart encoder-udp0');
|
||||
break;
|
||||
case 'enable':
|
||||
exec('sudo systemctl enable encoder-udp0');
|
||||
exec('sudo systemctl restart encoder-udp0');
|
||||
break;
|
||||
case 'disable':
|
||||
exec('sudo systemctl stop encoder-udp0');
|
||||
exec('sudo systemctl disable encoder-udp0');
|
||||
break;
|
||||
}
|
||||
case 'udp1':
|
||||
switch ($data[1]) {
|
||||
case 'restart':
|
||||
exec('sudo systemctl restart encoder-udp1');
|
||||
break;
|
||||
case 'enable':
|
||||
exec('sudo systemctl enable encoder-udp1');
|
||||
exec('sudo systemctl restart encoder-udp1');
|
||||
break;
|
||||
case 'disable':
|
||||
exec('sudo systemctl stop encoder-udp1');
|
||||
exec('sudo systemctl disable encoder-udp1');
|
||||
break;
|
||||
}
|
||||
case 'udp2':
|
||||
switch ($data[1]) {
|
||||
case 'restart':
|
||||
exec('sudo systemctl restart encoder-udp2');
|
||||
break;
|
||||
case 'enable':
|
||||
exec('sudo systemctl enable encoder-udp2');
|
||||
exec('sudo systemctl restart encoder-udp2');
|
||||
break;
|
||||
case 'disable':
|
||||
exec('sudo systemctl stop encoder-udp2');
|
||||
exec('sudo systemctl disable encoder-udp2');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'custom':
|
||||
switch ($data[1]) {
|
||||
case 'restart':
|
||||
exec('sudo systemctl restart encoder-custom');
|
||||
break;
|
||||
case 'enable':
|
||||
exec('sudo systemctl enable encoder-custom');
|
||||
exec('sudo systemctl restart encoder-custom');
|
||||
break;
|
||||
case 'disable':
|
||||
exec('sudo systemctl stop encoder-custom');
|
||||
exec('sudo systemctl disable encoder-custom');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<style>
|
||||
.card-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.card-left,
|
||||
.card-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.card-left {
|
||||
flex: 1 1 55%;
|
||||
}
|
||||
|
||||
.card-right {
|
||||
flex: 1 1 40%;
|
||||
align-items: flex-end;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input-wrapper input {
|
||||
width: 100%;
|
||||
padding: 10px 40px 10px 12px;
|
||||
border-radius: 25px;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 0.95rem;
|
||||
outline: none;
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
.copy-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 1.1rem;
|
||||
color: #444;
|
||||
pointer-events: none;
|
||||
/* visual only */
|
||||
}
|
||||
|
||||
.service-label {
|
||||
font-size: 0.9rem;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.badge-enabled {
|
||||
background: #16a34a22;
|
||||
color: #15803d;
|
||||
border: 1px solid #16a34a;
|
||||
}
|
||||
|
||||
.badge-disabled {
|
||||
background: #b91c1c22;
|
||||
color: #b91c1c;
|
||||
border: 1px solid #b91c1c;
|
||||
}
|
||||
|
||||
.service-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.service-buttons button {
|
||||
padding: 6px 14px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid transparent;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-restart {
|
||||
border-color: #0f172a;
|
||||
background: #0f172a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-enable {
|
||||
border-color: #15803d;
|
||||
background: #15803d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-disable {
|
||||
border-color: #b91c1c;
|
||||
background: #b91c1c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.hls-player-wrapper {
|
||||
max-width: 900px;
|
||||
margin: 20px auto;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
background: #121212;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
.hls-video {
|
||||
width: 100%;
|
||||
max-height: 70vh;
|
||||
border-radius: 10px;
|
||||
background: #000;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.hls-video:focus-visible {
|
||||
outline: 2px solid #1e88e5;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.card-right {
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card wide">
|
||||
<h3>Input Service</h3>
|
||||
<?php
|
||||
$status = shell_exec("sudo systemctl is-active encoder-main 2>&1");
|
||||
$status = trim($status);
|
||||
|
||||
if ($status === "active")
|
||||
$serviceEnabled = true;
|
||||
else
|
||||
$serviceEnabled = false;
|
||||
?>
|
||||
|
||||
<div class="card-row">
|
||||
<div class="service-label">
|
||||
<strong>Service</strong>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<span class="badge badge-enabled">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-disabled">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" class="service-buttons">
|
||||
<button type="submit" name="action" value="main_restart" class="btn-restart">
|
||||
Restart
|
||||
</button>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<button type="submit" name="action" value="main_disable" class="btn-disable">
|
||||
Disable
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="action" value="main_enable" class="btn-enable">
|
||||
Enable
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card wide">
|
||||
<h3>RTMP0 Server</h3>
|
||||
<?php
|
||||
$status = shell_exec("sudo systemctl is-active encoder-rtmp0 2>&1");
|
||||
$status = trim($status);
|
||||
|
||||
if ($status === "active")
|
||||
$serviceEnabled = true;
|
||||
else
|
||||
$serviceEnabled = false;
|
||||
|
||||
?>
|
||||
|
||||
<div class="card-row">
|
||||
<div class="card-right">
|
||||
<div class="service-label">
|
||||
<strong>Service</strong>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<span class="badge badge-enabled">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-disabled">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" class="service-buttons">
|
||||
<button type="submit" name="action" value="rtmp0_restart" class="btn-restart">Restart</button>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<button type="submit" name="action" value="rtmp0_disable" class="btn-disable">Disable</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="action" value="rtmp0_enable" class="btn-enable">Enable</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card wide">
|
||||
<h3>RTMP1 Server</h3>
|
||||
<?php
|
||||
$status = shell_exec("sudo systemctl is-active encoder-rtmp1 2>&1");
|
||||
$status = trim($status);
|
||||
|
||||
if ($status === "active")
|
||||
$serviceEnabled = true;
|
||||
else
|
||||
$serviceEnabled = false;
|
||||
|
||||
?>
|
||||
|
||||
<div class="card-row">
|
||||
<div class="card-right">
|
||||
<div class="service-label">
|
||||
<strong>Service</strong>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<span class="badge badge-enabled">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-disabled">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" class="service-buttons">
|
||||
<button type="submit" name="action" value="rtmp1_restart" class="btn-restart">Restart</button>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<button type="submit" name="action" value="rtmp1_disable" class="btn-disable">Disable</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="action" value="rtmp1_enable" class="btn-enable">Enable</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<h3>SRT Server</h3>
|
||||
<?php
|
||||
$status = shell_exec("sudo systemctl is-active encoder-srt 2>&1");
|
||||
$status = trim($status);
|
||||
|
||||
if ($status === "active")
|
||||
$serviceEnabled = true;
|
||||
else
|
||||
$serviceEnabled = false;
|
||||
?>
|
||||
|
||||
<div class="card-row">
|
||||
<div class="card-right">
|
||||
<div class="service-label">
|
||||
<strong>Service</strong>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<span class="badge badge-enabled">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-disabled">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" class="service-buttons">
|
||||
<button type="submit" name="action" value="srt_restart" class="btn-restart">Restart</button>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<button type="submit" name="action" value="srt_disable" class="btn-disable">Disable</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="action" value="srt_enable" class="btn-enable">Enable</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Udp0 Service</h3>
|
||||
<?php
|
||||
$status = shell_exec("sudo systemctl is-active encoder-udp0 2>&1");
|
||||
$status = trim($status);
|
||||
|
||||
if ($status === "active")
|
||||
$serviceEnabled = true;
|
||||
else
|
||||
$serviceEnabled = false;
|
||||
?>
|
||||
|
||||
<div class="card-row">
|
||||
<div class="service-label">
|
||||
<strong>Service</strong>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<span class="badge badge-enabled">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-disabled">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" class="service-buttons">
|
||||
<button type="submit" name="action" value="udp0_restart" class="btn-restart">
|
||||
Restart
|
||||
</button>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<button type="submit" name="action" value="udp0_disable" class="btn-disable">
|
||||
Disable
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="action" value="udp0_enable" class="btn-enable">
|
||||
Enable
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Udp1 Service</h3>
|
||||
<?php
|
||||
$status = shell_exec("sudo systemctl is-active encoder-udp1 2>&1");
|
||||
$status = trim($status);
|
||||
|
||||
if ($status === "active")
|
||||
$serviceEnabled = true;
|
||||
else
|
||||
$serviceEnabled = false;
|
||||
?>
|
||||
|
||||
<div class="card-row">
|
||||
<div class="service-label">
|
||||
<strong>Service</strong>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<span class="badge badge-enabled">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-disabled">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" class="service-buttons">
|
||||
<button type="submit" name="action" value="udp1_restart" class="btn-restart">
|
||||
Restart
|
||||
</button>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<button type="submit" name="action" value="udp1_disable" class="btn-disable">
|
||||
Disable
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="action" value="udp1_enable" class="btn-enable">
|
||||
Enable
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Udp2 Service</h3>
|
||||
<?php
|
||||
$status = shell_exec("sudo systemctl is-active encoder-udp2 2>&1");
|
||||
$status = trim($status);
|
||||
|
||||
if ($status === "active")
|
||||
$serviceEnabled = true;
|
||||
else
|
||||
$serviceEnabled = false;
|
||||
?>
|
||||
|
||||
<div class="card-row">
|
||||
<div class="service-label">
|
||||
<strong>Service</strong>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<span class="badge badge-enabled">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-disabled">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" class="service-buttons">
|
||||
<button type="submit" name="action" value="udp_restart" class="btn-restart">
|
||||
Restart
|
||||
</button>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<button type="submit" name="action" value="udp_disable" class="btn-disable">
|
||||
Disable
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="action" value="udp_enable" class="btn-enable">
|
||||
Enable
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Custom Output Service</h3>
|
||||
<?php
|
||||
$status = shell_exec("sudo systemctl is-active encoder-custom 2>&1");
|
||||
$status = trim($status);
|
||||
|
||||
if ($status === "active")
|
||||
$serviceEnabled = true;
|
||||
else
|
||||
$serviceEnabled = false;
|
||||
?>
|
||||
|
||||
<div class="card-row">
|
||||
<div class="service-label">
|
||||
<strong>Service</strong>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<span class="badge badge-enabled">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-disabled">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<form method="post" class="service-buttons">
|
||||
<button type="submit" name="action" value="custom_restart" class="btn-restart">
|
||||
Restart
|
||||
</button>
|
||||
|
||||
<?php if ($serviceEnabled): ?>
|
||||
<button type="submit" name="action" value="custom_disable" class="btn-disable">
|
||||
Disable
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button type="submit" name="action" value="custom_enable" class="btn-enable">
|
||||
Enable
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<h3>Output Links</h3>
|
||||
<?php echo $text; ?>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "Stopping and disabling all encoder@ services..."
|
||||
|
||||
for svc in $(systemctl list-units --all --no-legend "encoder@*" | awk '{print $1}'); do
|
||||
echo "Stopping $svc"
|
||||
systemctl stop "$svc"
|
||||
|
||||
echo "Disabling $svc"
|
||||
systemctl disable "$svc"
|
||||
done
|
||||
|
||||
echo "All encoder@ services stopped and disabled."
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
sudo apt update
|
||||
sudo apt install -y apache2 php libapache2-mod-php vainfo ufw intel-media-va-driver-non-free libavcodec-extra mesa-utils i965-va-driver libmfx1 intel-gpu-tools ffmpeg v4l-utils python3-pip mpv alsa-utils vlan git zlib1g-dev php-zip php-curl
|
||||
sudo pip3 install psutil --break-system-packages
|
||||
|
||||
cat > /etc/sudoers.d/www-data << 'EOL'
|
||||
www-data ALL=(ALL) NOPASSWD: ALL
|
||||
EOL
|
||||
|
||||
# graph monitor setup
|
||||
cat > /etc/systemd/system/system-monitor.service<< 'EOL'
|
||||
[Unit]
|
||||
Description=Lightweight System Monitor Sampler by ShreeBhattJi
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /usr/local/bin/nginx_system_monitor_sampler.py
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
User=root
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOL
|
||||
|
||||
|
||||
cat > /usr/local/bin/nginx_system_monitor_sampler.py<< 'EOL'
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Lightweight sampler for nginx static frontend.
|
||||
"""
|
||||
|
||||
import time, json, os
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
import psutil
|
||||
|
||||
OUT_FILE = "/var/www/html/metrics.json"
|
||||
TMP_FILE = OUT_FILE + ".tmp"
|
||||
SAMPLE_INTERVAL = 10.0 # seconds between samples
|
||||
HISTORY_SECONDS = 15 * 60 # 15 minutes
|
||||
MAX_SAMPLES = int(HISTORY_SECONDS / SAMPLE_INTERVAL)
|
||||
|
||||
# circular buffers
|
||||
timestamps = deque(maxlen=MAX_SAMPLES)
|
||||
cpu_hist = deque(maxlen=MAX_SAMPLES)
|
||||
ram_hist = deque(maxlen=MAX_SAMPLES)
|
||||
net_in_hist = deque(maxlen=MAX_SAMPLES)
|
||||
net_out_hist = deque(maxlen=MAX_SAMPLES)
|
||||
disk_read_hist = deque(maxlen=MAX_SAMPLES)
|
||||
disk_write_hist = deque(maxlen=MAX_SAMPLES)
|
||||
disk_percent_hist = deque(maxlen=MAX_SAMPLES)
|
||||
|
||||
_prev_net = psutil.net_io_counters()
|
||||
_prev_disk = psutil.disk_io_counters()
|
||||
_prev_time = time.time()
|
||||
|
||||
def sample_once():
|
||||
global _prev_net, _prev_disk, _prev_time
|
||||
now = time.time()
|
||||
iso = datetime.fromtimestamp(now).isoformat(timespec='seconds')
|
||||
cpu = psutil.cpu_percent(interval=None)
|
||||
ram = psutil.virtual_memory().percent
|
||||
|
||||
net = psutil.net_io_counters()
|
||||
disk = psutil.disk_io_counters()
|
||||
try:
|
||||
disk_percent = psutil.disk_usage("/").percent
|
||||
except Exception:
|
||||
disk_percent = 0.0
|
||||
|
||||
elapsed = now - _prev_time if _prev_time else SAMPLE_INTERVAL
|
||||
if elapsed <= 0:
|
||||
elapsed = SAMPLE_INTERVAL
|
||||
|
||||
in_rate = int(((net.bytes_recv - _prev_net.bytes_recv) / elapsed) * 8)
|
||||
out_rate = int(((net.bytes_sent - _prev_net.bytes_sent) / elapsed) * 8)
|
||||
|
||||
read_rate = (disk.read_bytes - _prev_disk.read_bytes) / elapsed
|
||||
write_rate = (disk.write_bytes - _prev_disk.write_bytes) / elapsed
|
||||
|
||||
timestamps.append(iso)
|
||||
cpu_hist.append(round(cpu, 2))
|
||||
ram_hist.append(round(ram, 2))
|
||||
net_in_hist.append(int(in_rate))
|
||||
net_out_hist.append(int(out_rate))
|
||||
disk_read_hist.append(int(read_rate))
|
||||
disk_write_hist.append(int(write_rate))
|
||||
disk_percent_hist.append(round(disk_percent, 2))
|
||||
|
||||
_prev_net = net
|
||||
_prev_disk = disk
|
||||
_prev_time = now
|
||||
|
||||
def write_json_atomic():
|
||||
payload = {
|
||||
"timestamps": list(timestamps),
|
||||
"cpu_percent": list(cpu_hist),
|
||||
"ram_percent": list(ram_hist),
|
||||
"net_in_Bps": list(net_in_hist),
|
||||
"net_out_Bps": list(net_out_hist),
|
||||
"disk_read_Bps": list(disk_read_hist),
|
||||
"disk_write_Bps": list(disk_write_hist),
|
||||
"disk_percent": list(disk_percent_hist),
|
||||
"sample_interval": SAMPLE_INTERVAL,
|
||||
"generated_at": datetime.utcnow().isoformat(timespec='seconds') + "Z"
|
||||
}
|
||||
with open(TMP_FILE, "w") as f:
|
||||
json.dump(payload, f)
|
||||
os.replace(TMP_FILE, OUT_FILE)
|
||||
|
||||
def main():
|
||||
global _prev_net, _prev_disk, _prev_time
|
||||
_prev_net = psutil.net_io_counters()
|
||||
_prev_disk = psutil.disk_io_counters()
|
||||
_prev_time = time.time()
|
||||
time.sleep(0.2) # warm-up
|
||||
|
||||
while True:
|
||||
try:
|
||||
sample_once()
|
||||
write_json_atomic()
|
||||
except Exception as e:
|
||||
# systemd journal will capture prints
|
||||
print("Sampler error:", e)
|
||||
time.sleep(SAMPLE_INTERVAL)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
EOL
|
||||
|
||||
|
||||
cat >/etc/netplan/00-stream.yaml<< 'EOL'
|
||||
network:
|
||||
version: 2
|
||||
renderer: networkd
|
||||
ethernets:
|
||||
eth:
|
||||
match:
|
||||
name: enx*
|
||||
addresses:
|
||||
- 172.16.111.111/24
|
||||
EOL
|
||||
|
||||
cat >/etc/systemd/system/encoder@.service<< 'EOL'
|
||||
[Unit]
|
||||
Description=Encoder Instance %i
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/bin/bash /var/www/encoder/%i.sh
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOL
|
||||
|
||||
sudo mkdir /var/www/encoder
|
||||
sudo cp -r html/* /var/www/html/
|
||||
sudo cp backup_private.pem /var/www/
|
||||
sudo cp backup_public.pem /var/www/
|
||||
sudo cp 00-stream.yaml /var/www/
|
||||
sudo cp attempts.json /var/www/
|
||||
sudo cp users.json /var/www/
|
||||
|
||||
sudo a2enmod ssl
|
||||
sudo systemctl enable apache2
|
||||
sudo systemctl restart apache2
|
||||
sudo a2ensite default-ssl
|
||||
sudo chmod +x /usr/local/bin/nginx_system_monitor_sampler.py
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
sudo chmod 777 -R /var/www
|
||||
sudo chown -R www-data:www-data /var/www
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
sudo chmod 444 /sys/class/dmi/id/product_uuid
|
||||
sudo systemctl disable systemd-networkd-wait-online.service
|
||||
sudo systemctl mask systemd-networkd-wait-online.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now system-monitor.service
|
||||
sudo systemctl restart --now system-monitor.service
|
||||
|
||||
|
||||
sudo ufw default allow outgoing
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw allow 80
|
||||
sudo ufw allow 443
|
||||
sudo ufw allow proto udp to 224.0.0.0/4
|
||||
sudo ufw route allow proto udp to 224.0.0.0/4
|
||||
sudo ufw deny out to 239.255.254.254 port 39000 proto udp
|
||||
sudo ufw allow from 172.16.111.112 to 172.16.111.111 port 80
|
||||
sudo ufw allow from 172.16.111.112 to 172.16.111.111 port 443
|
||||
sudo ufw --force enable
|
||||
DEVICE_ID="$(sudo cat /sys/class/dmi/id/product_uuid | tr -d '\n')"
|
||||
sudo sed -i 's/certificatecertificatecertificatecertificate/'$DEVICE_ID'/g' /var/www/html/certification.html
|
||||
|
||||
sudo chmod 777 -R /var/www
|
||||
sudo chown -R www-data:www-data /var/www
|
||||
sudo systemctl daemon-reload
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"shreebhattji": {
|
||||
"password": "$2y$10$BInKRv9mhK69VfYKIi4WVegAs9VWtLhfdZH4YoDk5aE2U61cmyT2a"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue