feat(ops-auth): 이메일 로그인 세션 만료를 로그인일+15일 달력 끝까지로 변경
- OPS_SESSION_TTL_DAYS 환경변수 추가(기본 15) - .env.example 주석 갱신 Made-with: Cursor
This commit is contained in:
@@ -18,8 +18,9 @@ ADMIN_TOKEN=xavis-admin
|
|||||||
# SMTP_FROM=noreply@xavis.co.kr
|
# SMTP_FROM=noreply@xavis.co.kr
|
||||||
# 선택: 587에서 STARTTLS 강제(기본 on). 특수 서버만 0
|
# 선택: 587에서 STARTTLS 강제(기본 on). 특수 서버만 0
|
||||||
# SMTP_REQUIRE_TLS=1
|
# SMTP_REQUIRE_TLS=1
|
||||||
# 이메일 로그인 세션 만료: 해당 일자 23:59:59까지(달력은 OPS_SESSION_TZ 기준, 기본 Asia/Seoul)
|
# 이메일 로그인 세션: 로그인한 달력일(OPS_SESSION_TZ) + OPS_SESSION_TTL_DAYS일의 23:59:59까지(기본 15일)
|
||||||
# OPS_SESSION_TZ=Asia/Seoul
|
# OPS_SESSION_TZ=Asia/Seoul
|
||||||
|
# OPS_SESSION_TTL_DAYS=15
|
||||||
PAGE_SIZE=9
|
PAGE_SIZE=9
|
||||||
# 학습센터 동영상 파일 업로드 최대 크기(MB, 기본 500). 리버스 프록시(Nginx 등)의 client_max_body_size도 같이 늘려야 합니다.
|
# 학습센터 동영상 파일 업로드 최대 크기(MB, 기본 500). 리버스 프록시(Nginx 등)의 client_max_body_size도 같이 늘려야 합니다.
|
||||||
LECTURE_VIDEO_MAX_MB=500
|
LECTURE_VIDEO_MAX_MB=500
|
||||||
|
|||||||
65
ops-auth.js
65
ops-auth.js
@@ -33,30 +33,65 @@ function calendarDateKeyInTz(tsMs, tz) {
|
|||||||
return `${y}-${mo}-${da}`;
|
return `${y}-${mo}-${da}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 그레고리력 YYYY-MM-DD 문자열에 일수 더하기(타임존 무관, 달력 날짜만) */
|
||||||
* 로그인 세션 만료: OPS_SESSION_TZ(기본 Asia/Seoul)에서 해당 일자의 마지막 순간(23:59:59.999에 해당하는 epoch ms)
|
function addCalendarDaysToKey(key, days) {
|
||||||
*/
|
const n = Math.floor(Number(days) || 0);
|
||||||
function getOpsSessionExpiresAtMs(nowMs = Date.now()) {
|
const [y, m, d] = key.split("-").map(Number);
|
||||||
const tz = (process.env.OPS_SESSION_TZ || "Asia/Seoul").trim() || "Asia/Seoul";
|
const base = new Date(Date.UTC(y, m - 1, d));
|
||||||
const cur = calendarDateKeyInTz(nowMs, tz);
|
base.setUTCDate(base.getUTCDate() + n);
|
||||||
let lo = nowMs;
|
const yy = base.getUTCFullYear();
|
||||||
let hi = nowMs + 48 * 60 * 60 * 1000;
|
const mm = String(base.getUTCMonth() + 1).padStart(2, "0");
|
||||||
let guard = 0;
|
const dd = String(base.getUTCDate()).padStart(2, "0");
|
||||||
while (calendarDateKeyInTz(hi, tz) === cur && guard < 400) {
|
return `${yy}-${mm}-${dd}`;
|
||||||
hi += 24 * 60 * 60 * 1000;
|
}
|
||||||
guard++;
|
|
||||||
|
/** dateKey 달력 날짜에 속하는 임의 시각(ms) 찾기 */
|
||||||
|
function findMsOnCalendarDay(dateKey, tz) {
|
||||||
|
const [y, m, d] = dateKey.split("-").map(Number);
|
||||||
|
const start = Date.UTC(y, m - 1, d, 0, 0, 0, 0);
|
||||||
|
for (let k = -24 * 4; k < 96 * 4; k += 1) {
|
||||||
|
const ms = start + k * 15 * 60 * 1000;
|
||||||
|
if (calendarDateKeyInTz(ms, tz) === dateKey) return ms;
|
||||||
}
|
}
|
||||||
if (calendarDateKeyInTz(hi, tz) === cur) {
|
throw new Error(`findMsOnCalendarDay: ${dateKey} (${tz})`);
|
||||||
hi = nowMs + 400 * 24 * 60 * 60 * 1000;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OPS_SESSION_TZ에서 dateKey 해당 날의 마지막 순간(그날 23:59:59.999에 해당하는 epoch ms)
|
||||||
|
*/
|
||||||
|
function getLastMsOfCalendarDayInTz(dateKey, tz) {
|
||||||
|
const anchor = findMsOnCalendarDay(dateKey, tz);
|
||||||
|
let lo = anchor;
|
||||||
|
let hi = anchor + 48 * 60 * 60 * 1000;
|
||||||
|
let guard = 0;
|
||||||
|
while (calendarDateKeyInTz(hi, tz) === dateKey && guard < 400) {
|
||||||
|
hi += 24 * 60 * 60 * 1000;
|
||||||
|
guard += 1;
|
||||||
|
}
|
||||||
|
if (calendarDateKeyInTz(hi, tz) === dateKey) {
|
||||||
|
hi = anchor + 400 * 24 * 60 * 60 * 1000;
|
||||||
}
|
}
|
||||||
while (hi - lo > 1) {
|
while (hi - lo > 1) {
|
||||||
const mid = Math.floor((lo + hi) / 2);
|
const mid = Math.floor((lo + hi) / 2);
|
||||||
if (calendarDateKeyInTz(mid, tz) === cur) lo = mid;
|
if (calendarDateKeyInTz(mid, tz) === dateKey) lo = mid;
|
||||||
else hi = mid;
|
else hi = mid;
|
||||||
}
|
}
|
||||||
return hi - 1;
|
return hi - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 로그인 세션 만료: 로그인일(OPS_SESSION_TZ 달력) + OPS_SESSION_TTL_DAYS(기본 15)일의 마지막 순간
|
||||||
|
*/
|
||||||
|
function getOpsSessionExpiresAtMs(nowMs = Date.now()) {
|
||||||
|
const tz = (process.env.OPS_SESSION_TZ || "Asia/Seoul").trim() || "Asia/Seoul";
|
||||||
|
const raw = (process.env.OPS_SESSION_TTL_DAYS || "15").trim();
|
||||||
|
const parsed = parseInt(raw, 10);
|
||||||
|
const ttlDays = Number.isFinite(parsed) && parsed >= 0 ? parsed : 15;
|
||||||
|
const loginDayKey = calendarDateKeyInTz(nowMs, tz);
|
||||||
|
const targetKey = addCalendarDaysToKey(loginDayKey, ttlDays);
|
||||||
|
return getLastMsOfCalendarDayInTz(targetKey, tz);
|
||||||
|
}
|
||||||
|
|
||||||
function isOpsProd() {
|
function isOpsProd() {
|
||||||
return isOpsProdMode();
|
return isOpsProdMode();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user