diff --git a/i_entrance/admin_userlist.php b/i_entrance/admin_userlist.php
index d9f7537e..964207cb 100644
--- a/i_entrance/admin_userlist.php
+++ b/i_entrance/admin_userlist.php
@@ -19,7 +19,6 @@ require(__DIR__.'/../vendor/autoload.php');
=WebUtil::printJS('../d_shared/common_path.js') ?>
=WebUtil::printJS('../js/vendors.js') ?>
=WebUtil::printJS('../js/common.js') ?>
- =WebUtil::printJS('../js/func.js')?>
=WebUtil::printJS('../js/admin_member.js')?>
diff --git a/i_entrance/entrance.php b/i_entrance/entrance.php
index afb27dd2..5bcb2244 100644
--- a/i_entrance/entrance.php
+++ b/i_entrance/entrance.php
@@ -33,8 +33,6 @@ $acl = $session->acl;
=WebUtil::printJS('../d_shared/common_path.js') ?>
=WebUtil::printJS('../js/vendors.js') ?>
=WebUtil::printJS('../js/common.js') ?>
- =WebUtil::printJS('../e_lib/moment.min.js')?>
- =WebUtil::printJS('../js/func.js')?>
=WebUtil::printJS('../js/entrance.js')?>
= 5 || $acl): ?>
diff --git a/i_entrance/user_info.php b/i_entrance/user_info.php
index 7cf952f6..74bd35b2 100644
--- a/i_entrance/user_info.php
+++ b/i_entrance/user_info.php
@@ -20,10 +20,6 @@ require(__DIR__.'/../vendor/autoload.php');
=WebUtil::printJS('../d_shared/common_path.js') ?>
=WebUtil::printJS('../js/vendors.js') ?>
- =WebUtil::printJS('../js/common.js') ?>
- =WebUtil::printJS('../e_lib/sha512.min.js')?>
- =WebUtil::printJS('../e_lib/moment.min.js')?>
- =WebUtil::printJS('../js/func.js')?>
=WebUtil::printJS('../js/user_info.js')?>
diff --git a/js/func.js b/js/func.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/package.json b/package.json
index ad7ee2c2..cfdcf161 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"bootstrap": "^4.6.0",
"core-js": "^3.16.1",
"jquery": "^3.6.0",
+ "js-sha512": "^0.8.0",
"lodash": "^4.17.21",
"luxon": "^2.0.2",
"vue": "^3.2.2"
diff --git a/ts/user_info.ts b/ts/user_info.ts
new file mode 100644
index 00000000..bb718633
--- /dev/null
+++ b/ts/user_info.ts
@@ -0,0 +1,379 @@
+import { setAxiosXMLHttpRequest } from "../hwe/ts/util/setAxiosXMLHttpRequest";
+import $ from 'jquery';
+import 'bootstrap';
+import axios from 'axios';
+import { DateTime } from "luxon";
+import { DATE_TIME_FORMAT, getDateTimeNow } from "../hwe/ts/util/getDateTimeNow";
+import { sha512 } from "js-sha512";
+import { convertFormData } from "../hwe/ts/util/convertFormData";
+import { InvalidResponse } from "../hwe/ts/defs";
+import { unwrap } from "./util";
+
+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;
+ 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 = $(`\
+ \
+ \
+
`);
+ $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[] = [];
+ $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;
+
+ 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 = DateTime.fromFormat(validUntil, DATE_TIME_FORMAT).minus({ days: 5 }).toFormat(DATE_TIME_FORMAT);
+ 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);
+})
diff --git a/webpack.config.js b/webpack.config.js
index c300bd31..7db0ee2a 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -87,7 +87,7 @@ module.exports = [
entry: {
'common': './ts/common_deprecated.ts',
'entrance': './ts/entrance.ts',
- //test: './ts/test.ts',
+ 'user_info': './ts/user_info.ts',
},
output: {
filename: '[name].js',