misc: move gateway path

This commit is contained in:
2021-09-02 00:46:33 +09:00
parent 46ae4669ea
commit f4fb4ec00d
13 changed files with 58 additions and 55 deletions
+7
View File
@@ -0,0 +1,7 @@
interface Window {
pathConfig: {
root: string,
sharedIcon: string,
gameImage: string,
}
}
+283
View File
@@ -0,0 +1,283 @@
import axios from 'axios';
import $ from 'jquery';
import { isNumber } from 'lodash';
import { TemplateEngine } from '../util/TemplateEngine';
import { InvalidResponse } from '../defs';
import { setAxiosXMLHttpRequest } from '../util/setAxiosXMLHttpRequest';
import { unwrap_any } from '../util/unwrap_any';
import { convertFormData } from '../util/convertFormData';
import { exportWindow } from '../util/exportWindow';
type UserEntry = {
userID: string,
userName: string,
email: string,
authType: string | null,
userGrade: number,
blockUntil: string | null,
nickname: string,
icon: string,
joinDate: string,
loginDate: string,
deleteAfter: string | null,
}
type UserListResponse = {
result: true,
users: UserEntry[],
servers: string[],
allowJoin: boolean,
allowLogin: boolean,
}
const userFrame = '\
<tr id="userinfo_<%userID%>" data-username="<%userName%>" data-id="<%userID%>">\
<th scope="row"><%userID%></th>\
<td><%userName%></td>\
<td class="small"><%emailFunc(email)%><br>(<%authType%>)</td>\
<td><%userGradeText%><p class="small hide_text user_grade_<%userGrade%>" style="margin:0;"><%shortDate(blockUntil)%></p></td>\
<td><%nickname%></td>\
<td><img class="generalIcon" src="<%icon%>" width="64" height="64"></td>\
<td class="small"><%slotGeneralList%></td>\
<td class="small"><%shortDate(joinDate)%></td>\
<td class="small"><%shortDate(loginDate)%></td>\
<td class="small"><%shortDate(deleteAfter)%></td>\
<td>\
<div class="btn-group" role="group">\
<button type="button" onclick="changeUserStatus(\'delete\', this);" class="btn btn-danger btn-sm">강제<br>탈퇴</button>\
<button type="button" onclick="changeUserStatus(\'reset_pw\', this);" class="btn btn-info btn-sm">암호<br>변경</button>\
<button type="button" onclick="changeUserStatus(\'block\', this);" class="btn btn-warning btn-sm">유저<br>차단</button>\
<button type="button" onclick="changeUserStatus(\'unblock\', this);" class="btn btn-secondary btn-sm">차단<br>해제</button>\
<button type="button" onclick="changeUserStatus(\'set_userlevel\', this);" class="btn btn-primary btn-sm">별도<br>권한</button>\
</div>\
</td>\
</tr>';
function convUserGrade(grade: number): string {
const userGradeMap = {
0: '차단',
1: '일반',
4: '특별',
5: '부운영자',
6: '운영자'
};
if (grade in userGradeMap) {
return userGradeMap[grade as (keyof typeof userGradeMap)];
}
return grade.toString();
}
function fillAllowJoinLogin(result: UserListResponse) {
if (result.allowJoin) {
$('#allow_join_y').trigger('click');
}
else {
$('#allow_join_n').trigger('click');
}
if (result.allowLogin) {
$('#allow_login_y').trigger('click');
}
else {
$('#allow_login_n').trigger('click');
}
}
function fillUserList(result: UserListResponse) {
const $user_list = $('#user_list');
$user_list.empty();
const slotGeneralList = $.map(result.servers, function (value) {
return `<span class="server_generalName_${value}"></span>`;
}).join('<br>');
const emailFunc = function (text: string) {
return String(text).replace('@', '@<br>');
}
const brFunc = function (text: string) {
return String(text).split(' ').join('<br>');
};
const shortDateFunc = function (date: string | null) {
if (!date) {
return '-';
}
return brFunc(date.substr(2));
}
$.each(result.users, function (idx, user) {
const templateItem = {
...user,
email: user.email ?? '-',
br: brFunc,
shortDate: shortDateFunc,
emailFunc: emailFunc,
slotGeneralList: slotGeneralList,
userGradeText: convUserGrade(user.userGrade),
}
$user_list.append(
TemplateEngine(userFrame, templateItem)
)
});
//TODO: slotGeneralList에 값을 채워야함. ajax로 받아올 필요 있음
}
async function changeSystem(action: string, param?: string): Promise<void> {
const text = `${action}${param ? (', ' + param) : ''}을 진행합니다.`;
if (!confirm(text)) {
return;
}
let result: InvalidResponse | {
result: true,
affected?: number,
};
try {
const response = await axios({
url: 'j_set_userlist.php',
method: 'post',
responseType: 'json',
data: convertFormData({
'action': action,
'param': param ?? null
})
})
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
return;
}
if (result.affected) {
alert(`${result.affected}건이 처리되었습니다.`);
await refreshAll();
}
else {
alert('완료되었습니다.');
}
}
async function changeUserStatus(action: string, userID: Element | number, param?: number): Promise<void> {
if (userID instanceof Element) {
userID = parseInt($(userID).parents('tr').data('id'));
}
if (!isNumber(userID)) {
alert('userID가 올바르게 지정되지 않았습니다!');
return;
}
if (action == 'set_userlevel') {
if (!isNumber(param)) {
param = parseInt(prompt('원하는 등급을 입력해주세요.(1:일반, 4:특별, 5:부운영자, 6:운영자)', '1') ?? '0');
}
if (param < 1 || param > 6) {
alert('올바르지 않습니다.');
return;
}
}
if (action == 'block') {
if (!isNumber(param)) {
param = parseInt(prompt('블록 기간을 입력해주세요. <= 0은 반영구(50년)입니다.', '7') ?? '7');
}
}
const userName = unwrap_any<string>($('#userinfo_' + userID).data('username'));
const text = `${userName}에 대해서 ${action}${param ? (', ' + param) : ''}을 진행합니다.`;
if (!confirm(text)) {
return;
}
let result: InvalidResponse | {
result: true,
detail?: string,
};
try {
const response = await axios({
url: 'j_set_userlist.php',
method: 'post',
responseType: 'json',
data: convertFormData({
'action': action,
'user_id': userID,
'param': param ?? null
})
})
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
return;
}
if (result.detail) {
alert(`완료되었습니다: ${result.detail}`);
}
else {
alert('완료되었습니다.');
}
await refreshAll();
}
async function refreshAll() {
let result: InvalidResponse | UserListResponse;
try {
const response = await axios({
url: 'j_get_userlist.php',
method: 'post',
responseType: 'json',
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
return;
}
fillAllowJoinLogin(result);
fillUserList(result);
}
$(async function () {
setAxiosXMLHttpRequest();
await refreshAll();
$('input[name=allow_join], input[name=allow_login]').on('change', async function () {
const $this = $(this);
await changeSystem(unwrap_any<string>($this.attr('name')), unwrap_any<string>($this.val()));
})
});
exportWindow(changeSystem, 'changeSystem');
exportWindow(changeUserStatus, 'changeUserStatus');
+332
View File
@@ -0,0 +1,332 @@
import $ from 'jquery';
import axios from 'axios';
import { setAxiosXMLHttpRequest } from '../util/setAxiosXMLHttpRequest';
import { InvalidResponse } from '../defs';
import { convertFormData } from '../util/convertFormData';
import { TemplateEngine } from "../util/TemplateEngine";
import { unwrap_any } from '../util/unwrap_any';
import { unwrap } from '../util/unwrap';
type ServerUpdateResponse = {
result: true,
version: string,
server: string,
imgResult: false,
}
type ServerRootUpdateResponse = {
result: true,
version: string,
server: string,
imgResult: true,
imgDetail: string,
}
type ServerStateItem = {
name: string,
korName: string,
color: string,
isRoot: boolean,
lastGitPath: string,
valid: boolean,
run: boolean,
installed: boolean,
version: string,
reason: string,
status?: string,
}
type ServerStateResponse = {
acl: Record<string, string[]>,
server: ServerStateItem[],
grade: number,
}
type ServerChangeResponse = {
result: true,
installURL?: string,
}
const serverAdminTemplate = '\
<tr class="bg0 server_admin_<%name%>" data-server_name="<%name%>" data-is_root="<%isRoot%>" data-git-path="<%lastGitPath%>">\
<th style="color:<%color%>;"><%korName%>(<%name%>)</th>\
<td><%status%></td>\
<td><%version%></td>\
<td><button type="button" class="serv_act_close with_skin valid_if_set with_border obj_fill" onclick="modifyServerStatus(this, \'close\');">폐쇄</button></td>\
<td><button type="button" class="serv_act_open with_skin valid_if_set with_border obj_fill" onclick="modifyServerStatus(this, \'open\');">오픈</button></td>\
<td><a class="just_link" href="../<%name%>/install.php"><button type="button" class="serv_act_reset with_skin valid_if_set with_border obj_fill">리셋</button></a></td>\
<td><a class="just_link" href="../<%name%>/install_db.php"><button type="button" class="serv_act_hard_reset with_skin valid_if_installed only_admin with_border obj_fill">하드리셋</button></a></td>\
<td><button type="button" class="serv_act_login_close with_skin valid_if_set with_border obj_fill" onclick="Entrance_AdminClosedLogin(this);">폐쇄중 로그인</button></td>\
<td><button type="button" class="serv_act_119 with_skin valid_if_set with_border obj_fill" onclick="Entrance_AdminOpen119(this);">서버119</button></td>\
<td><button type="button" class="serv_act_update with_skin with_border obj_fill" onclick="serverUpdate(this);">업데이트</button></td>\
</tr>\
';//TODO: npm install 관련 기능 추가, js/css output 경로 변경
declare global {
interface Window {
modifyServerStatus: (caller: HTMLElement, action: string) => void;
Entrance_AdminClosedLogin: (caller: HTMLElement) => void;
Entrance_AdminOpen119: (caller: HTMLElement) => void;
serverUpdate: (caller: HTMLElement) => void;
adminGrade: number;
aclList: Record<string, string[]>;
}
}
async function serverUpdate(caller: HTMLElement) {
const $caller = $(caller);
const $tr = $caller.parents('tr');
const server = unwrap_any<string>($tr.data('server_name'));
let isRoot: string | boolean = unwrap_any<string>($tr.data('is_root'));
let target = $tr.data('gitPath');
if (typeof isRoot !== 'boolean') {
isRoot = (isRoot != 'false');
}
let allowFullUpdate = (server in window.aclList && window.aclList[server].indexOf('fullUpdate') >= 0);
allowFullUpdate = allowFullUpdate || window.adminGrade > 5;
let allowUpdate = (server in window.aclList && window.aclList[server].indexOf('update') >= 0);
allowUpdate = allowUpdate || window.adminGrade >= 5;
allowUpdate = allowUpdate || allowFullUpdate;
if (!allowUpdate) {
alert('권한이 없습니다!');
return;
}
if (allowFullUpdate) {
target = prompt('가져올 git tree-ish 명을 입력해주세요.', target)
if (!target) {
return;
}
}
else if (isRoot) {
if (!confirm('서버 라이브러리, 루트 서버에 대해 git pull을 실행합니다.')) {
return;
}
}
else if (!confirm('다음 git tree-ish 주소로 업데이트를 시도합니다 : ' + target)) {
return;
}
let result: InvalidResponse | ServerUpdateResponse | ServerRootUpdateResponse;
try {
const response = await axios({
url: '../j_updateServer.php',
responseType: 'json',
method: 'post',
data: convertFormData({
server: server,
target: target
})
});
result = response.data;
} catch (e) {
console.error(e);
alert(`에러: ${e}`);
location.reload();
return;
}
if (!result.result) {
alert(`실패했습니다: ${result.reason}`);
return;
}
let aux = '';
if (result.imgResult) {
aux = ` (이미지 서버 갱신:${result.imgResult}, ${result.imgDetail})`;
}
alert(`${result.server} 서버가 ${result.version} 버전으로 업데이트 되었습니다.${aux}`);
}
function drawServerAdminList(serverList: ServerStateResponse) {
const $table = $('#server_admin_list');
const $showErrorLog = $('#showErrorLog');
if (serverList.grade >= 5) {
$showErrorLog.show();
}
$.each(serverList.server, function (idx, server) {
console.log(server);
let status: string;
if (!server.valid) {
status = `에러, ${server.reason}`;
}
else if (!server.run) {
status = '폐쇄됨';
}
else {
status = '운영 중';
}
server.status = status;
const $tr = $(TemplateEngine(serverAdminTemplate, server));
$table.append($tr);
if (serverList.grade < 4) {
$tr.find('button').prop('disabled', true);
}
if (!server.valid) {
$tr.find('.valid_if_set').prop('disabled', true);
}
if (!server.installed) {
$tr.find('.valid_if_installed').prop('disabled', true);
}
const aclByServer = serverList.acl[server.name];
$.each(aclByServer, function (idx, action) {
console.log(action);
if (action == 'update' || action == 'fullUpdate') {
if (!server.installed) {
return true;
}
$tr.find('.serv_act_update').prop('disabled', false);
$showErrorLog.show();
}
else if (action == 'openClose') {
if (!server.valid) {
return true;
}
$tr.find('.serv_act_open, .serv_act_close').prop('disabled', false);
}
else if (action == 'reset') {
if (!server.installed) {
return true;
}
$tr.find('.serv_act_reset, .serv_act_close').prop('disabled', false);
}
});
});
window.adminGrade = serverList.grade;
window.aclList = serverList.acl;
if (serverList.grade <= 5) {
$table.find('.only_admin').prop('disabled', true);
}
}
export async function loadPlugin(): Promise<void> {
setAxiosXMLHttpRequest();
Entrance_AdminInit();
window.modifyServerStatus = modifyServerStatus;
window.Entrance_AdminClosedLogin = Entrance_AdminClosedLogin;
window.Entrance_AdminOpen119 = Entrance_AdminOpen119;
window.serverUpdate = serverUpdate;
const response = await axios({
url: 'j_server_get_admin_status.php',
method: 'post',
responseType: 'json'
});
drawServerAdminList(response.data);
}
function Entrance_AdminInit() {
console.log('adminInit');
$("#Entrance_000202").on('click', Entrance_Member);
$("#notice_change_btn").on('click', Entrance_AdminChangeNotice);
}
function Entrance_Member(e: JQuery.Event) {
e.preventDefault();
$("#Entrance_00").hide();
$("#EntranceMember_00").show();
}
async function Entrance_AdminChangeNotice(e: JQuery.Event) {
e.preventDefault();
const notice = unwrap_any<string>($("#notice_edit").val());
if (!confirm('정말 실행하시겠습니까?')) {
return;
}
let result: InvalidResponse | ServerChangeResponse;
try {
const response = await axios({
url: 'j_server_change_status.php',
method: 'post',
responseType: 'json',
data: convertFormData({
action: 'notice',
notice: notice
})
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
if (!result.result) {
alert(`실패했습니다: ${result.reason}`);
return;
}
location.reload();
}
async function modifyServerStatus(caller: HTMLElement, action: string) {
const $caller = $(caller);
const server = unwrap_any<string>($caller.parents('tr').data('server_name'));
if (!confirm('정말 실행하시겠습니까?')) {
return;
}
let result: InvalidResponse | ServerChangeResponse;
try {
const response = await axios({
url: 'j_server_change_status.php',
method: 'post',
responseType: 'json',
data: convertFormData({
server: server,
action: action
})
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
if (!result.result) {
alert(`실패했습니다: ${result.reason}`);
return;
}
if (action == 'reset') {
location.href = unwrap(result.installURL);
} else {
location.reload();
}
}
function Entrance_AdminClosedLogin(caller: HTMLElement) {
const $caller = $(caller);
const serverDir = $caller.parents('tr').data('server_name');
location.href = serverDir + "/npc_login.php";
}
function Entrance_AdminOpen119(caller: HTMLElement) {
const $caller = $(caller);
const serverDir = $caller.parents('tr').data('server_name');
location.href = `../${serverDir}/_119.php`;
}
+1
View File
@@ -0,0 +1 @@
export * from '../common_deprecated';
+312
View File
@@ -0,0 +1,312 @@
import axios from 'axios';
import $ from 'jquery';
import { initTooltip } from '../common_legacy';
import { TemplateEngine } from "../util/TemplateEngine";
import { InvalidResponse } from '../defs';
import { getDateTimeNow } from '../util/getDateTimeNow';
import { setAxiosXMLHttpRequest } from '../util/setAxiosXMLHttpRequest';
import { loadPlugin as loadAdminPlugin } from './admin_server';
declare const isAdmin: boolean;
const serverListTemplate = "\
<tr class='server_item bg0 server_name_<%name%>' data-server='<%name%>'>\
<td class='server_name obj_tooltip' data-toggle='tooltip' data-placement='bottom'>\
<span style='font-weight:bold;font-size:1.4em;color:<%color%>'><%korName%>섭</span><br>\
<span class='n_country'></span>\
<span class='tooltiptext server_date'></span>\
</td>\
<td colspan='4' class='server_down'>- 폐 쇄 중 -</td>\
</tr>\
";
const serverTextInfo = "\
<td>\
서기 <%year%>년 <%month%>월 (<span style='color:orange;'><%scenario%></span>)<br>\
유저 : <%userCnt%> / <%maxUserCnt%>명 <span style='color:cyan;'>NPC : <%npcCnt%>명</span> (<span style='color:limegreen;'><%turnTerm%>분 턴 서버</span>)<br>\
(상성 설정:<%fictionMode%>), (기타 설정:<%otherTextInfo%>)\
</td>\
";
const serverProvisionalInfo = "\
<td>\
- 오픈 일시 : <%opentime%> -<br>\
서기 <%year%>년 <%month%>월 (<span style='color:orange;'><%scenario%></span>)<br>\
유저 : <%userCnt%> / <%maxUserCnt%>명 <span style='color:cyan;'>NPC : <%npcCnt%>명</span> (<span style='color:limegreen;'><%turnTerm%>분 턴 서버</span>)<br>\
(상성 설정:<%fictionMode%>), (기타 설정:<%otherTextInfo%>)\
</td>\
";
const serverFullTemplate = "\
<td colspan='4' class='server_full'>- 장수 등록 마감 -</td>\
";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const serverSelectPoolTemplate = "\
<td colspan='2' class='not_registered'>- 미 등 록 -</td>\
<td class='ignore_border'>\
<a href='<%serverPath%>/select_general_from_pool.php'><button type='button' class='general_select with_skin'>장수선택</button></a>\
</td>\
";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const serverLoginBtn = "<a href='<%serverPath%>/' class='item'\
><button type='button' class='fill_box with_skin'>입장</button\
></a>";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const serverCreateBtn = "<a href='<%serverPath%>/join.php' class='item'\
><button type='button' class='fill_box with_skin'>장수생성</button\
></a>";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const serverSelectNPCBtn = "<a href='<%serverPath%>/select_npc.php' class='item'\
><button type='button' class='fill_box with_skin'>장수빙의</button\
></a>";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const serverSelectPoolBtn = "<a href='<%serverPath%>/select_general_from_pool.php' class='item'\
><button type='button' class='fill_box with_skin'>장수선택</button\
></a>";
const serverCreateTemplate = "\
<td colspan='2' class='not_registered'>- 미 등 록 -</div>\
<td class='ignore_border vertical_flex BtnPlate'>\
<%if(canCreate) {%>\
<a href='<%serverPath%>/join.php' class='item'><button type='button' class='fill_box with_skin'>장수생성</button></a>\
<%}%>\
<%if(canSelectNPC) {%>\
<a href='<%serverPath%>/select_npc.php' class='item'><button type='button' class='fill_box with_skin'>장수빙의</button></a>\
<%}%>\
<%if(canSelectPool) {%>\
<a href='<%serverPath%>/select_general_from_pool.php' class='item'><button type='button' class='fill_box with_skin'>장수선택</button></a>\
<%}%>\
</td>";
const serverLoginTemplate = "\
<td style='background:url(\"<%picture%>\");background-size: 64px 64px;'></td>\
<td><%name%></td>\
<td class='ignore_border vertical_flex BtnPlate'>\
<a href='<%serverPath%>/' class='item'><button type='button' class='fill_box with_skin'>입장</button></a>\
</td>\
";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const serverReservedTemplate = "\
<td colspan='4' class='server_reserved'>\
<%openDatetime==starttime?'':'- 가오픈 일시 : '+openDatetime+ '- <br>'%>\
- 오픈 일시 : <%starttime%> - <br>\
<span style='color:orange;'><%scenarioName%></span> <span style='color:limegreen;'><%turnterm%>분 턴 서버</span><br>\
(상성 설정:<%fictionMode%>), (빙의 여부:<%npcMode%>), (최대 스탯:<%gameConf.defaultStatTotal%>), (기타 설정:<%otherTextInfo%>)\
</td>\
";
type ServerResponseItem = {
color: string,
korName: string,
name: string,
exists: boolean,
enable: boolean,
};
type ServerResponse = {
result: true;
reason: 'success';
server: ServerResponseItem[];
}
type RawServerResponse = InvalidResponse | ServerResponse;
type ReservedGameInfo = {
scenarioName: string,
turnterm: number,
fictionMode: '가상' | '사실',
block_general_create: boolean,
npcMode: '불가' | '가능' | '선택 생성',
openDatetime: string,
starttime: string,
gameConf: Record<string, string | number>,
otherTextInfo: string,
}
type GameInfo = {
isUnited: number,
npcMode: '불가' | '가능' | '선택 생성',
year: number,
month: number,
scenario: string,
maxUserCnt: number,
turnTerm: number,
opentime: string,
starttime: string,
turntime: string,
join_mode: string,
fictionMode: '가상' | '사실',
block_general_create: boolean,
autorun_user: string,
userCnt: number,
npcCnt: number,
nationCnt: number,
otherTextInfo: string,
defaultStatTotal: number,
}
type MyInfo = {
name: string,
picture: string,
serverPath?: string,
}
type ServerDetailResponse = {
reserved?: ReservedGameInfo;
game: GameInfo;
me: MyInfo | null | undefined;
}
$(function ($) {
setAxiosXMLHttpRequest();
$("#btn_logout").on('click', Entrance_Logout);
void Entrance_UpdateServer();
if(isAdmin){
void loadAdminPlugin();
}
});
async function Entrance_UpdateServer() {
let data: RawServerResponse;
try {
const response = await axios({
url: 'j_server_get_status.php',
responseType: 'json',
method: 'post'
});
data = response.data;
}
catch (e) {
alert(e);
return;
}
if (!data.result) {
alert(data.reason);
return;
}
await Entrance_drawServerList(data.server);
}
async function Entrance_drawServerList(serverInfos: ServerResponseItem[]) {
const $serverList = $('#server_list');
const now = getDateTimeNow();
const serverDetailInfoP: Record<string, Promise<ServerDetailResponse>> = {};
for (const serverInfo of serverInfos) {
if(!serverInfo.exists){
continue;
}
const responseP = axios({
url: `../${serverInfo.name}/j_server_basic_info.php`,
method: 'get',
responseType: 'json'
}).then(v=>{
return v.data as ServerDetailResponse;
});
serverDetailInfoP[serverInfo.name] = responseP;
}
for (const serverInfo of serverInfos) {
const $serverHtml = $(TemplateEngine(serverListTemplate, serverInfo));
$serverList.append($serverHtml);
if (!serverInfo.exists) {
continue;
}
const serverPath = `../${serverInfo.name}`;
let response: ServerDetailResponse;
try{
if(!(serverInfo.name in serverDetailInfoP)){
continue;
}
response = await serverDetailInfoP[serverInfo.name];
}
catch(e){
console.error(e);
continue;
}
if(!response.game){
continue;
}
const game = response.game;
//TODO: 서버 폐쇄 방식을 새롭게 변경
$serverHtml.find('.server_down').detach();
if (game.isUnited == 3) {
$serverHtml.find('.n_country').html('§이벤트 종료§');
$serverHtml.find('.server_date').html(`${game.starttime} <br>~ ${game.turntime}`);
} else if (game.isUnited == 1) {
$serverHtml.find('.n_country').html('§이벤트 진행중§');
$serverHtml.find('.server_date').html(`${game.starttime} ~`);
} else if (game.isUnited == 2) {
$serverHtml.find('.n_country').html('§천하통일§');
$serverHtml.find('.server_date').html(`${game.starttime} <br>~ ${game.turntime}`);
} else if (game.opentime <= now) {
$serverHtml.find('.n_country').html(`<${game.nationCnt}국 경쟁중>`);
$serverHtml.find('.server_date').html(`${game.starttime} ~`);
} else {
$serverHtml.find('.n_country').html('-가오픈 중-');
$serverHtml.find('.server_date').html(`${game.starttime} ~`);
}
if (game.opentime <= now) {
$serverHtml.append(
TemplateEngine(serverTextInfo, game)
);
} else {
$serverHtml.append(
TemplateEngine(serverProvisionalInfo, game)
);
}
if (response.me && response.me.name) {
const me = response.me;
me.serverPath = serverPath;
$serverHtml.append(
TemplateEngine(serverLoginTemplate, me)
);
} else if (game.userCnt >= game.maxUserCnt) {
$serverHtml.append(
TemplateEngine(serverFullTemplate, {})
);
} else {
$serverHtml.append(
TemplateEngine(serverCreateTemplate, {
serverPath: serverPath,
canCreate: !game.block_general_create,
canSelectNPC: game.npcMode == '가능',
canSelectPool: game.npcMode == '선택 생성'
})
)
}
initTooltip($serverHtml);
}
}
async function Entrance_Logout() {
const response = await axios({
url: 'j_logout.php',
method: 'post',
responseType: 'json'
});
const data: InvalidResponse = response.data;
if(!data.result){
alert(`로그아웃 실패: ${data.reason}`);
return;
}
location.href = "../";
}
+315
View File
@@ -0,0 +1,315 @@
import $ from 'jquery';
import axios from 'axios';
import { setAxiosXMLHttpRequest } from '../util/setAxiosXMLHttpRequest';
import { InvalidResponse } from '../defs';
import { JQValidateForm, NamedRules } from '../util/jqValidateForm';
import { convertFormData } from '../util/convertFormData';
import { unwrap_any } from '../util/unwrap_any';
import { mb_strwidth } from '../util/mb_strwidth';
import { isString } from 'lodash';
import { sha512 } from 'js-sha512';
async function changeInstallMode() {
let result: {
step: string,
globalSalt: string,
};
try {
const response = await axios({
url: 'j_install_status.php',
method: 'post',
responseType: 'json'
});
result = response.data;
} catch (e) {
console.error(e);
alert(`에러: ${e}`);
return;
}
if (result.step == 'config') {
$('#db_form_card').show();
$('#admin_form_card').hide();
return;
}
if (result.step == 'admin') {
$('#db_form_card').hide();
$('#admin_form_card').show();
$('#global_salt').val(result.globalSalt);
return;
}
if (result.step == 'done') {
alert('설치가 완료되었습니다.');
window.location.href = "..";
return;
}
if (result.step == 'conn_fail') {
$('#db_form_card').hide();
$('#admin_form_card').hide();
alert('설치 이후 DB 설정이 변경된 것 같습니다. RootDB.php 파일의 설정을 확인해주십시오.');
return;
}
if (result.step == 'sql_fail') {
$('#db_form_card').hide();
$('#admin_form_card').hide();
alert('DB가 제대로 설정되지 않았거나, 훼손된 것 같습니다. DB를 복구하거나 RootDB.php 파일을 삭제 후 재설치를 진행해 주십시오.');
return;
}
alert(`알 수 없는 오류: ${result}`);
}
function setupDBForm() {
const parentPathname = location.pathname.split('/').slice(0, -2).join('/');
$('#serv_host').val(
[location.protocol, '//', location.host, parentPathname].join('')
);
$('#btn_random_generate_key').on('click', function (e) {
e.preventDefault();
let token = '';
while (token.length < 24) {
token += (Math.random() + 1).toString(36).substring(7);
}
token = token.substr(0, 24);
$('#image_request_key').val(token);
});
type DBFormType = {
db_host: string,
db_port: string,
db_id: string,
db_pw: string,
db_name: string,
serv_host: string,
shared_icon_path: string,
game_image_path: string,
image_request_key: string,
kakao_rest_key: string,
kakao_admin_key: string,
}
const descriptor: NamedRules<DBFormType> = {
db_host: {
required: true,
type: 'string',
},
db_port: {
required: true,
type: 'number',
transform: parseInt,
},
db_id: {
required: true,
type: 'string',
},
db_pw: {
required: true,
type: 'string',
},
db_name: {
required: true,
type: 'string',
},
serv_host: {
required: true,
type: 'string',
},
shared_icon_path: {
required: true,
type: 'string',
},
game_image_path: {
required: true,
type: 'string',
},
image_request_key: {
required: false,
type: 'string',
min: 16,
},
kakao_rest_key: {
required: false,
type: 'string',
},
kakao_admin_key: {
required: false,
type: 'string',
}
}
const validator = new JQValidateForm($('#db_form'), descriptor);
validator.installChangeHandler();
$('#db_form').on('submit', async function (e) {
e.preventDefault();
const values = await validator.validate();
if (values === undefined) {
return;
}
let result: InvalidResponse;
try {
const response = await axios({
url: 'j_setup_db.php',
method: 'post',
responseType: 'json',
data: convertFormData({
db_host: values.db_host,
db_port: values.db_port,
db_id: values.db_id,
db_pw: values.db_pw,
db_name: values.db_name,
serv_host: values.serv_host,
shared_icon_path: values.shared_icon_path,
game_image_path: values.game_image_path,
image_request_key: values.image_request_key,
kakao_rest_key: values.kakao_rest_key,
kakao_admin_key: values.kakao_admin_key,
})
});
result = response.data;
} catch (e) {
console.error(e);
alert(`에러: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
return;
}
alert('RootDB.php가 생성되었습니다. 관리자 계정 생성을 진행합니다.');
await changeInstallMode();
});
}
function maxStrWidth(maxWidth: number) {
return (rule: unknown, value: string) => {
if (!value || !isString(value) || mb_strwidth(value) > maxWidth) {
return new Error(`글자 너비가 알파벳 ${maxWidth} 자보다 길지 않아야합니다`);
}
return true;
};
}
function setupAdminForm() {
type AdminValues = {
username: string,
password: string,
confirm_password: string,
nickname: string,
}
const descriptor: NamedRules<AdminValues> = {
username: {
required: true,
min: 4,
max: 64,
type: 'string',
options: {//FIXME: options.messages가 동작하지 않는다?
messages: {
required: "유저명을 입력해주세요",
string: {
min: (v, l) => `${l}글자 이상 입력하셔야 합니다`,
max: (v, l) => `${l}자를 넘을 수 없습니다`,
}
}
}
},
password: {
required: true,
type: 'string',
min: 6,
options: {
messages: {
required: "비밀번호를 입력해주세요",
string: {
min: (v, l) => `비밀번호는 적어도 ${l}글자 이상이어야 합니다`
}
}
}
},
confirm_password: {
required: true,
type: 'string',
min: 6,
validator: (rule, value: string, _callback, source) => {
console.log(value);
if (value != (source.password ?? '')) {
return new Error('비밀번호가 일치하지 않습니다');
}
return true;
},
options: {
messages: {
required: "비밀번호를 입력해주세요",
string: {
min: (v, l) => `비밀번호는 적어도 ${l}글자 이상이어야 합니다`
}
}
}
},
nickname: {
required: true,
validator: maxStrWidth(18),
options: {
messages: {
required: "유저명을 입력해주세요",
}
}
},
}
const validator = new JQValidateForm($('#db_form'), descriptor);
validator.installChangeHandler();
$('#admin_form').on('submit', async function (e) {
e.preventDefault();
const values = await validator.validate();
if (values === undefined) {
return;
}
const raw_password = values.password;
const salt = unwrap_any<string>($('#global_salt').val());
console.log(salt + raw_password + salt);
const hash_pw = sha512(salt + raw_password + salt);
let result: InvalidResponse;
try {
const response = await axios({
url: 'j_setup_db.php',
method: 'post',
responseType: 'json',
data: convertFormData({
username: values.username,
password: hash_pw,
nickname: values.nickname,
})
});
result = response.data;
} catch (e) {
console.error(e);
alert(`에러: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
return;
}
alert('RootDB.php가 생성되었습니다. 관리자 계정 생성을 진행합니다.');
await changeInstallMode();
});
}
$(function () {
setAxiosXMLHttpRequest();
void changeInstallMode();
setupDBForm();
setupAdminForm();
});
+189
View File
@@ -0,0 +1,189 @@
import $ from 'jquery';
import axios from 'axios';
import { JQValidateForm, NamedRules } from '../util/jqValidateForm';
import { convertFormData } from '../util/convertFormData';
import { InvalidResponse } from '../defs';
import { setAxiosXMLHttpRequest } from '../util/setAxiosXMLHttpRequest';
import { unwrap_any } from '../util/unwrap_any';
import { sha512 } from 'js-sha512';
import { isString } from 'lodash';
import { mb_strwidth } from '../util/mb_strwidth';
$(async function () {
setAxiosXMLHttpRequest();
const terms1P = axios('../terms.1.html');
const terms2P = axios('../terms.2.html');
type JoinFormType = {
secret_agree: boolean,
secret_agree2: boolean,
third_use: boolean,
username: string,
password: string,
confirm_password: string,
nickname: string,
}
const descriptor: NamedRules<JoinFormType> = {
username: {
required: true,
min: 4,
max: 64,
type: 'string',
asyncValidator: async (rule, value: string) => {
const response = await axios({
url: 'j_check_dup.php',
method: 'post',
responseType: 'json',
data: convertFormData({
type: 'username',
value: value,
})
});
const isValid: boolean|string = response.data;
if (isString(isValid)) {
throw new Error(isValid);
}
},
options: {//FIXME: options.messages가 동작하지 않는다?
messages: {
required: "유저명을 입력해주세요",
string: {
min: (v, l) => `${l}글자 이상 입력하셔야 합니다`,
max: (v, l) => `${l}자를 넘을 수 없습니다`,
}
}
}
},
password: {
required: true,
type: 'string',
min: 6,
options: {
messages: {
required: "비밀번호를 입력해주세요",
string: {
min: (v, l) => `비밀번호는 적어도 ${l}글자 이상이어야 합니다`
}
}
}
},
confirm_password: {
required: true,
type: 'string',
min: 6,
validator: (rule, value: string, _callback, source) => {
console.log(value);
if (value != (source.password ?? '')) {
return new Error('비밀번호가 일치하지 않습니다');
}
return true;
},
options: {
messages: {
required: "비밀번호를 입력해주세요",
string: {
min: (v, l) => `비밀번호는 적어도 ${l}글자 이상이어야 합니다`
}
}
}
},
nickname: {
required: true,
asyncValidator: async (rule, value: string) => {
if (!value || !isString(value) || mb_strwidth(value) > 18) {
throw new Error(`글자 너비가 알파벳 ${18}자보다 길지 않아야합니다`);
}
const response = await axios({
url: 'j_check_dup.php',
method: 'post',
responseType: 'json',
data: convertFormData({
type: 'nickname',
value: value,
})
});
const isValid: boolean|string = response.data;
if (isString(isValid)) {
throw new Error(isValid);
}
},
options: {
messages: {
required: "유저명을 입력해주세요",
}
}
},
secret_agree: {
required: true,
transform: (v)=>v=='on',
type: 'enum',
enum: [true],
message: '동의해야만 가입하실 수 있습니다.',
},
secret_agree2: {
required: true,
transform: (v)=>v=='on',
type: 'enum',
enum: [true],
message: '동의해야만 가입하실 수 있습니다.',
},
third_use: {
required: true,
transform: (v)=>v=='on',
type: 'boolean'
}
}
const validator = new JQValidateForm($('#main_form'), descriptor);
validator.installChangeHandler();
$("#main_form").on('submit', async function (e) {
e.preventDefault();
const values = await validator.validate();
if (values === undefined) {
return;
}
const raw_password = values.password;
const salt = unwrap_any<string>($('#global_salt').val());
console.log(salt + raw_password + salt);
const hash_pw = sha512(salt + raw_password + salt);
let result: InvalidResponse;
try {
const response = await axios({
url: 'j_join_process.php',
responseType: 'json',
method: 'post',
data: convertFormData({
secret_agree: values.secret_agree,
secret_agree2: values.secret_agree2,
third_use: values.third_use,
username: values.username,
password: hash_pw,
nickname: values.nickname,
})
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
window.location.href = "../";
return;
}
alert('회원 등록되었습니다.\n첫 로그인 과정에서 인증 코드를 입력하는 것으로 계정이 활성화됩니다.');
window.location.href = "../";
});
$('#terms1').html((await terms1P).data);
$('#terms2').html((await terms2P).data);
});
+299
View File
@@ -0,0 +1,299 @@
import $ from 'jquery';
import 'popper.js';
import Popper from 'popper.js';
(window as unknown as {Popper:unknown}).Popper = Popper;
import 'bootstrap';
import { JQValidateForm, NamedRules } from '../util/jqValidateForm';
import axios from 'axios';
import { convertFormData } from '../util/convertFormData';
import { setAxiosXMLHttpRequest } from '../util/setAxiosXMLHttpRequest';
import { unwrap_any } from '../util/unwrap_any';
import { sha512 } from 'js-sha512';
import { unwrap } from '../util/unwrap';
import { InvalidResponse } from '../defs';
type LoginResponse = {
result: true,
} | {
result: false,
reqOTP: boolean,
reason: string,
}
type OTPResponse = {
result: true,
validUntil: string,
} | {
result: false,
reset: boolean,
reason: string,
}
declare global {
interface Window {
getOAuthToken: (mode: string, scope_list: string[]) => void;
postOAuthResult: (mode: string) => void;
}
}
declare const kakao_oauth_client_id: string;
declare const kakao_oauth_redirect_uri: string;
let oauthMode: string | null = null;
function getOAuthToken(mode: string, scope_list?: string[] | string) {
if (mode === undefined) {
mode = 'login';
}
oauthMode = mode;
let url = `https://kauth.kakao.com/oauth/authorize?client_id=${kakao_oauth_client_id}&redirect_uri=${kakao_oauth_redirect_uri}&response_type=code`;
if (Array.isArray(scope_list)) {
url += `&scope=${scope_list.join(',')}`;
} else if (typeof scope_list === 'string') {
url += `&scope=${scope_list}`;
}
window.open(url, "KakaoAccountLogin", "width=600,height=450,resizable=yes,scrollbars=yes");
}
window.getOAuthToken = getOAuthToken;
async function sendTempPasswordToKakaoTalk(): Promise<void> {
let result: InvalidResponse;
try {
const response = await axios({
url: 'oauth_kakao/j_login_oauth.php',
responseType: 'json',
method: 'post',
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다_login: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
return;
}
try {
const response = await axios({
url: 'oauth_kakao/j_change_pw.php',
responseType: 'json',
method: 'post',
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다_pw: ${e}`);
return;
}
alert('임시 비밀번호가 카카오톡으로 전송되었습니다.');
}
async function doLoginUsingOAuth() {
let result: LoginResponse;
try {
const response = await axios({
url: 'oauth_kakao/j_login_oauth.php',
responseType: 'json',
method: 'post',
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다_login: ${e}`);
return;
}
if (result.result) {
window.location.href = "./";
return;
}
if (!result.reqOTP) {
alert(result.reason);
return;
}
const $modal = $('#modalOTP').modal();
$modal.on('shown.bs.modal', function () {
$('#otp_code').trigger('focus');
});
}
function postOAuthResult(mode: string) {
if (mode == 'join') {
window.location.href = 'oauth_kakao/join.php';
return;
}
if (mode == 'req_email') {
alert('이메일 정보 공유를 허가해 주셔야 합니다.');
return;
}
if (mode == 'login') {
console.log('로그인모드');
if (oauthMode == 'change_pw') {
void sendTempPasswordToKakaoTalk();
} else {
void doLoginUsingOAuth();
}
return;
}
alert('예외 발생!');
}
window.postOAuthResult = postOAuthResult;
$(function ($) {
setAxiosXMLHttpRequest();
type LoginFormType = {
username: string,
password: string,
};
const descriptor: NamedRules<LoginFormType> = {
username: {
required: true,
type: 'string',
message: '유저명을 입력해주세요',
},
password: {
required: true,
type: 'string',
message: '비밀번호를 입력해주세요'
}
};
const validator = new JQValidateForm($('#main_form'), descriptor);
validator.installChangeHandler();
$("#main_form").on('submit', async function (e) {
e.preventDefault();
const values = await validator.validate();
if (values === undefined) {
return;
}
const raw_password = values.password;
const salt = unwrap_any<string>($('#global_salt').val());
const hash_pw = sha512(salt + raw_password + salt);
let result: LoginResponse;
try {
const response = await axios({
url: 'j_login.php',
responseType: 'json',
method: 'post',
data: convertFormData({
username: values.username,
password: hash_pw,
})
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
if (result.result) {
window.location.href = "./";
return;
}
if (!result.reqOTP) {
alert(result.reason);
return;
}
const $modal = $('#modalOTP').modal();
$modal.on('shown.bs.modal', function () {
$('#otp_code').trigger('focus');
});
});
$('#otp_form').on('submit', async function (e) {
e.preventDefault();
let result: OTPResponse;
try {
const response = await axios({
url: 'oauth_kakao/j_check_OTP.php',
responseType: 'json',
method: 'post',
data: convertFormData({
otp: unwrap_any<string>($('#otp_code').val()),
})
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
if (result.result) {
alert(`로그인되었습니다. ${result.validUntil}까지 유효합니다.`);
window.location.href = "./";
return;
}
alert(result.reason);
if (result.reset) {
$('#modalOTP').modal('hide')
return;
}
});
$('#oauth_change_pw').on('click', function (e) {
e.preventDefault();
getOAuthToken('change_pw', 'talk_message');
});
$('#btn_kakao_login').on('click', function(e){
e.preventDefault();
getOAuthToken('login', ['account_email','talk_message']);
})
//TODO: 모바일에서 크기 비례 지도 다시 띄우기?
/*
if (document.body.clientWidth < 700) {
const targetWidth = document.body.clientWidth * 0.9;
const scale = targetWidth / 700;
const $map = $('#running_map');
$map.find('.col').css('max-width', targetWidth);
$map.find('.card').css('width', targetWidth);
$map.find('.map-container').css({
'transform-origin': 'top left',
'transform': `scale(${scale}, ${scale})`,
'height': 500 * scale,
});
$map.find('.map_body').data('scale', scale);
}*/
});
window.fitIframe = function () {
const iframe = unwrap(document.querySelector<HTMLIFrameElement>('#running_map'));//TODO: 근황 여러개 볼 수 있도록?
const scrollHeight = unwrap(iframe.contentWindow).document.body.scrollHeight;
iframe.style.height = `${scrollHeight}px`;
}
+384
View File
@@ -0,0 +1,384 @@
import { setAxiosXMLHttpRequest } from "../util/setAxiosXMLHttpRequest";
import $ from 'jquery';
import Popper from 'popper.js';
exportWindow(Popper, 'Popper');//XXX: 왜 popper를 이렇게 불러야 하는가?
import 'bootstrap';
import axios from 'axios';
import { subDays } from 'date-fns';
import { getDateTimeNow } from "../util/getDateTimeNow";
import { sha512 } from "js-sha512";
import { convertFormData } from "../util/convertFormData";
import { InvalidResponse } from "../defs";
import { unwrap } from "../util/unwrap";
import { parseTime } from "../util/parseTime";
import { formatTime } from "../util/formatTime";
import { exportWindow } from "../util/exportWindow";
type ResultUserInfo = {
result: true,
id: number,
name: string,
grade: string,
picture: string,
global_salt: string,
join_date: string,
third_use: boolean,
acl: string,
oauth_type: 'NONE' | 'KAKAO',
token_valid_until?: string
}
function fillUserInfo(result: ResultUserInfo | InvalidResponse) {
if (!result.result) {
alert(result.reason);
location.href = 'entrance.php';
return;
}
$('#slot_id').html(result.id.toString());
$('#slot_nickname').html(result.name);
$('#slot_grade').html(result.grade);
$('#slot_acl').html(result.acl);
$('#slot_icon').attr('src', result.picture);
$('#global_salt').val(result.global_salt);
$('#slot_join_date').html(result.join_date);
$('#slot_third_use').html(result.third_use ? '○' : '×');
if (result.third_use) {
$('#third_use_disallow').show();
}
$('#slot_oauth_type').text(result.oauth_type);
if (result.oauth_type != 'NONE') {
$('#slot_token_valid_until').text(unwrap(result.token_valid_until));
}
else {
$('#slot_token_valid_until').parent().html('');
}
}
function changeIconPreview(this: HTMLFormElement) {
const $preview = $(this) as JQuery<HTMLFormElement>;
console.log($preview);
const filename = $preview[0].files[0].name;
const reader = new FileReader();
reader.onload = function (e) {
$('#slot_new_icon').attr('src', unwrap(unwrap(e.target).result).toString()).css('visibility', 'visible');
}
reader.readAsDataURL($preview[0].files[0]);
$('#image_upload_filename').val(filename);
}
type IconResponse = {
result: true,
servers: [string, string][]
};
async function deleteIcon() {
let result: InvalidResponse | IconResponse;
try {
const response = await axios({
url: 'j_icon_delete.php',
responseType: 'json',
method: 'post'
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`아이콘 삭제를 실패했습니다: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
location.reload();
return;
}
showAdjustServerModal(result.servers);
}
async function disallowThirdUse() {
try {
await axios({
url: 'j_disallow_third_use.php',
method: 'post',
responseType: 'json'
});
alert('철회했습니다.');
}
catch (e) {
alert('알 수 없는 이유로 철회를 실패했습니다.');
}
location.reload();
}
function showAdjustServerModal(serverList: [string, string][]) {
const $form = $('#chooseServerForm');
$form.empty();
for (const [serverKey, serverKorName] of serverList) {
const $item = $(`<div style="display:inline-block;margin-right:7px;" class="custom-control custom-checkbox">\
<input type="checkbox" checked class="custom-control-input" name="${serverKey}" id="switch_${serverKey}">\
<label class="custom-control-label" for="switch_${serverKey}">${serverKorName}</label>\
</div>`);
$form.append($item);
}
const $modal = $('#chooseServer');
$modal.modal({
backdrop: 'static'
});
$modal.on('hidden.bs.modal', function () {
location.reload();
return;
});
$("#modal-apply").off("click").on("click", async function () {
const events: Promise<unknown>[] = [];
$form.find('input:checked').each(function () {
const $input = $(this);
const server = $input.attr('name');
console.log(server);
events.push(axios({
url: `../${server}/j_adjust_icon.php`,
method: 'post',
responseType: 'json',
}));
})
for (const p of events) {
try {
await p;
}
catch (e) {
//서버 폐쇄등으로 접근하지 못할 수도 있음.
console.error(e, p);
}
}
alert('적용되었습니다.');
location.reload();
});
}
async function changeIcon(this: HTMLFormElement) {
const $icon = $('#image_upload') as JQuery<HTMLFormElement>;
if ($icon[0].files.length == 0) {
alert('파일을 선택해주세요');
return false;
}
let result: InvalidResponse | IconResponse;
try {
const response = await axios({
url: 'j_icon_change.php',
method: 'post',
responseType: 'json',
data: new FormData(this)
});
result = response.data;
}
catch (e) {
alert('알 수 없는 이유로 아이콘 업로드를 실패했습니다.');
location.reload();
return;
}
if (!result.result) {
alert(result.reason);
location.reload();
return;
}
showAdjustServerModal(result.servers);
}
async function changePassword() {
const old_pw = $('#current_pw').val() as string;
const new_pw = $('#new_pw').val() as string;
const new_pw_confirm = $('#new_pw_confirm').val() as string;
if (!old_pw) {
alert('이전 비밀번호를 입력해야 합니다.');
return;
}
if (new_pw.length < 6) {
alert('비밀번호 길이는 6글자 이상이어야 합니다.');
return;
}
if (new_pw != new_pw_confirm) {
alert('입력 값이 일치하지 않습니다.');
return;
}
const global_salt = $('#global_salt').val();
const old_password = sha512(global_salt + old_pw + global_salt);
const new_password = sha512(global_salt + new_pw + global_salt);
let result: InvalidResponse;
try {
const response = await axios({
url: 'j_change_password.php',
method: 'post',
responseType: 'json',
data: convertFormData({
old_pw: old_password,
new_pw: new_password
})
});
result = response.data;
}
catch (e) {
console.error(e);
alert(`알 수 없는 이유로 비밀번호를 바꾸지 못했습니다.: ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
return;
}
alert('비밀번호를 바꾸었습니다');
location.reload();
}
async function deleteMe() {
const pw = $('#delete_pw').val() as string;
if (!pw) {
alert('비밀번호를 입력해야 합니다.');
return;
}
const global_salt = $('#global_salt').val();
const password = sha512(global_salt + pw + global_salt);
let data: InvalidResponse;
try {
const response = await axios({
url: 'j_delete_me.php',
responseType: 'json',
method: 'post',
data: convertFormData({
pw: password
})
});
data = response.data;
}
catch (e) {
console.error(e);
alert(`회원 탈퇴에 실패했습니다.: ${e}`);
return;
}
if (!data.result) {
alert(data.reason);
return;
}
alert('탈퇴 처리되었습니다.');
location.href = '../';
}
async function extendAuth() {
const validUntil = $('#slot_token_valid_until').html();
const availableAt = formatTime(subDays(parseTime(validUntil), 5));
const now = getDateTimeNow();
if (now < availableAt) {
alert(`${availableAt}부터 초기화할 수 있습니다.`);
return false;
}
if (!confirm('로그아웃됩니다. 진행할까요?')) {
return;
}
let result: InvalidResponse;
try {
const response = await axios({
url: '../oauth_kakao/j_reset_token.php',
method: 'post',
responseType: 'json'
})
result = response.data;
} catch (e) {
alert(`알 수 없는 이유로 로그인 토큰 연장에 실패했습니다. : ${e}`);
return;
}
if (!result.result) {
alert(result.reason);
return;
}
alert('초기화했습니다. 다시 로그인해 주십시오.');
location.href = '../';
}
$(function () {
setAxiosXMLHttpRequest();
$('#slot_icon, #slot_new_icon').attr('src', window.pathConfig.sharedIcon + '/default.jpg');
axios({
url: 'j_get_user_info.php',
method: 'post',
responseType: 'json'
}).then(result => {
fillUserInfo(result.data);
}, () => {
alert('알 수 없는 이유로, 회원 정보를 불러오지 못했습니다.');
location.href = 'entrance.php';
})
$('#image_upload').on('change', changeIconPreview);
$('#btn_remove_icon').on('click', function () {
if (confirm('아이콘을 제거할까요?')) {
void deleteIcon();
}
return false;
});
$('#third_use_disallow').on('click', function () {
if (confirm('개인정보 3자 제공 동의를 철회할까요?')) {
void disallowThirdUse();
}
});
$('#change_pw_form').on('submit', function (e) {
e.preventDefault();
void changePassword();
});
$('#change_icon_form').on('submit', function (e) {
e.preventDefault();
void changeIcon.apply(this as HTMLFormElement);
});
$('#delete_me_form').on('submit', function (e) {
e.preventDefault();
if (confirm('한 달 동안 재 가입할 수 없습니다. 정말로 탈퇴할까요?')) {
void deleteMe();
}
});
$('#expand_login_token').on('click', extendAuth);
})