Skip to content

Commit c995a7c

Browse files
authored
Merge pull request #60 from nahyeongjin1/feat/library-seats-info
feat: 도서관 좌석 현황 확인 및 예약 바로가기
2 parents 35e6bbd + 0512e10 commit c995a7c

7 files changed

Lines changed: 506 additions & 2 deletions

File tree

public/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"host_permissions": [
2727
"<all_urls>",
2828
"*://ecampus.konkuk.ac.kr/*",
29+
"*://library.konkuk.ac.kr/*",
2930
"https://www.google-analytics.com/*",
3031
"https://ku-linku.store/*"
3132
],

src/apis/external/library.ts

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/**
2+
* Library Integration API
3+
* External service integration for Konkuk University Library seat reservation
4+
*/
5+
6+
import {
7+
LibraryApiResponse,
8+
LibraryLoginData,
9+
LibraryLoginRequest,
10+
LibrarySeatRoomsData,
11+
} from '@/types/api';
12+
13+
const LIBRARY_BASE_URL = 'https://library.konkuk.ac.kr';
14+
const LIBRARY_API_URL = `${LIBRARY_BASE_URL}/pyxis-api`;
15+
const LIBRARY_TOKEN_STORAGE_KEY = 'libraryToken';
16+
17+
/**
18+
* 도서관 열람실 예약 페이지 URL 생성
19+
* @param roomId 열람실 ID
20+
* @returns 예약 페이지 URL
21+
*/
22+
export function getLibraryReservationUrl(roomId: number): string {
23+
return `${LIBRARY_BASE_URL}/library-services/smuf/reading-rooms/${roomId}`;
24+
}
25+
26+
/**
27+
* 도서관 로그인 응답
28+
*/
29+
export interface LibraryLoginResponse {
30+
success: boolean;
31+
data?: LibraryLoginData;
32+
error?: string;
33+
}
34+
35+
/**
36+
* 도서관 좌석 현황 응답
37+
*/
38+
export interface LibrarySeatRoomsResponse {
39+
success: boolean;
40+
needLogin?: boolean;
41+
data?: LibrarySeatRoomsData;
42+
error?: string;
43+
}
44+
45+
/**
46+
* 도서관 로그인
47+
* @param loginId 사용자 ID (학번)
48+
* @param password 비밀번호
49+
* @returns 로그인 응답 (accessToken 포함)
50+
*/
51+
export async function libraryLoginAPI(
52+
loginId: string,
53+
password: string
54+
): Promise<LibraryLoginResponse> {
55+
try {
56+
const requestBody: LibraryLoginRequest = {
57+
loginId,
58+
password,
59+
isFamilyLogin: false,
60+
isMobile: false,
61+
};
62+
63+
const response = await fetch(`${LIBRARY_API_URL}/api/login`, {
64+
method: 'POST',
65+
headers: {
66+
'Content-Type': 'application/json;charset=UTF-8',
67+
},
68+
body: JSON.stringify(requestBody),
69+
credentials: 'include',
70+
});
71+
72+
const result: LibraryApiResponse<LibraryLoginData> = await response.json();
73+
74+
if (result.success) {
75+
return {
76+
success: true,
77+
data: result.data,
78+
};
79+
} else {
80+
return {
81+
success: false,
82+
error: result.message || '로그인에 실패했습니다.',
83+
};
84+
}
85+
} catch (error) {
86+
console.error('[Library] Login error:', error);
87+
return {
88+
success: false,
89+
error: error instanceof Error ? error.message : String(error),
90+
};
91+
}
92+
}
93+
94+
/**
95+
* 도서관 좌석 현황 조회
96+
* @param accessToken 인증 토큰 (pyxis-auth-token)
97+
* @returns 열람실 목록 및 좌석 현황
98+
*/
99+
export async function getLibrarySeatRoomsAPI(
100+
accessToken: string
101+
): Promise<LibrarySeatRoomsResponse> {
102+
try {
103+
const response = await fetch(
104+
`${LIBRARY_API_URL}/1/seat-rooms?smufMethodCode=PC&branchGroupId=1`,
105+
{
106+
method: 'GET',
107+
headers: {
108+
'Content-Type': 'application/json;charset=UTF-8',
109+
'pyxis-auth-token': accessToken,
110+
},
111+
credentials: 'include',
112+
}
113+
);
114+
115+
const result: LibraryApiResponse<LibrarySeatRoomsData> =
116+
await response.json();
117+
118+
if (!result.success) {
119+
return {
120+
success: false,
121+
needLogin: true,
122+
error: result.message || '좌석 현황을 불러올 수 없습니다.',
123+
};
124+
}
125+
126+
// 빈 데이터가 오면 로그인 필요
127+
if (!result.data.list || result.data.list.length === 0) {
128+
return {
129+
success: false,
130+
needLogin: true,
131+
error: '로그인이 필요합니다.',
132+
};
133+
}
134+
135+
return {
136+
success: true,
137+
data: result.data,
138+
};
139+
} catch (error) {
140+
console.error('[Library] Get seat rooms error:', error);
141+
return {
142+
success: false,
143+
error: error instanceof Error ? error.message : String(error),
144+
};
145+
}
146+
}
147+
148+
/**
149+
* 도서관 인증 토큰을 chrome.storage에 저장
150+
* @param loginData 로그인 응답 데이터
151+
*/
152+
export async function setLibraryToken(
153+
loginData: LibraryLoginData
154+
): Promise<boolean> {
155+
try {
156+
if (typeof chrome === 'undefined' || !chrome.storage) {
157+
return false;
158+
}
159+
160+
// 만료 시간 설정 (현재 시간 + 1시간)
161+
const expireDate = new Date();
162+
expireDate.setHours(expireDate.getHours() + 1);
163+
164+
await chrome.storage.local.set({
165+
[LIBRARY_TOKEN_STORAGE_KEY]: {
166+
accessToken: loginData.accessToken,
167+
expireDate: expireDate.toISOString(),
168+
},
169+
});
170+
171+
return true;
172+
} catch (error) {
173+
console.error('[Library] Failed to set token:', error);
174+
return false;
175+
}
176+
}
177+
178+
/**
179+
* chrome.storage에서 도서관 인증 토큰 가져오기
180+
* @returns accessToken 또는 null
181+
*/
182+
export async function getLibraryTokenFromStorage(): Promise<string | null> {
183+
try {
184+
if (typeof chrome === 'undefined' || !chrome.storage) {
185+
return null;
186+
}
187+
188+
const result = await chrome.storage.local.get(LIBRARY_TOKEN_STORAGE_KEY);
189+
const data = result[LIBRARY_TOKEN_STORAGE_KEY];
190+
191+
if (!data?.accessToken) {
192+
return null;
193+
}
194+
195+
// 만료 체크
196+
if (data.expireDate) {
197+
const expireDate = new Date(data.expireDate);
198+
if (expireDate < new Date()) {
199+
// 만료된 토큰 삭제
200+
await chrome.storage.local.remove(LIBRARY_TOKEN_STORAGE_KEY);
201+
return null;
202+
}
203+
}
204+
205+
return data.accessToken;
206+
} catch (error) {
207+
console.error('[Library] Failed to get token from storage:', error);
208+
return null;
209+
}
210+
}
211+
212+
/**
213+
* 도서관 예약 페이지 열기
214+
* 사용자가 직접 로그인하면 바로 예약 화면으로 이동됨
215+
* @param roomId 열람실 ID
216+
*/
217+
export async function openLibraryReservationPage(roomId: number): Promise<void> {
218+
const url = getLibraryReservationUrl(roomId);
219+
await chrome.tabs.create({ url });
220+
}

src/apis/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export * from "./alerts";
1616
// External Integrations
1717
export * from "./external/ecampus";
1818
export * from "./external/banners";
19+
export * from "./external/library";

0 commit comments

Comments
 (0)