mirror of
https://github.com/headporter81/specialsource-homepage-frontend.git
synced 2026-08-08 15:21:11 +09:00
This commit is contained in:
+70
-8
@@ -152,7 +152,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
import { ref, onMounted, onUnmounted, computed, watch } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import TuiProgressBar from '@/components/TuiProgressBar.vue';
|
import TuiProgressBar from '@/components/TuiProgressBar.vue';
|
||||||
|
|
||||||
@@ -165,7 +165,14 @@ const loginPw = ref('');
|
|||||||
|
|
||||||
const activeMenu = ref('main');
|
const activeMenu = ref('main');
|
||||||
const authToken = ref(localStorage.getItem('token'));
|
const authToken = ref(localStorage.getItem('token'));
|
||||||
const activeTheme = ref('green');
|
const currentUsername = ref(localStorage.getItem('username') || '');
|
||||||
|
const VALID_THEMES = ['green', 'amber', 'vga', 'ega'];
|
||||||
|
const storedTheme = localStorage.getItem('theme');
|
||||||
|
const activeTheme = ref(VALID_THEMES.includes(storedTheme) ? storedTheme : 'green');
|
||||||
|
|
||||||
|
watch(activeTheme, (theme) => {
|
||||||
|
localStorage.setItem('theme', theme);
|
||||||
|
});
|
||||||
|
|
||||||
const isProcessing = ref(false);
|
const isProcessing = ref(false);
|
||||||
const overlayProgress = ref(0);
|
const overlayProgress = ref(0);
|
||||||
@@ -187,6 +194,14 @@ const menuItems = [
|
|||||||
|
|
||||||
const isLoggedIn = computed(() => !!authToken.value);
|
const isLoggedIn = computed(() => !!authToken.value);
|
||||||
|
|
||||||
|
const terminalPrompt = () => `${isLoggedIn.value && currentUsername.value ? currentUsername.value : 'anonymous'}@specialsource:~$ `;
|
||||||
|
|
||||||
|
const syncTerminalPrompt = () => {
|
||||||
|
if (termInstance.value) {
|
||||||
|
termInstance.value.set_prompt(terminalPrompt());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const currentServerSourceDisplay = computed(() => {
|
const currentServerSourceDisplay = computed(() => {
|
||||||
if (currentServerSource.value === 'firebase_night') {
|
if (currentServerSource.value === 'firebase_night') {
|
||||||
return 'SYS: CLOUD_REDUNDANCY (NIGHT)';
|
return 'SYS: CLOUD_REDUNDANCY (NIGHT)';
|
||||||
@@ -316,11 +331,14 @@ const handleRegister = () => alert("회원가입은 현재 관리자 승인제
|
|||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
|
localStorage.removeItem('username');
|
||||||
authToken.value = null;
|
authToken.value = null;
|
||||||
|
currentUsername.value = '';
|
||||||
activeMenu.value = 'main';
|
activeMenu.value = 'main';
|
||||||
if (termInstance.value) {
|
if (termInstance.value) {
|
||||||
termInstance.value.echo(`\n[[b;red;][ALERT]] SESSION TERMINATED. USER LOGGED OUT.`);
|
termInstance.value.echo(`\n[[b;red;][ALERT]] SESSION TERMINATED. USER LOGGED OUT.`);
|
||||||
}
|
}
|
||||||
|
syncTerminalPrompt();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoginSubmit = async () => {
|
const handleLoginSubmit = async () => {
|
||||||
@@ -356,7 +374,10 @@ const handleLoginSubmit = async () => {
|
|||||||
overlayStatus.value = 'Access Granted. Generating JWT Session token...';
|
overlayStatus.value = 'Access Granted. Generating JWT Session token...';
|
||||||
|
|
||||||
localStorage.setItem('token', response.data.token);
|
localStorage.setItem('token', response.data.token);
|
||||||
|
localStorage.setItem('username', loginId.value);
|
||||||
authToken.value = response.data.token;
|
authToken.value = response.data.token;
|
||||||
|
currentUsername.value = loginId.value;
|
||||||
|
syncTerminalPrompt();
|
||||||
|
|
||||||
if (termInstance.value) {
|
if (termInstance.value) {
|
||||||
termInstance.value.echo(`[[b;var(--tui-color);][SUCCESS]] ACCESS GRANTED. CREDENTIALS VERIFIED.`);
|
termInstance.value.echo(`[[b;var(--tui-color);][SUCCESS]] ACCESS GRANTED. CREDENTIALS VERIFIED.`);
|
||||||
@@ -429,12 +450,53 @@ onMounted(() => {
|
|||||||
}, 110);
|
}, 110);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'login': function () {
|
||||||
|
const term = this;
|
||||||
|
|
||||||
|
if (isLoggedIn.value) {
|
||||||
|
term.echo(`\n[[b;red;][ERROR]] ALREADY AUTHENTICATED AS ${currentUsername.value || 'ADMIN'}. TYPE 'logout' FIRST TO SWITCH USER.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
term.echo(`\n[SYS] LOGIN: ENTER CREDENTIALS TO AUTHENTICATE.`);
|
||||||
|
|
||||||
|
term.read('ACCESS ID: ').then((id) => {
|
||||||
|
term.set_mask(true);
|
||||||
|
return term.read('ACCESS KEY: ').then((pw) => {
|
||||||
|
term.set_mask(false);
|
||||||
|
return { id, pw };
|
||||||
|
});
|
||||||
|
}).then(({ id, pw }) => {
|
||||||
|
if (!id || !pw) {
|
||||||
|
term.echo(`[[b;red;][ERROR]] ACCESS ID/KEY CANNOT BE EMPTY.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loginId.value = id;
|
||||||
|
loginPw.value = pw;
|
||||||
|
handleLoginSubmit();
|
||||||
|
}).catch(() => {
|
||||||
|
term.set_mask(false);
|
||||||
|
term.echo(`\n[[b;red;][LOGIN]] AUTHENTICATION CANCELLED.`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'logout': function () {
|
||||||
|
const term = this;
|
||||||
|
|
||||||
|
if (!isLoggedIn.value) {
|
||||||
|
term.echo(`\n[[b;red;][ERROR]] NOT AUTHENTICATED. NOTHING TO LOG OUT OF.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLogout();
|
||||||
|
},
|
||||||
|
|
||||||
'help': function () {
|
'help': function () {
|
||||||
this.echo(`- hello : Say hello\n- specialsource : Core info\n- email : Contact\n- status : Check Live Connected Users\n- clear : Clear console`);
|
this.echo(`- hello : Say hello\n- specialsource : Core info\n- email : Contact\n- status : Check Live Connected Users\n- login : Login via ID/Password prompt\n- logout : Log out of current session\n- clear : Clear console`);
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
greetings: `[SYSTEM V3.2] INTERFACE ONLINE. TYPE 'help' FOR COMMANDS.`,
|
greetings: `[SYSTEM V3.2] INTERFACE ONLINE. TYPE 'help' FOR COMMANDS.`,
|
||||||
prompt: 'admin@specialsource:~$ '
|
prompt: terminalPrompt()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -584,7 +646,7 @@ onUnmounted(() => {
|
|||||||
.status-dot.secure-mode { background: var(--tui-color); box-shadow: 0 0 8px var(--tui-color); }
|
.status-dot.secure-mode { background: var(--tui-color); box-shadow: 0 0 8px var(--tui-color); }
|
||||||
|
|
||||||
/* --- 컨텐츠 영역 --- */
|
/* --- 컨텐츠 영역 --- */
|
||||||
.content-area { flex: 1; padding: 20px; min-height: 400px; overflow-y: auto; display: flex; flex-direction: column; }
|
.content-area { flex: 1; padding: 8px 20px; min-height: 220px; overflow-y: auto; display: flex; flex-direction: column; }
|
||||||
.sub-page { max-width: 1000px; margin: 0 auto; padding-top: 15px; width: 100%; }
|
.sub-page { max-width: 1000px; margin: 0 auto; padding-top: 15px; width: 100%; }
|
||||||
.page-title { border-bottom: 1px solid var(--tui-border); padding-bottom: 10px; margin-bottom: 20px; color: var(--tui-color);}
|
.page-title { border-bottom: 1px solid var(--tui-border); padding-bottom: 10px; margin-bottom: 20px; color: var(--tui-color);}
|
||||||
|
|
||||||
@@ -596,7 +658,7 @@ onUnmounted(() => {
|
|||||||
justify-content: center; align-items: center; pointer-events: all;
|
justify-content: center; align-items: center; pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ascii-container { width: 100%; overflow: auto; padding: 20px; box-sizing: border-box; position: relative; }
|
.ascii-container { width: 100%; overflow: auto; padding: 6px 20px; box-sizing: border-box; position: relative; }
|
||||||
.ascii-art {
|
.ascii-art {
|
||||||
font-weight: bold; white-space: pre; display: block; margin: 0 auto; width: max-content;
|
font-weight: bold; white-space: pre; display: block; margin: 0 auto; width: max-content;
|
||||||
color: var(--tui-color) !important; font-size: 14px; line-height: 1.2;
|
color: var(--tui-color) !important; font-size: 14px; line-height: 1.2;
|
||||||
@@ -648,7 +710,7 @@ iframe { width: 100%; height: 180px; border-radius: 4px; }
|
|||||||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||||||
|
|
||||||
/* 하단 CRT 터미널 */
|
/* 하단 CRT 터미널 */
|
||||||
.crt-monitor-frame { height: 300px; margin: 10px 40px 30px; border: 2px solid #1a1a1a; background: #0a0a0a; overflow: hidden; }
|
.crt-monitor-frame { height: 300px; margin: 6px 40px 14px; border: 2px solid #1a1a1a; background: #0a0a0a; overflow: hidden; }
|
||||||
.monitor-header { background: #151515; padding: 5px 15px; color: #555; display: flex; justify-content: space-between; font-size: 12px;}
|
.monitor-header { background: #151515; padding: 5px 15px; color: #555; display: flex; justify-content: space-between; font-size: 12px;}
|
||||||
.monitor-status { color: var(--tui-dim); animation: blink 2s infinite; }
|
.monitor-status { color: var(--tui-dim); animation: blink 2s infinite; }
|
||||||
.crt-screen-overlay { position: relative; height: calc(100% - 30px); background: #020802; }
|
.crt-screen-overlay { position: relative; height: calc(100% - 30px); background: #020802; }
|
||||||
@@ -731,7 +793,7 @@ iframe { width: 100%; height: 180px; border-radius: 4px; }
|
|||||||
/* 5. 터미널 창 모바일 최적화 */
|
/* 5. 터미널 창 모바일 최적화 */
|
||||||
.crt-monitor-frame {
|
.crt-monitor-frame {
|
||||||
height: 180px !important;
|
height: 180px !important;
|
||||||
margin: 10px !important;
|
margin: 6px 10px !important;
|
||||||
}
|
}
|
||||||
.monitor-header { padding: 4px 10px; font-size: 10px; }
|
.monitor-header { padding: 4px 10px; font-size: 10px; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user