## Summary - 기상 맵 컨트롤 컴포넌트 추가 및 KHOA API 연동 개선 - KHOA API 엔드포인트 교체 및 해양예측 오버레이 Canvas 렌더링 전환 ## 변경 파일 - OceanForecastOverlay.tsx - WeatherMapOverlay.tsx - WeatherView.tsx - useOceanForecast.ts - khoaApi.ts - vite.config.ts ## Test plan - [ ] 기상정보 -> 기상 레이어 -> 해황 예보도 클릭 -> 이미지 렌더링 확인 - [ ] 기상정보 -> 기상 레이어 -> 백터 바람 클릭 -> 백터 이미지 렌더링 확인 Co-authored-by: Nan Kyung Lee <nankyunglee@Nanui-Macmini.local> Reviewed-on: #78 Co-authored-by: leedano <dnlee@gcsc.co.kr> Co-committed-by: leedano <dnlee@gcsc.co.kr>
293 lines
7.4 KiB
TypeScript
293 lines
7.4 KiB
TypeScript
import { api } from './api'
|
|
|
|
export interface AuthUser {
|
|
id: string
|
|
account: string
|
|
name: string
|
|
rank: string | null
|
|
org: { sn: number; name: string; abbr: string } | null
|
|
roles: string[]
|
|
permissions: Record<string, string[]>
|
|
}
|
|
|
|
interface LoginResponse {
|
|
success: boolean
|
|
user: AuthUser
|
|
pending?: boolean
|
|
message?: string
|
|
}
|
|
|
|
export async function loginApi(account: string, password: string): Promise<AuthUser> {
|
|
const response = await api.post<LoginResponse>('/auth/login', { account, password })
|
|
return response.data.user
|
|
}
|
|
|
|
export class PendingApprovalError extends Error {
|
|
constructor(message: string) {
|
|
super(message)
|
|
this.name = 'PendingApprovalError'
|
|
}
|
|
}
|
|
|
|
export async function googleLoginApi(credential: string): Promise<AuthUser> {
|
|
const response = await api.post<LoginResponse>('/auth/oauth/google', { credential })
|
|
if (response.data.pending) {
|
|
throw new PendingApprovalError(response.data.message || '관리자 승인 후 로그인할 수 있습니다.')
|
|
}
|
|
return response.data.user
|
|
}
|
|
|
|
export async function logoutApi(): Promise<void> {
|
|
await api.post('/auth/logout')
|
|
}
|
|
|
|
export async function fetchMe(): Promise<AuthUser> {
|
|
const response = await api.get<AuthUser>('/auth/me')
|
|
return response.data
|
|
}
|
|
|
|
// 사용자 관리 API (ADMIN 전용)
|
|
export interface UserListItem {
|
|
id: string
|
|
account: string
|
|
name: string
|
|
rank: string | null
|
|
orgSn: number | null
|
|
orgName: string | null
|
|
orgAbbr: string | null
|
|
status: string
|
|
failCount: number
|
|
lastLogin: string | null
|
|
roles: string[]
|
|
roleSns: number[]
|
|
regDtm: string
|
|
oauthProvider: string | null
|
|
email: string | null
|
|
}
|
|
|
|
export async function fetchUsers(search?: string, status?: string): Promise<UserListItem[]> {
|
|
const params = new URLSearchParams()
|
|
if (search) params.set('search', search)
|
|
if (status) params.set('status', status)
|
|
const response = await api.get<UserListItem[]>(`/users?${params}`)
|
|
return response.data
|
|
}
|
|
|
|
export async function fetchUser(id: string): Promise<UserListItem> {
|
|
const response = await api.get<UserListItem>(`/users/${id}`)
|
|
return response.data
|
|
}
|
|
|
|
export async function createUserApi(data: {
|
|
account: string
|
|
password: string
|
|
name: string
|
|
rank?: string
|
|
orgSn?: number
|
|
roleSns?: number[]
|
|
}): Promise<{ id: string }> {
|
|
const response = await api.post<{ id: string }>('/users', data)
|
|
return response.data
|
|
}
|
|
|
|
export async function updateUserApi(id: string, data: {
|
|
name?: string
|
|
rank?: string
|
|
orgSn?: number | null
|
|
status?: string
|
|
}): Promise<void> {
|
|
await api.put(`/users/${id}`, data)
|
|
}
|
|
|
|
export async function changePasswordApi(id: string, password: string): Promise<void> {
|
|
await api.put(`/users/${id}/password`, { password })
|
|
}
|
|
|
|
export async function assignRolesApi(id: string, roleSns: number[]): Promise<void> {
|
|
await api.put(`/users/${id}/roles`, { roleSns })
|
|
}
|
|
|
|
// 조직 목록 API
|
|
export interface OrgItem {
|
|
orgSn: number
|
|
orgNm: string
|
|
orgAbbrNm: string | null
|
|
orgTpCd: string
|
|
upperOrgSn: number | null
|
|
}
|
|
|
|
export async function fetchOrgs(): Promise<OrgItem[]> {
|
|
const response = await api.get<OrgItem[]>('/users/orgs')
|
|
return response.data
|
|
}
|
|
|
|
// 역할/권한 API (ADMIN 전용)
|
|
export interface RoleWithPermissions {
|
|
sn: number
|
|
code: string
|
|
name: string
|
|
description: string | null
|
|
isDefault: boolean
|
|
permissions: Array<{
|
|
sn: number
|
|
resourceCode: string
|
|
operationCode: string
|
|
granted: boolean
|
|
}>
|
|
}
|
|
|
|
export async function fetchRoles(): Promise<RoleWithPermissions[]> {
|
|
const response = await api.get<RoleWithPermissions[]>('/roles')
|
|
return response.data
|
|
}
|
|
|
|
// 권한 트리 구조 API
|
|
export interface PermTreeNode {
|
|
code: string
|
|
parentCode: string | null
|
|
name: string
|
|
description: string | null
|
|
icon: string | null
|
|
level: number
|
|
sortOrder: number
|
|
children: PermTreeNode[]
|
|
}
|
|
|
|
export async function fetchPermTree(): Promise<PermTreeNode[]> {
|
|
const response = await api.get<PermTreeNode[]>('/roles/perm-tree')
|
|
return response.data
|
|
}
|
|
|
|
export async function updatePermissionsApi(
|
|
roleSn: number,
|
|
permissions: Array<{ resourceCode: string; operationCode: string; granted: boolean }>
|
|
): Promise<void> {
|
|
await api.put(`/roles/${roleSn}/permissions`, { permissions })
|
|
}
|
|
|
|
export async function updateRoleDefaultApi(roleSn: number, isDefault: boolean): Promise<void> {
|
|
await api.put(`/roles/${roleSn}/default`, { isDefault })
|
|
}
|
|
|
|
export async function createRoleApi(data: {
|
|
code: string
|
|
name: string
|
|
description?: string
|
|
}): Promise<RoleWithPermissions> {
|
|
const response = await api.post<RoleWithPermissions>('/roles', data)
|
|
return response.data
|
|
}
|
|
|
|
export async function updateRoleApi(
|
|
roleSn: number,
|
|
data: { name?: string; description?: string }
|
|
): Promise<void> {
|
|
await api.put(`/roles/${roleSn}`, data)
|
|
}
|
|
|
|
export async function deleteRoleApi(roleSn: number): Promise<void> {
|
|
await api.delete(`/roles/${roleSn}`)
|
|
}
|
|
|
|
// 사용자 승인/거절 API (ADMIN 전용)
|
|
export async function approveUserApi(id: string): Promise<void> {
|
|
await api.put(`/users/${id}/approve`)
|
|
}
|
|
|
|
export async function rejectUserApi(id: string): Promise<void> {
|
|
await api.put(`/users/${id}/reject`)
|
|
}
|
|
|
|
// 시스템 설정 API (ADMIN 전용)
|
|
export interface RegistrationSettings {
|
|
autoApprove: boolean
|
|
defaultRole: boolean
|
|
}
|
|
|
|
export async function fetchRegistrationSettings(): Promise<RegistrationSettings> {
|
|
const response = await api.get<RegistrationSettings>('/settings/registration')
|
|
return response.data
|
|
}
|
|
|
|
export async function updateRegistrationSettingsApi(
|
|
settings: Partial<RegistrationSettings>
|
|
): Promise<RegistrationSettings> {
|
|
const response = await api.put<RegistrationSettings>('/settings/registration', settings)
|
|
return response.data
|
|
}
|
|
|
|
// OAuth 설정 API (ADMIN 전용)
|
|
export interface OAuthSettings {
|
|
autoApproveDomains: string
|
|
}
|
|
|
|
export async function fetchOAuthSettings(): Promise<OAuthSettings> {
|
|
const response = await api.get<OAuthSettings>('/settings/oauth')
|
|
return response.data
|
|
}
|
|
|
|
export async function updateOAuthSettingsApi(
|
|
settings: Partial<OAuthSettings>
|
|
): Promise<OAuthSettings> {
|
|
const response = await api.put<OAuthSettings>('/settings/oauth', settings)
|
|
return response.data
|
|
}
|
|
|
|
// 메뉴 설정 API
|
|
export interface MenuConfigItem {
|
|
id: string
|
|
label: string
|
|
icon: string
|
|
enabled: boolean
|
|
order: number
|
|
}
|
|
|
|
export async function fetchMenuConfig(): Promise<MenuConfigItem[]> {
|
|
const response = await api.get<MenuConfigItem[]>('/menus')
|
|
return response.data
|
|
}
|
|
|
|
export async function updateMenuConfigApi(menus: MenuConfigItem[]): Promise<MenuConfigItem[]> {
|
|
const response = await api.put<MenuConfigItem[]>('/menus', { menus })
|
|
return response.data
|
|
}
|
|
|
|
// 감사 로그 API (ADMIN 전용)
|
|
export interface AuditLogItem {
|
|
logSn: number
|
|
userId: string
|
|
userName: string | null
|
|
userAccount: string | null
|
|
actionCd: string
|
|
actionDtl: string | null
|
|
httpMethod: string | null
|
|
crudType: string | null
|
|
reqUrl: string | null
|
|
reqDtm: string
|
|
resDtm: string | null
|
|
resStatus: number | null
|
|
resSize: number | null
|
|
ipAddr: string | null
|
|
userAgent: string | null
|
|
extra: Record<string, unknown> | null
|
|
}
|
|
|
|
export interface AuditLogListResult {
|
|
items: AuditLogItem[]
|
|
total: number
|
|
page: number
|
|
size: number
|
|
}
|
|
|
|
export async function fetchAuditLogs(params?: {
|
|
page?: number
|
|
size?: number
|
|
userId?: string
|
|
actionCd?: string
|
|
from?: string
|
|
to?: string
|
|
}): Promise<AuditLogListResult> {
|
|
const response = await api.get<AuditLogListResult>('/audit/logs', { params })
|
|
return response.data
|
|
}
|