generated from ttipo/project-base
fix(backend): 관광공사 사진 주소를 https 로 맞춤
관광공사 장소 사진 1,059건 중 279건이 http 였다. 같은 서버(tong.visitkorea.or.kr)가 https 로도 주므로 저장할 때 https 로 바꾼다(장소·행사 모두). 앱은 http 사진도 받지만 웹(https 페이지)에서는 혼합 콘텐츠로 막힌다. 확인하지 않은 다른 서버의 주소는 그대로 둔다. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,8 +52,9 @@ describe('festivalItemToEvent', () => {
|
||||
ldongRegnCd: '27',
|
||||
ldongSignguCd: '260',
|
||||
phone: '053-666-4842',
|
||||
imageUrl: 'http://tong.visitkorea.or.kr/f.jpg',
|
||||
imageThumbUrl: 'http://tong.visitkorea.or.kr/f_s.jpg',
|
||||
// 관광공사 사진 서버는 https 로 맞춘다
|
||||
imageUrl: 'https://tong.visitkorea.or.kr/f.jpg',
|
||||
imageThumbUrl: 'https://tong.visitkorea.or.kr/f_s.jpg',
|
||||
imageLicense: 'Type3',
|
||||
sourceModifiedAt: new Date('2026-10-20T01:15:00.000Z'),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { isInKorea } from '@/shared/utils/geo';
|
||||
import { extractUrl, htmlToText } from '@/shared/utils/html-text';
|
||||
import { preferHttps } from '@/shared/utils/image-url';
|
||||
import { parseKstTimestamp } from '@/shared/utils/kst';
|
||||
import { IMAGE_LICENSES } from '../place.constants';
|
||||
import type { EventKind, ImageLicense } from '../place.constants';
|
||||
@@ -88,8 +89,8 @@ export function festivalItemToEvent(
|
||||
ldongRegnCd: str(item, 'lDongRegnCd'),
|
||||
ldongSignguCd: str(item, 'lDongSignguCd'),
|
||||
phone: htmlToText(item.tel),
|
||||
imageUrl: str(item, 'firstimage'),
|
||||
imageThumbUrl: str(item, 'firstimage2'),
|
||||
imageUrl: preferHttps(str(item, 'firstimage')),
|
||||
imageThumbUrl: preferHttps(str(item, 'firstimage2')),
|
||||
imageLicense: (IMAGE_LICENSES as readonly string[]).includes(license ?? '')
|
||||
? (license as ImageLicense)
|
||||
: null,
|
||||
|
||||
@@ -70,8 +70,9 @@ describe('listItemToPlace', () => {
|
||||
ldongRegnCd: '27',
|
||||
ldongSignguCd: '110',
|
||||
phone: '053-661-2194',
|
||||
imageUrl: 'http://tong.visitkorea.or.kr/a.jpg',
|
||||
imageThumbUrl: 'http://tong.visitkorea.or.kr/a_s.jpg',
|
||||
// 관광공사 사진 서버는 https 로 맞춘다(웹에서 혼합 콘텐츠로 막히지 않게)
|
||||
imageUrl: 'https://tong.visitkorea.or.kr/a.jpg',
|
||||
imageThumbUrl: 'https://tong.visitkorea.or.kr/a_s.jpg',
|
||||
imageLicense: 'Type1',
|
||||
sourceModifiedAt: new Date('2026-10-15T00:30:00.000Z'),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { isInKorea, isInSido, sidoCodeFromAddress } from '@/shared/utils/geo';
|
||||
import { extractUrl, htmlToText } from '@/shared/utils/html-text';
|
||||
import { preferHttps } from '@/shared/utils/image-url';
|
||||
import { parseKstTimestamp } from '@/shared/utils/kst';
|
||||
import { parseOpeningHours } from '@/shared/utils/opening-hours';
|
||||
import type { ParsedOpeningHours } from '@/shared/utils/opening-hours';
|
||||
@@ -224,8 +225,8 @@ export function listItemToPlace(
|
||||
ldongRegnCd: str(item, 'lDongRegnCd'),
|
||||
ldongSignguCd: str(item, 'lDongSignguCd'),
|
||||
phone: htmlToText(item.tel),
|
||||
imageUrl: str(item, 'firstimage'),
|
||||
imageThumbUrl: str(item, 'firstimage2'),
|
||||
imageUrl: preferHttps(str(item, 'firstimage')),
|
||||
imageThumbUrl: preferHttps(str(item, 'firstimage2')),
|
||||
imageLicense: (IMAGE_LICENSES as readonly string[]).includes(license ?? '')
|
||||
? (license as ImageLicense)
|
||||
: null,
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { preferHttps } from './image-url';
|
||||
|
||||
describe('preferHttps', () => {
|
||||
it('관광공사 사진 서버의 http 주소를 https 로 바꾼다', () => {
|
||||
expect(
|
||||
preferHttps('http://tong.visitkorea.or.kr/cms/resource/a_image2_1.jpg'),
|
||||
).toBe('https://tong.visitkorea.or.kr/cms/resource/a_image2_1.jpg');
|
||||
expect(preferHttps('HTTP://Tong.VisitKorea.or.kr/a.jpg')).toBe(
|
||||
'https://Tong.VisitKorea.or.kr/a.jpg',
|
||||
);
|
||||
});
|
||||
|
||||
it('이미 https 이거나 확인하지 않은 서버·빈 값은 그대로 둔다', () => {
|
||||
expect(preferHttps('https://tong.visitkorea.or.kr/a.jpg')).toBe(
|
||||
'https://tong.visitkorea.or.kr/a.jpg',
|
||||
);
|
||||
expect(preferHttps('http://www.kopis.or.kr/a.jpg')).toBe(
|
||||
'http://www.kopis.or.kr/a.jpg',
|
||||
);
|
||||
expect(preferHttps('http://tong.visitkorea.or.kr.evil.example/a.jpg')).toBe(
|
||||
'http://tong.visitkorea.or.kr.evil.example/a.jpg',
|
||||
);
|
||||
expect(preferHttps(null)).toBeNull();
|
||||
expect(preferHttps('')).toBe('');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 사진 주소 정리 (순수 함수).
|
||||
*/
|
||||
|
||||
/**
|
||||
* 같은 파일을 https 로도 주는 것을 확인한 사진 서버.
|
||||
* 2026-11-04 대구 관광공사 장소 사진 1,059건 중 780건은 https, 279건은 http 였다(같은 서버).
|
||||
* https 페이지(웹)에서는 http 사진이 혼합 콘텐츠로 막히므로 저장할 때 https 로 맞춘다.
|
||||
*/
|
||||
const HTTPS_IMAGE_HOSTS = new Set(['tong.visitkorea.or.kr']);
|
||||
|
||||
/** 확인한 서버의 http 사진 주소를 https 로 바꾼다. 다른 서버·형식은 그대로 둔다 */
|
||||
export function preferHttps(url: string | null): string | null {
|
||||
if (!url) return url;
|
||||
const m = /^http:\/\/([^/?#:]+)/i.exec(url);
|
||||
if (!m || !HTTPS_IMAGE_HOSTS.has(m[1].toLowerCase())) return url;
|
||||
return `https://${url.slice('http://'.length)}`;
|
||||
}
|
||||
@@ -199,6 +199,8 @@ erDiagram
|
||||
- 좌표가 주소(없으면 법정동 코드)의 시·도 밖인 항목과, 좌표가 실제 위치가 아닌 것으로 확인한 항목(`place.constants.ts` `TOUR_WRONG_LOCATION_IDS`)도 받지 않는다.
|
||||
이미 있던 장소는 목록을 끝까지 받은 뒤 숨겨진다. 2026-11-04 회귀 점검: '오천서원'(주소 대구 수성구 · 좌표 포항),
|
||||
대구올레 팔공산 3·4·5코스(주소·좌표 모두 운영 사무실이라 김광석길에서 '도보 7분'으로 보였다). 실제 좌표를 확인하면 큐레이션으로 되살린다.
|
||||
- 관광공사 사진 서버(`tong.visitkorea.or.kr`)의 `http://` 주소는 `https://` 로 바꿔 저장한다(`image-url.ts` `preferHttps`).
|
||||
2026-11-04 대구 장소 사진 1,059건 중 279건이 http 였다(같은 서버가 https 로도 준다). 웹(https 페이지)에서는 http 사진이 막힌다.
|
||||
- 2026-11-02 대구 도심 쇼핑 135곳 대부분이 `SH040300` **사후면세점**(더현대 입점 브랜드 매장)이었다. 쇼핑은 큐레이션으로만 AI 후보가 된다.
|
||||
- 분류 코드가 비면 관광타입(12 관광지·14 문화시설·28 레포츠·32 숙박·38 쇼핑·39 음식점)으로 대신한다.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user