DeepSeek Harness Remote
DeepSeek Harness Remote 可将手机或另一台电脑与你运行 DeepSeek Harness 的电脑安全连接,随时随地继续你的工作,方便随时查看进度并继续工作。
Translation pending
교회 홀·방 사용 현황을 한눈에 확인하세요.
Translation pending
ProductivityTypeScript · PLpgSQLGitHub @tjdals-sudoOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
인천 온누리교회 장소 사용 현황 — 공개 조회 + 관리자 예약 (Next.js + Supabase)
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗교회 홀·방 사용 현황을 누구나 실시간으로 보고, 관리자(admin1, admin2, …)만 예약을 등록·수정·삭제하는 웹입니다.
/ : 일 / 주 / 월 보기, 층 필터, 장소 검색, "지금 비어 있는 장소", 실시간 반영/admin : 예약 등록(달력 → 장소 → 시간), 반복 예약, 예약 복사, 여러 장소 한꺼번에 예약, 겹침 알림 팝업(어느 팀과 겹치는지)·빈 장소 추천, 장소 관리, 부서 관리, 사용 통계, 변경 이력, 접속 기록, 비밀번호 변경, 엑셀 다운로드비용 0원: Supabase Free + Vercel Hobby + Google Sheets API(무료 할당량). 모든 시각은 Asia/Seoul.
Next.js 16 (App Router) · TypeScript · Tailwind CSS v4 · Supabase (Postgres, Auth, Realtime) · Vercel · googleapis · exceljs · vitest
brew install supabase/tap/supabasegit clone https://github.com/tjdals-sudo/onnuri-rooms.git
cd onnuri-rooms
npm install
cp .env.example .env.local
onnuri-rooms, Region Northeast Asia (Seoul), DB 비밀번호를 정해서 메모Project URL → .env.local 의 NEXT_PUBLIC_SUPABASE_URLPublishable key (sb_publishable_…) → NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYSecret key (sb_secret_…, "Reveal" 클릭) → SUPABASE_SECRET_KEY ← 절대 공개 금지supabase login # 브라우저로 로그인
supabase link --project-ref <프로젝트 ref> # ref 는 대시보드 URL 의 20자 문자열
supabase db push # supabase/migrations/*.sql 적용 (테이블·제약·트리거·RLS·장소 15곳 seed)
supabase config push # 공개 회원가입 차단 등 인증 설정 반영 (supabase/config.toml)
이미 운영 중인 프로젝트(ref
kpwxddwxlfujfjizwfwd)가 있다면 1~2단계는 건너뛰고.env.local값만 받아서 쓰면 됩니다.
.env.local)| 변수 | 설명 |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase Project URL |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | 공개 키 (브라우저에 노출돼도 되는 키) |
SUPABASE_SECRET_KEY | 서버 전용 비밀 키 (시트 동기화 상태 기록, Cron, 관리자 생성 스크립트) |
ADMIN_ID_DOMAIN | 아이디 → 내부 이메일 변환용 도메인. 기본 onnuri-incheon.local |
ADMIN1_PASSWORD, ADMIN2_PASSWORD … | 관리자 초기 비밀번호 (8자 이상). 계정 생성 때 1회 사용. 번호를 이어 넣으면 관리자 추가 |
GOOGLE_APPS_SCRIPT_URL | (5-A) Apps Script 웹 앱 URL |
GOOGLE_APPS_SCRIPT_SECRET | (5-A) Code.gs 의 SECRET 과 같은 값 |
GOOGLE_SHEET_ID | (5-B) 스프레드시트 URL 의 /d/ 와 /edit 사이 문자열 |
GOOGLE_SERVICE_ACCOUNT_JSON_BASE64 | (5-B) 서비스 계정 키 JSON 을 base64 로 인코딩한 값 |
CRON_SECRET | Vercel Cron 호출 보호용 아무 긴 문자열 |
TEST_ADMIN_LOGIN_ID, TEST_ADMIN_PASSWORD | 통합 테스트용 (선택) |
.env.local 에 ADMIN1_PASSWORD, ADMIN2_PASSWORD, ADMIN3_PASSWORD … 를 넣고:
npm run create-admins
# 비밀번호를 다시 설정하려면: npx tsx scripts/create-admins.ts --reset-password
admin1, admin2, admin3 … 형식이고 회원가입 화면은 없습니다. 관리자를 늘리려면 .env.local 에 ADMIN4_PASSWORD=… 처럼 번호를 이어 넣고 npm run create-admins 를 다시 실행하세요(최대 admin99, 기존 계정 비밀번호는 그대로).[email protected] 형식의 이메일로 저장되지만 화면에는 아이디만 보입니다.npm run dev
# http://localhost:3000 → 사용 현황(누구나)
# http://localhost:3000/admin → 관리자 로그인
두 가지 방식 중 하나만 설정하면 됩니다. 둘 다 있으면 Apps Script 를 씁니다.
Google Cloud 콘솔 없이 스프레드시트 안에서 끝납니다.
장소 예약 현황).google-apps-script/Code.gs 내용을 통째로 붙여 넣습니다.
맨 위 var SECRET = "..." 값은 서버의 GOOGLE_APPS_SCRIPT_SECRET 과 같아야 합니다.https://script.google.com/macros/s/…/exec)을 복사해 GOOGLE_APPS_SCRIPT_URL 에 넣습니다.{"ok":true,...} 가 보이면 준비 완료. 관리자 화면 배너의 시트 다시 동기화 를 누릅니다.코드를 고친 뒤에는 배포 → 배포 관리 → 연필 아이콘 → 버전: 새 버전 → 배포 를 해야 반영됩니다(URL 은 그대로).
실패 알림 메일: Apps Script 방식이면 시트 동기화가 실패하는 순간, 그리고 실패가 다음 날 새벽까지 계속되면 하루 한 번, 스프레드시트를 만든 구글 계정으로 메일이 갑니다. 새벽 점검(Cron)에서 DB 에 접속하지 못해도 메일이 갑니다. Gmail 무료 할당량(하루 100통) 안에서만 쓰므로 비용이 없습니다. 2026-10-23 이전에 붙여 넣은 Code.gs 라면 새 Code.gs 로 바꿔 붙여 넣고 위처럼 새 버전으로 다시 배포한 뒤, 처음 한 번 메일 보내기 권한을 허용해야 합니다. Apps Script 자체가 고장 나면 메일도 못 보내므로, 관리자 화면의 빨간 배너도 함께 확인하세요. (5-B 서비스 계정 방식은 메일 알림이 없습니다.)
https://console.cloud.google.com → 새 프로젝트(예: onnuri-rooms)
API 및 서비스 → 라이브러리 에서 Google Sheets API 사용 설정
API 및 서비스 → 사용자 인증 정보 → 사용자 인증 정보 만들기 → 서비스 계정 생성 (역할은 없어도 됨)
만든 서비스 계정 → 키 탭 → 키 추가 → JSON → 파일 다운로드
터미널에서 base64 로 변환해 .env.local 의 GOOGLE_SERVICE_ACCOUNT_JSON_BASE64 에 붙여넣기
base64 -i ~/Downloads/<키파일>.json | tr -d '\n'
구글 드라이브에서 새 스프레드시트 를 만들고(이름 예: 장소 예약 현황), 공유 버튼 → 서비스 계정 이메일([email protected])을 편집자 로 추가
스프레드시트 URL 의 ID 를 GOOGLE_SHEET_ID 에 입력
관리자 화면 상단 배너의 시트 다시 동기화 를 누르면 YYYY-MM 월 탭과 변경이력 탭이 만들어집니다.
동작 방식(두 방식 공통): 예약·장소가 바뀔 때마다 영향받은 월 탭 전체를 DB 기준으로 다시 씁니다(헤더 고정, 층별 행 색). 실패하면 관리자 화면에 빨간 배너가 뜨고, 매일 새벽 5시 Cron 이 이번 달을 다시 동기화합니다. 환경변수를 비워 두면 시트 연동만 건너뛰고 나머지는 정상 동작합니다.
현재 운영 주소: https://onnuri-rooms.vercel.app (Vercel 프로젝트
onnuri-rooms, 계정 tjdals-sudo). 아래는 처음부터 다시 배포할 때의 안내입니다.
.env.local 의 값을 모두 입력 (TEST_* 는 제외해도 됨)https://onnuri-rooms.vercel.app)로 접속supabase/config.toml 의 site_url 을 수정하고 supabase config pushvercel.json 의 Cron(/api/keepalive, 매일 UTC 20:00 = KST 05:00)은 자동 등록됩니다. Supabase 무료 프로젝트는 7일간 요청이 없으면 일시정지되는데, 이 Cron 이 매일 DB 를 깨워 둡니다.이후에는 main 브랜치에 push 할 때마다 자동 배포됩니다. (2026-10-03 GitHub 연동 완료)
| 테이블 | 내용 |
|---|---|
admins | 관리자(user_id, login_id admin1~admin99) |
rooms | 장소(층, 이름, 수용 인원, 좌석 형태, 정렬, 활성 여부) — 삭제 대신 숨김 |
reservations | 예약. EXCLUDE USING gist (room_id WITH =, tstzrange(start_at,end_at,'[)') WITH &&) 로 겹침 차단 |
departments | 사용 부서 목록(이름, 정렬, 사용 여부). 예약의 department 는 이 목록에서 고른 이름(text) |
audit_logs | 변경 이력(누가·언제·무엇을·변경 전/후 jsonb). 트리거가 기록, 수정·삭제 불가 |
login_logs | 관리자 로그인/로그아웃 |
sheet_sync_status | 시트 동기화 상태 |
reservations_public (뷰) | 연락처·담당자·메모를 뺀 공개용 |
rooms 와 reservations_public 만 조회. 쓰기와 이력 조회는 관리자만(RLS).realtime.send() 로 공개 채널 rooms-public 에 "바뀌었다" 신호만 보내고, 화면이 다시 조회합니다(개인정보가 실시간 페이로드로 새지 않음).preview_recurring / create_recurring / update_recurring_from / delete_recurring_from 함수.npm run typecheck # 타입 검사
npm run lint # ESLint
npm test # 단위 테스트 (시간 유틸, 물어보기, 빈 장소 추천, 사용 통계, 시트·엑셀 행 만들기, 변경 이력 요약)
npm run test:db # 통합 테스트 (원격 Supabase, .env.local 의 TEST_ADMIN_* 필요)
통합 테스트가 확인하는 것: 1분 겹침 거부·맞닿은 예약 허용, 자정 넘김 예약 충돌, 반복 예약 충돌 미리보기·건너뛰기·이후 전체 수정/삭제, 이력 트리거 누락 없음·이력 불변, 비로그인 RLS, 접속 기록, 실시간 신호 수신. 테스트 데이터는 2031년에 [TEST] 접두사로 만들고 끝나면 지웁니다.
app/ 페이지 (홈, admin/login, admin/(protected)/{예약,rooms,stats,history,access,account}, api/{export,keepalive}), 공유 미리보기·아이콘·manifest
components/public/ 일/주/월 뷰, 모바일 카드, 빈 장소 패널
components/admin/ 예약 관리(달력·장소·슬롯·폼·목록), 장소 관리, 내비, 시트 배너
lib/ time(서울 시간 유틸), sheets(구글 시트: Apps Script/서비스 계정, 알림 메일), stats(사용 통계), suggest(빈 장소 추천), excel, audit-format, actions/(서버 액션), supabase/
google-apps-script/ 스프레드시트에 붙여 넣는 Code.gs
supabase/migrations/ DB 스키마 (테이블·제약·트리거·RLS·seed)
scripts/ create-admins.ts
tests/ vitest
proxy.ts /admin 보호 (Next 16 의 middleware)
.env.local 에 새 비밀번호를 넣고 npx tsx scripts/create-admins.ts --reset-password.