security: 보안 강제 시스템 구축 + 하드코딩 비밀번호 제거
보안 감사 결과 CRITICAL 2건, HIGH 5건 발견 → 수정 완료 + 자동화 구축. [보안 수정] - issue-view.js: 하드코딩 비밀번호 → crypto.getRandomValues() 랜덤 생성 - pushSubscriptionController.js: ntfy 비밀번호 → process.env.NTFY_SUB_PASSWORD - DEPLOY-GUIDE.md/PROGRESS.md/migration SQL: 평문 비밀번호 → placeholder - docker-compose.yml/.env.example: NTFY_SUB_PASSWORD 환경변수 추가 [보안 강제 시스템 - 신규] - scripts/security-scan.sh: 8개 규칙 (CRITICAL 2, HIGH 4, MEDIUM 2) 3모드(staged/all/diff), severity, .securityignore, MEDIUM 임계값 - .githooks/pre-commit: 로컬 빠른 피드백 - .githooks/pre-receive-server.sh: Gitea 서버 최종 차단 bypass 거버넌스([SECURITY-BYPASS: 사유] + 사용자 제한 + 로그) - SECURITY-CHECKLIST.md: 10개 카테고리 자동/수동 구분 - docs/SECURITY-GUIDE.md: 운영자 가이드 (워크플로우, bypass, FAQ) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
322
system1-factory/web/public/js/tbm/state.js
Normal file
322
system1-factory/web/public/js/tbm/state.js
Normal file
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* TBM - State Manager
|
||||
* TBM 페이지의 전역 상태 관리 (BaseState 상속)
|
||||
*/
|
||||
|
||||
class TbmState extends BaseState {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// 세션 데이터
|
||||
this.allSessions = [];
|
||||
this.todaySessions = [];
|
||||
this.dateGroupedSessions = {};
|
||||
this.allLoadedSessions = [];
|
||||
this.loadedDaysCount = 7;
|
||||
|
||||
// 마스터 데이터
|
||||
this.allWorkers = [];
|
||||
this.allProjects = [];
|
||||
this.allWorkTypes = [];
|
||||
this.allTasks = [];
|
||||
this.allSafetyChecks = [];
|
||||
this.allWorkplaces = [];
|
||||
this.allWorkplaceCategories = [];
|
||||
|
||||
// 현재 상태
|
||||
this.currentUser = null;
|
||||
this.currentSessionId = null;
|
||||
this.currentTab = 'tbm-input';
|
||||
|
||||
// 작업자 관련
|
||||
this.selectedWorkers = new Set();
|
||||
this.workerTaskList = [];
|
||||
this.selectedWorkersInModal = new Set();
|
||||
this.currentEditingTaskLine = null;
|
||||
|
||||
// 작업장 선택 관련
|
||||
this.selectedCategory = null;
|
||||
this.selectedWorkplace = null;
|
||||
this.selectedCategoryName = '';
|
||||
this.selectedWorkplaceName = '';
|
||||
|
||||
// 일괄 설정 관련
|
||||
this.isBulkMode = false;
|
||||
this.bulkSelectedWorkers = new Set();
|
||||
|
||||
// 지도 관련
|
||||
this.mapCanvas = null;
|
||||
this.mapCtx = null;
|
||||
this.mapImage = null;
|
||||
this.mapRegions = [];
|
||||
|
||||
console.log('[TbmState] 초기화 완료');
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin 여부 확인
|
||||
*/
|
||||
isAdminUser() {
|
||||
const user = this.getUser();
|
||||
if (!user) return false;
|
||||
const role = (user.role || '').toLowerCase();
|
||||
return role === 'admin' || role === 'system admin' || role === 'system';
|
||||
}
|
||||
|
||||
/**
|
||||
* 탭 변경
|
||||
*/
|
||||
setCurrentTab(tab) {
|
||||
const prevTab = this.currentTab;
|
||||
this.currentTab = tab;
|
||||
this.notifyListeners('currentTab', tab, prevTab);
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업자 목록에 추가
|
||||
*/
|
||||
addWorkerToList(worker) {
|
||||
this.workerTaskList.push({
|
||||
user_id: worker.user_id,
|
||||
worker_name: worker.worker_name,
|
||||
job_type: worker.job_type,
|
||||
tasks: [this.createEmptyTaskLine()]
|
||||
});
|
||||
this.notifyListeners('workerTaskList', this.workerTaskList, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 빈 작업 라인 생성
|
||||
*/
|
||||
createEmptyTaskLine() {
|
||||
return {
|
||||
task_line_id: window.CommonUtils.generateUUID(),
|
||||
project_id: null,
|
||||
work_type_id: null,
|
||||
task_id: null,
|
||||
workplace_category_id: null,
|
||||
workplace_id: null,
|
||||
workplace_category_name: '',
|
||||
workplace_name: '',
|
||||
work_detail: null,
|
||||
is_present: true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업자에 작업 라인 추가
|
||||
*/
|
||||
addTaskLineToWorker(workerIndex) {
|
||||
if (this.workerTaskList[workerIndex]) {
|
||||
this.workerTaskList[workerIndex].tasks.push(this.createEmptyTaskLine());
|
||||
this.notifyListeners('workerTaskList', this.workerTaskList, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업 라인 제거
|
||||
*/
|
||||
removeTaskLine(workerIndex, taskIndex) {
|
||||
if (this.workerTaskList[workerIndex]?.tasks) {
|
||||
this.workerTaskList[workerIndex].tasks.splice(taskIndex, 1);
|
||||
this.notifyListeners('workerTaskList', this.workerTaskList, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업자 제거
|
||||
*/
|
||||
removeWorkerFromList(workerIndex) {
|
||||
const removed = this.workerTaskList.splice(workerIndex, 1);
|
||||
this.notifyListeners('workerTaskList', this.workerTaskList, null);
|
||||
return removed[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업장 선택 초기화
|
||||
*/
|
||||
resetWorkplaceSelection() {
|
||||
this.selectedCategory = null;
|
||||
this.selectedWorkplace = null;
|
||||
this.selectedCategoryName = '';
|
||||
this.selectedWorkplaceName = '';
|
||||
this.mapCanvas = null;
|
||||
this.mapCtx = null;
|
||||
this.mapImage = null;
|
||||
this.mapRegions = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 일괄 설정 초기화
|
||||
*/
|
||||
resetBulkSettings() {
|
||||
this.isBulkMode = false;
|
||||
this.bulkSelectedWorkers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 날짜별 세션 그룹화
|
||||
*/
|
||||
groupSessionsByDate(sessions) {
|
||||
this.dateGroupedSessions = {};
|
||||
this.allLoadedSessions = [];
|
||||
|
||||
sessions.forEach(session => {
|
||||
const date = window.CommonUtils.formatDate(session.session_date);
|
||||
if (!this.dateGroupedSessions[date]) {
|
||||
this.dateGroupedSessions[date] = [];
|
||||
}
|
||||
this.dateGroupedSessions[date].push(session);
|
||||
this.allLoadedSessions.push(session);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 초기화
|
||||
*/
|
||||
reset() {
|
||||
this.workerTaskList = [];
|
||||
this.selectedWorkers.clear();
|
||||
this.selectedWorkersInModal.clear();
|
||||
this.currentEditingTaskLine = null;
|
||||
this.resetWorkplaceSelection();
|
||||
this.resetBulkSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* 디버그 출력
|
||||
*/
|
||||
debug() {
|
||||
console.log('[TbmState] 현재 상태:', {
|
||||
allSessions: this.allSessions.length,
|
||||
todaySessions: this.todaySessions.length,
|
||||
allWorkers: this.allWorkers.length,
|
||||
allProjects: this.allProjects.length,
|
||||
workerTaskList: this.workerTaskList.length,
|
||||
currentTab: this.currentTab
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 전역 인스턴스 생성
|
||||
window.TbmState = new TbmState();
|
||||
|
||||
// 하위 호환성을 위한 전역 변수 프록시
|
||||
const tbmStateProxy = window.TbmState;
|
||||
|
||||
Object.defineProperties(window, {
|
||||
allSessions: {
|
||||
get: () => tbmStateProxy.allSessions,
|
||||
set: (v) => { tbmStateProxy.allSessions = v; }
|
||||
},
|
||||
todaySessions: {
|
||||
get: () => tbmStateProxy.todaySessions,
|
||||
set: (v) => { tbmStateProxy.todaySessions = v; }
|
||||
},
|
||||
allWorkers: {
|
||||
get: () => tbmStateProxy.allWorkers,
|
||||
set: (v) => { tbmStateProxy.allWorkers = v; }
|
||||
},
|
||||
allProjects: {
|
||||
get: () => tbmStateProxy.allProjects,
|
||||
set: (v) => { tbmStateProxy.allProjects = v; }
|
||||
},
|
||||
allWorkTypes: {
|
||||
get: () => tbmStateProxy.allWorkTypes,
|
||||
set: (v) => { tbmStateProxy.allWorkTypes = v; }
|
||||
},
|
||||
allTasks: {
|
||||
get: () => tbmStateProxy.allTasks,
|
||||
set: (v) => { tbmStateProxy.allTasks = v; }
|
||||
},
|
||||
allSafetyChecks: {
|
||||
get: () => tbmStateProxy.allSafetyChecks,
|
||||
set: (v) => { tbmStateProxy.allSafetyChecks = v; }
|
||||
},
|
||||
allWorkplaces: {
|
||||
get: () => tbmStateProxy.allWorkplaces,
|
||||
set: (v) => { tbmStateProxy.allWorkplaces = v; }
|
||||
},
|
||||
allWorkplaceCategories: {
|
||||
get: () => tbmStateProxy.allWorkplaceCategories,
|
||||
set: (v) => { tbmStateProxy.allWorkplaceCategories = v; }
|
||||
},
|
||||
currentUser: {
|
||||
get: () => tbmStateProxy.currentUser,
|
||||
set: (v) => { tbmStateProxy.currentUser = v; }
|
||||
},
|
||||
currentSessionId: {
|
||||
get: () => tbmStateProxy.currentSessionId,
|
||||
set: (v) => { tbmStateProxy.currentSessionId = v; }
|
||||
},
|
||||
selectedWorkers: {
|
||||
get: () => tbmStateProxy.selectedWorkers,
|
||||
set: (v) => { tbmStateProxy.selectedWorkers = v; }
|
||||
},
|
||||
workerTaskList: {
|
||||
get: () => tbmStateProxy.workerTaskList,
|
||||
set: (v) => { tbmStateProxy.workerTaskList = v; }
|
||||
},
|
||||
selectedWorkersInModal: {
|
||||
get: () => tbmStateProxy.selectedWorkersInModal,
|
||||
set: (v) => { tbmStateProxy.selectedWorkersInModal = v; }
|
||||
},
|
||||
currentEditingTaskLine: {
|
||||
get: () => tbmStateProxy.currentEditingTaskLine,
|
||||
set: (v) => { tbmStateProxy.currentEditingTaskLine = v; }
|
||||
},
|
||||
selectedCategory: {
|
||||
get: () => tbmStateProxy.selectedCategory,
|
||||
set: (v) => { tbmStateProxy.selectedCategory = v; }
|
||||
},
|
||||
selectedWorkplace: {
|
||||
get: () => tbmStateProxy.selectedWorkplace,
|
||||
set: (v) => { tbmStateProxy.selectedWorkplace = v; }
|
||||
},
|
||||
selectedCategoryName: {
|
||||
get: () => tbmStateProxy.selectedCategoryName,
|
||||
set: (v) => { tbmStateProxy.selectedCategoryName = v; }
|
||||
},
|
||||
selectedWorkplaceName: {
|
||||
get: () => tbmStateProxy.selectedWorkplaceName,
|
||||
set: (v) => { tbmStateProxy.selectedWorkplaceName = v; }
|
||||
},
|
||||
isBulkMode: {
|
||||
get: () => tbmStateProxy.isBulkMode,
|
||||
set: (v) => { tbmStateProxy.isBulkMode = v; }
|
||||
},
|
||||
bulkSelectedWorkers: {
|
||||
get: () => tbmStateProxy.bulkSelectedWorkers,
|
||||
set: (v) => { tbmStateProxy.bulkSelectedWorkers = v; }
|
||||
},
|
||||
dateGroupedSessions: {
|
||||
get: () => tbmStateProxy.dateGroupedSessions,
|
||||
set: (v) => { tbmStateProxy.dateGroupedSessions = v; }
|
||||
},
|
||||
allLoadedSessions: {
|
||||
get: () => tbmStateProxy.allLoadedSessions,
|
||||
set: (v) => { tbmStateProxy.allLoadedSessions = v; }
|
||||
},
|
||||
loadedDaysCount: {
|
||||
get: () => tbmStateProxy.loadedDaysCount,
|
||||
set: (v) => { tbmStateProxy.loadedDaysCount = v; }
|
||||
},
|
||||
mapRegions: {
|
||||
get: () => tbmStateProxy.mapRegions,
|
||||
set: (v) => { tbmStateProxy.mapRegions = v; }
|
||||
},
|
||||
mapCanvas: {
|
||||
get: () => tbmStateProxy.mapCanvas,
|
||||
set: (v) => { tbmStateProxy.mapCanvas = v; }
|
||||
},
|
||||
mapCtx: {
|
||||
get: () => tbmStateProxy.mapCtx,
|
||||
set: (v) => { tbmStateProxy.mapCtx = v; }
|
||||
},
|
||||
mapImage: {
|
||||
get: () => tbmStateProxy.mapImage,
|
||||
set: (v) => { tbmStateProxy.mapImage = v; }
|
||||
}
|
||||
});
|
||||
|
||||
console.log('[Module] tbm/state.js 로드 완료');
|
||||
Reference in New Issue
Block a user