Upload 3 files
Browse files- static/app.js +24 -0
- static/index.html +2 -2
static/app.js
CHANGED
|
@@ -99,6 +99,8 @@ function hugpanel() {
|
|
| 99 |
await this._loadZoneLimit();
|
| 100 |
|
| 101 |
// Try to restore session from stored token
|
|
|
|
|
|
|
| 102 |
if (this.token && this.adminApiUrl) {
|
| 103 |
try {
|
| 104 |
const resp = await fetch(`${this.adminApiUrl}/auth/me`, {
|
|
@@ -189,6 +191,20 @@ function hugpanel() {
|
|
| 189 |
setTimeout(() => { this.toast.show = false; }, 3000);
|
| 190 |
},
|
| 191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
// ── API Helper ──
|
| 193 |
async api(url, options = {}) {
|
| 194 |
try {
|
|
@@ -258,6 +274,7 @@ function hugpanel() {
|
|
| 258 |
this.token = data.token;
|
| 259 |
this.user = data.user;
|
| 260 |
localStorage.setItem('hugpanel_token', data.token);
|
|
|
|
| 261 |
await this._loadPanel();
|
| 262 |
this.$nextTick(() => lucide.createIcons());
|
| 263 |
} catch (err) {
|
|
@@ -288,6 +305,7 @@ function hugpanel() {
|
|
| 288 |
this.token = data.token;
|
| 289 |
this.user = data.user;
|
| 290 |
localStorage.setItem('hugpanel_token', data.token);
|
|
|
|
| 291 |
await this._loadPanel();
|
| 292 |
this.$nextTick(() => lucide.createIcons());
|
| 293 |
} catch (err) {
|
|
@@ -299,6 +317,7 @@ function hugpanel() {
|
|
| 299 |
logout() {
|
| 300 |
this.token = null;
|
| 301 |
this.user = null;
|
|
|
|
| 302 |
localStorage.removeItem('hugpanel_token');
|
| 303 |
localStorage.removeItem('hugpanel_admin_url');
|
| 304 |
localStorage.removeItem('hugpanel_zone');
|
|
@@ -559,6 +578,11 @@ function hugpanel() {
|
|
| 559 |
return this.backupList.filter((item) => item.zone_name === zoneName).length;
|
| 560 |
},
|
| 561 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 562 |
async copyText(value, message = 'Đã copy') {
|
| 563 |
try {
|
| 564 |
await navigator.clipboard.writeText(value);
|
|
|
|
| 99 |
await this._loadZoneLimit();
|
| 100 |
|
| 101 |
// Try to restore session from stored token
|
| 102 |
+
this.syncAuthCookie();
|
| 103 |
+
|
| 104 |
if (this.token && this.adminApiUrl) {
|
| 105 |
try {
|
| 106 |
const resp = await fetch(`${this.adminApiUrl}/auth/me`, {
|
|
|
|
| 191 |
setTimeout(() => { this.toast.show = false; }, 3000);
|
| 192 |
},
|
| 193 |
|
| 194 |
+
setAuthCookie(token) {
|
| 195 |
+
const secure = location.protocol === 'https:' ? '; Secure' : '';
|
| 196 |
+
document.cookie = `token=${encodeURIComponent(token)}; Path=/; SameSite=Lax${secure}`;
|
| 197 |
+
},
|
| 198 |
+
|
| 199 |
+
clearAuthCookie() {
|
| 200 |
+
document.cookie = 'token=; Path=/; Max-Age=0; SameSite=Lax';
|
| 201 |
+
},
|
| 202 |
+
|
| 203 |
+
syncAuthCookie() {
|
| 204 |
+
if (this.token) this.setAuthCookie(this.token);
|
| 205 |
+
else this.clearAuthCookie();
|
| 206 |
+
},
|
| 207 |
+
|
| 208 |
// ── API Helper ──
|
| 209 |
async api(url, options = {}) {
|
| 210 |
try {
|
|
|
|
| 274 |
this.token = data.token;
|
| 275 |
this.user = data.user;
|
| 276 |
localStorage.setItem('hugpanel_token', data.token);
|
| 277 |
+
this.syncAuthCookie();
|
| 278 |
await this._loadPanel();
|
| 279 |
this.$nextTick(() => lucide.createIcons());
|
| 280 |
} catch (err) {
|
|
|
|
| 305 |
this.token = data.token;
|
| 306 |
this.user = data.user;
|
| 307 |
localStorage.setItem('hugpanel_token', data.token);
|
| 308 |
+
this.syncAuthCookie();
|
| 309 |
await this._loadPanel();
|
| 310 |
this.$nextTick(() => lucide.createIcons());
|
| 311 |
} catch (err) {
|
|
|
|
| 317 |
logout() {
|
| 318 |
this.token = null;
|
| 319 |
this.user = null;
|
| 320 |
+
this.clearAuthCookie();
|
| 321 |
localStorage.removeItem('hugpanel_token');
|
| 322 |
localStorage.removeItem('hugpanel_admin_url');
|
| 323 |
localStorage.removeItem('hugpanel_zone');
|
|
|
|
| 578 |
return this.backupList.filter((item) => item.zone_name === zoneName).length;
|
| 579 |
},
|
| 580 |
|
| 581 |
+
buildPortUrl(port) {
|
| 582 |
+
const base = port?.url || (`/port/${this.currentZone}/${port?.port}/`);
|
| 583 |
+
return new URL(base, location.origin).toString();
|
| 584 |
+
},
|
| 585 |
+
|
| 586 |
async copyText(value, message = 'Đã copy') {
|
| 587 |
try {
|
| 588 |
await navigator.clipboard.writeText(value);
|
static/index.html
CHANGED
|
@@ -499,11 +499,11 @@
|
|
| 499 |
Port: <span x-text="port.port" class="text-gray-400 font-mono"></span>
|
| 500 |
</div>
|
| 501 |
</div>
|
| 502 |
-
<button @click="copyText((
|
| 503 |
class="p-2 rounded-lg text-gray-400 hover:text-cyan-400 hover:bg-cyan-400/10 transition" title="Copy link">
|
| 504 |
<i data-lucide="copy" class="w-4 h-4"></i>
|
| 505 |
</button>
|
| 506 |
-
<a :href="
|
| 507 |
target="_blank"
|
| 508 |
class="p-2 rounded-lg text-gray-400 hover:text-brand-400 hover:bg-brand-400/10 transition" title="Mở">
|
| 509 |
<i data-lucide="external-link" class="w-4 h-4"></i>
|
|
|
|
| 499 |
Port: <span x-text="port.port" class="text-gray-400 font-mono"></span>
|
| 500 |
</div>
|
| 501 |
</div>
|
| 502 |
+
<button @click="copyText(buildPortUrl(port), 'Đã copy link port')"
|
| 503 |
class="p-2 rounded-lg text-gray-400 hover:text-cyan-400 hover:bg-cyan-400/10 transition" title="Copy link">
|
| 504 |
<i data-lucide="copy" class="w-4 h-4"></i>
|
| 505 |
</button>
|
| 506 |
+
<a :href="buildPortUrl(port)"
|
| 507 |
target="_blank"
|
| 508 |
class="p-2 rounded-lg text-gray-400 hover:text-brand-400 hover:bg-brand-400/10 transition" title="Mở">
|
| 509 |
<i data-lucide="external-link" class="w-4 h-4"></i>
|