From 7f59b94dcf1694c72e5a72cd5734a762573d56c3 Mon Sep 17 00:00:00 2001 From: Johngreen Date: Sun, 8 Mar 2026 20:22:08 +0900 Subject: [PATCH] Rename project from Re:Link to Startover Rebrand repository from "Re:Link" to "Startover" across the codebase. Updates include package names and scopes (@relink/* -> @startover/*), import paths, Next.js transpile settings, vitest name, UI text and docs, Dockerfile and CI/workflow names, deploy scripts and repo paths, and example/production env values. Also add auth-related env vars, an apps/web .env symlink, and small formatting/typing cleanups in several TSX/TS files and tests to accommodate the rename. --- .env | 6 ++++ .env.example | 4 +-- .env.production.example | 6 ++-- .gitea/workflows/deploy.yml | 2 +- DEVELOPMENT_PLAN.md | 8 ++--- Dockerfile | 4 +-- apps/admin/next.config.ts | 2 +- apps/admin/package.json | 6 ++-- apps/admin/src/app/layout.tsx | 4 +-- apps/admin/src/app/page.tsx | 2 +- apps/web/.env | 1 + apps/web/next.config.ts | 2 +- apps/web/package.json | 12 ++++---- .../contract-service.integration.test.ts | 2 +- .../match-request-service.integration.test.ts | 9 ++++-- .../store-service.integration.test.ts | 2 +- .../subsidy-case-service.integration.test.ts | 2 +- ...-certification-service.integration.test.ts | 2 +- apps/web/src/app/admin/contracts/page.tsx | 2 +- apps/web/src/app/admin/page.tsx | 21 ++++++++++--- .../src/app/admin/settings/invite/page.tsx | 2 +- apps/web/src/app/admin/stores/page.tsx | 2 +- apps/web/src/app/admin/subsidies/page.tsx | 2 +- apps/web/src/app/admin/vendors/page.tsx | 2 +- .../app/api/auth/complete-profile/route.ts | 2 +- .../src/app/api/auth/invite/accept/route.ts | 2 +- .../src/app/api/auth/invite/create/route.ts | 2 +- apps/web/src/app/api/v1/master-data/route.ts | 2 +- .../app/api/v1/stores/[id]/submit/route.ts | 7 ++--- apps/web/src/app/api/v1/stores/route.ts | 2 +- apps/web/src/app/auth/register/actions.ts | 2 +- apps/web/src/app/auth/verify/page.tsx | 2 +- apps/web/src/app/contracts/page.tsx | 21 +++++++++---- apps/web/src/app/layout.tsx | 6 ++-- apps/web/src/app/matching/page.tsx | 15 ++++++---- apps/web/src/app/page.tsx | 17 ++++++++--- apps/web/src/app/stores/[id]/page.tsx | 2 +- apps/web/src/app/stores/new/page.tsx | 2 +- apps/web/src/app/stores/page.tsx | 2 +- apps/web/src/app/subsidies/page.tsx | 13 +++++--- apps/web/src/app/vendors/actions.ts | 4 +-- apps/web/src/app/vendors/page.tsx | 2 +- apps/web/src/lib/auth.ts | 8 ++--- apps/web/src/services/contract-service.ts | 6 ++-- .../web/src/services/match-request-service.ts | 26 ++++++++++++---- apps/web/src/services/store-service.ts | 6 ++-- apps/web/src/services/subsidy-case-service.ts | 6 ++-- .../services/vendor-certification-service.ts | 6 ++-- apps/web/vitest.config.ts | 2 +- deploy/deploy.sh | 6 ++-- deploy/poll-deploy.sh | 6 ++-- deploy/setup-server.sh | 8 ++--- deploy/webhook-receiver.py | 8 ++--- docker-compose.prod.yml | 10 +++---- docker-compose.yml | 16 +++++----- docs/analytics/event-schema.md | 4 +-- docs/database/schema-prisma-draft.md | 4 +-- docs/master-data/beta-master-data.md | 4 +-- docs/policies/contract-escrow-policy.md | 10 +++---- docs/policies/data-exposure-policy.md | 16 +++++----- docs/policies/subsidy-policy.md | 12 ++++---- package.json | 6 ++-- packages/analytics/package.json | 4 +-- packages/analytics/src/index.ts | 2 +- packages/analytics/tsup.config.ts | 2 +- packages/application/package.json | 6 ++-- packages/application/src/index.ts | 2 +- packages/application/src/use-case.ts | 2 +- packages/application/tsup.config.ts | 2 +- packages/database/.env | 2 +- packages/database/package.json | 2 +- packages/database/prisma/schema.prisma | 2 +- packages/database/src/index.ts | 2 +- packages/database/src/test-helpers.ts | 13 ++++---- packages/domain/package.json | 4 +-- .../domain/src/contract/check-idempotency.ts | 2 +- .../domain/src/contract/create-contract.ts | 6 ++-- packages/domain/src/contract/open-dispute.ts | 16 +++++----- .../domain/src/contract/release-escrow.ts | 10 ++----- packages/domain/src/index.ts | 2 +- .../src/matching/accept-match-request.ts | 12 +++++--- .../src/matching/create-match-request.ts | 2 +- .../domain/src/store/create-store-draft.ts | 12 ++++---- packages/domain/src/store/publish-store.ts | 6 ++-- packages/domain/src/store/review-store.ts | 10 ++----- packages/domain/src/store/store-facility.ts | 6 ++-- packages/domain/src/store/store-lease.ts | 6 ++-- .../src/subsidy/advance-subsidy-to-ready.ts | 12 +++++--- .../domain/src/subsidy/create-subsidy-case.ts | 18 ++++++----- .../domain/src/subsidy/review-subsidy-case.ts | 12 +++++--- .../src/vendor/apply-vendor-certification.ts | 10 ++----- .../vendor/approve-vendor-certification.ts | 17 +++++++---- packages/infrastructure/package.json | 10 +++---- packages/infrastructure/tsup.config.ts | 2 +- packages/shared/package.json | 2 +- packages/shared/src/constants.ts | 2 +- packages/shared/src/test-utils/index.ts | 2 +- packages/ui/package.json | 2 +- packages/ui/src/index.ts | 2 +- plan.md | 4 +-- pnpm-lock.yaml | 30 +++++++++---------- 101 files changed, 361 insertions(+), 281 deletions(-) create mode 120000 apps/web/.env diff --git a/.env b/.env index 91428de..bd2c790 100644 --- a/.env +++ b/.env @@ -9,5 +9,11 @@ REDIS_URL="redis://localhost:6379" NEXT_PUBLIC_APP_URL="http://localhost:3000" NEXT_PUBLIC_ADMIN_URL="http://localhost:3001" +# Auth.js +AUTH_SECRET="iSceQlYVZ5fhUQdMyFBC0Z8kMo3nMfJRtyPSxbFo6Eg=" +NEXTAUTH_URL="http://localhost:4100" +AUTH_KAKAO_CLIENT_ID="3644464b875f62dc139fe75fef96477c" +AUTH_KAKAO_CLIENT_SECRET="cjijqCD14S5JXyfjeiatFIxIMU1w9Ldr" + # Node NODE_ENV="development" diff --git a/.env.example b/.env.example index 91428de..5b9365f 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ # Database -DATABASE_URL="postgresql://relink:relink_dev@localhost:5432/relink_dev" -DATABASE_TEST_URL="postgresql://relink:relink_test@localhost:5433/relink_test" +DATABASE_URL="postgresql://startover:startover_dev@localhost:5432/startover_dev" +DATABASE_TEST_URL="postgresql://startover:startover_test@localhost:5433/startover_test" # Redis REDIS_URL="redis://localhost:6379" diff --git a/.env.production.example b/.env.production.example index 3d8adab..ad32f71 100644 --- a/.env.production.example +++ b/.env.production.example @@ -1,12 +1,12 @@ # =========================================== -# Re:Link Production Environment Variables +# Startover Production Environment Variables # Copy to .env.production and fill in values # =========================================== # Database -DB_USER=relink +DB_USER=startover DB_PASSWORD=CHANGE_ME_STRONG_PASSWORD -DB_NAME=relink_prod +DB_NAME=startover_prod # Redis REDIS_PASSWORD=CHANGE_ME_STRONG_PASSWORD diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index afeea8a..c0f1dd7 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Deploy Re:Link +name: Deploy Startover on: push: diff --git a/DEVELOPMENT_PLAN.md b/DEVELOPMENT_PLAN.md index cb869a1..92f7614 100644 --- a/DEVELOPMENT_PLAN.md +++ b/DEVELOPMENT_PLAN.md @@ -1,4 +1,4 @@ -# Re:Link (리링크) 실행형 개발계획서 +# Startover (스타트오버) 실행형 개발계획서 > 폐업-철거-인테리어 선순환 통합 매칭 플랫폼 > 작성일: 2026-03-07 | 기준 문서: 2026년 예비창업패키지 사업계획서 @@ -8,7 +8,7 @@ ## 1. 문서 목적 -이 문서는 단순 기능 목록이 아니라, Re:Link MVP를 실제로 만들기 위해 먼저 고정해야 할 **정책, 운영, 계측, 아키텍처, 일정**을 정리한 실행 기준 문서다. +이 문서는 단순 기능 목록이 아니라, Startover MVP를 실제로 만들기 위해 먼저 고정해야 할 **정책, 운영, 계측, 아키텍처, 일정**을 정리한 실행 기준 문서다. ### 1-1. 문서 역할 @@ -19,7 +19,7 @@ ### 1-2. 토스식 의사결정 원칙 -Re:Link의 MVP 의사결정은 아래 원칙을 기본값으로 삼는다. +Startover의 MVP 의사결정은 아래 원칙을 기본값으로 삼는다. | 원칙 | 적용 방식 | |------|-----------| @@ -529,7 +529,7 @@ re-link/ ## 14. 최종 정리 -Re:Link의 MVP는 단순한 매칭 앱이 아니라, **정책과 운영으로 신뢰를 만드는 거래 플랫폼**이다. 따라서 개발 우선순위는 `예쁜 UI`나 `기능 수`가 아니라 아래 순서를 따라야 한다. +Startover의 MVP는 단순한 매칭 앱이 아니라, **정책과 운영으로 신뢰를 만드는 거래 플랫폼**이다. 따라서 개발 우선순위는 `예쁜 UI`나 `기능 수`가 아니라 아래 순서를 따라야 한다. 1. 정책 고정 2. 운영 도구 구축 diff --git a/Dockerfile b/Dockerfile index 1480671..aa58ad4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # ============================================ -# Re:Link Turborepo Multi-stage Dockerfile +# Startover Turborepo Multi-stage Dockerfile # Usage: docker build --build-arg APP_NAME=web . # ============================================ @@ -32,7 +32,7 @@ COPY . . # Generate Prisma client & build RUN cd packages/database && npx prisma generate -RUN pnpm turbo run build --filter=@relink/${APP_NAME} +RUN pnpm turbo run build --filter=@startover/${APP_NAME} # Ensure public directories exist for COPY RUN mkdir -p /app/apps/${APP_NAME}/public diff --git a/apps/admin/next.config.ts b/apps/admin/next.config.ts index 0b761bb..1ddf477 100644 --- a/apps/admin/next.config.ts +++ b/apps/admin/next.config.ts @@ -2,7 +2,7 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { output: 'standalone', - transpilePackages: ['@relink/ui', '@relink/shared'], + transpilePackages: ['@startover/ui', '@startover/shared'], }; export default nextConfig; diff --git a/apps/admin/package.json b/apps/admin/package.json index a235a78..4e084da 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/admin", + "name": "@startover/admin", "version": "0.0.1", "private": true, "type": "module", @@ -12,8 +12,8 @@ "clean": "rm -rf .next" }, "dependencies": { - "@relink/shared": "workspace:*", - "@relink/ui": "workspace:*", + "@startover/shared": "workspace:*", + "@startover/ui": "workspace:*", "next": "^15.1.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/apps/admin/src/app/layout.tsx b/apps/admin/src/app/layout.tsx index 5a9dc3d..10c18cc 100644 --- a/apps/admin/src/app/layout.tsx +++ b/apps/admin/src/app/layout.tsx @@ -3,8 +3,8 @@ import type { Metadata } from 'next'; import './globals.css'; export const metadata: Metadata = { - title: 'Re:Link Admin', - description: 'Re:Link 운영 콘솔', + title: 'Startover Admin', + description: 'Startover 운영 콘솔', }; export default function RootLayout({ diff --git a/apps/admin/src/app/page.tsx b/apps/admin/src/app/page.tsx index cf44fd4..5dd547c 100644 --- a/apps/admin/src/app/page.tsx +++ b/apps/admin/src/app/page.tsx @@ -1,7 +1,7 @@ export default function AdminHomePage() { return (
-

Re:Link Admin

+

Startover Admin

운영 콘솔

); diff --git a/apps/web/.env b/apps/web/.env new file mode 120000 index 0000000..e44b744 --- /dev/null +++ b/apps/web/.env @@ -0,0 +1 @@ +/Users/johngreen/Dev/Re_Link/.env \ No newline at end of file diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 0b761bb..1ddf477 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -2,7 +2,7 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { output: 'standalone', - transpilePackages: ['@relink/ui', '@relink/shared'], + transpilePackages: ['@startover/ui', '@startover/shared'], }; export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json index e4c957f..69af2c8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/web", + "name": "@startover/web", "version": "0.0.1", "private": true, "type": "module", @@ -15,11 +15,11 @@ "dependencies": { "@auth/prisma-adapter": "^2.11.1", "@prisma/client": "^6.1.0", - "@relink/database": "workspace:*", - "@relink/domain": "workspace:*", - "@relink/infrastructure": "workspace:*", - "@relink/shared": "workspace:*", - "@relink/ui": "workspace:*", + "@startover/database": "workspace:*", + "@startover/domain": "workspace:*", + "@startover/infrastructure": "workspace:*", + "@startover/shared": "workspace:*", + "@startover/ui": "workspace:*", "argon2": "^0.44.0", "next": "^15.1.0", "next-auth": "5.0.0-beta.30", diff --git a/apps/web/src/__tests__/integration/contract-service.integration.test.ts b/apps/web/src/__tests__/integration/contract-service.integration.test.ts index 276365b..8e7d655 100644 --- a/apps/web/src/__tests__/integration/contract-service.integration.test.ts +++ b/apps/web/src/__tests__/integration/contract-service.integration.test.ts @@ -5,7 +5,7 @@ import { teardownTestDatabase, cleanAllTables, seedTestMasterData, -} from '@relink/database'; +} from '@startover/database'; import { createStoreDraftService, submitStoreService, diff --git a/apps/web/src/__tests__/integration/match-request-service.integration.test.ts b/apps/web/src/__tests__/integration/match-request-service.integration.test.ts index f45e207..79eb687 100644 --- a/apps/web/src/__tests__/integration/match-request-service.integration.test.ts +++ b/apps/web/src/__tests__/integration/match-request-service.integration.test.ts @@ -5,7 +5,7 @@ import { teardownTestDatabase, cleanAllTables, seedTestMasterData, -} from '@relink/database'; +} from '@startover/database'; import { createStoreDraftService, submitStoreService, @@ -71,7 +71,12 @@ describe('Match Request Service Integration Tests', () => { if (!createResult.ok) throw new Error('createStoreDraft failed'); await submitStoreService(prisma, createResult.value.publicId, ownerUserId.toString()); - await reviewStoreService(prisma, createResult.value.publicId, 'APPROVED', operatorUserId.toString()); + await reviewStoreService( + prisma, + createResult.value.publicId, + 'APPROVED', + operatorUserId.toString(), + ); const policyVersion = await prisma.policyVersion.create({ data: { diff --git a/apps/web/src/__tests__/integration/store-service.integration.test.ts b/apps/web/src/__tests__/integration/store-service.integration.test.ts index 8c5b27a..1e98c0b 100644 --- a/apps/web/src/__tests__/integration/store-service.integration.test.ts +++ b/apps/web/src/__tests__/integration/store-service.integration.test.ts @@ -5,7 +5,7 @@ import { teardownTestDatabase, cleanAllTables, seedTestMasterData, -} from '@relink/database'; +} from '@startover/database'; import { createStoreDraftService, submitStoreService, diff --git a/apps/web/src/__tests__/integration/subsidy-case-service.integration.test.ts b/apps/web/src/__tests__/integration/subsidy-case-service.integration.test.ts index ea87a64..2601831 100644 --- a/apps/web/src/__tests__/integration/subsidy-case-service.integration.test.ts +++ b/apps/web/src/__tests__/integration/subsidy-case-service.integration.test.ts @@ -5,7 +5,7 @@ import { teardownTestDatabase, cleanAllTables, seedTestMasterData, -} from '@relink/database'; +} from '@startover/database'; import { createStoreDraftService, submitStoreService, diff --git a/apps/web/src/__tests__/integration/vendor-certification-service.integration.test.ts b/apps/web/src/__tests__/integration/vendor-certification-service.integration.test.ts index b9e0a41..90dc927 100644 --- a/apps/web/src/__tests__/integration/vendor-certification-service.integration.test.ts +++ b/apps/web/src/__tests__/integration/vendor-certification-service.integration.test.ts @@ -5,7 +5,7 @@ import { teardownTestDatabase, cleanAllTables, seedTestMasterData, -} from '@relink/database'; +} from '@startover/database'; import { applyVendorCertificationService, reviewVendorCertificationService, diff --git a/apps/web/src/app/admin/contracts/page.tsx b/apps/web/src/app/admin/contracts/page.tsx index 67a5275..82e708a 100644 --- a/apps/web/src/app/admin/contracts/page.tsx +++ b/apps/web/src/app/admin/contracts/page.tsx @@ -1,5 +1,5 @@ import { revalidatePath } from 'next/cache'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import { releaseEscrowService } from '@/services/contract-service'; import ContractActionButtons from './ContractActionButtons'; diff --git a/apps/web/src/app/admin/page.tsx b/apps/web/src/app/admin/page.tsx index 863882f..44828b6 100644 --- a/apps/web/src/app/admin/page.tsx +++ b/apps/web/src/app/admin/page.tsx @@ -4,7 +4,7 @@ export default function AdminDashboardPage() { return (

운영자 대시보드

-

Re:Link 운영 현황을 한눈에 확인합니다

+

Startover 운영 현황을 한눈에 확인합니다

{/* KPI 요약 */}
@@ -75,10 +75,23 @@ export default function AdminDashboardPage() { {[ { action: '매장 승인', target: '강남역 카페 양도', actor: '김운영', time: '10분 전' }, { action: '업체 인증 승인', target: '(주)클린철거', actor: '이운영', time: '1시간 전' }, - { action: '에스크로 입금 확인', target: '선릉역 한식당 계약', actor: 'SYSTEM', time: '2시간 전' }, - { action: '매칭 요청 수락', target: '홍대 디저트카페', actor: '박운영', time: '3시간 전' }, + { + action: '에스크로 입금 확인', + target: '선릉역 한식당 계약', + actor: 'SYSTEM', + time: '2시간 전', + }, + { + action: '매칭 요청 수락', + target: '홍대 디저트카페', + actor: '박운영', + time: '3시간 전', + }, ].map((log, i) => ( -
+
{log.action} {log.target} diff --git a/apps/web/src/app/admin/settings/invite/page.tsx b/apps/web/src/app/admin/settings/invite/page.tsx index 815ff15..2abdc1f 100644 --- a/apps/web/src/app/admin/settings/invite/page.tsx +++ b/apps/web/src/app/admin/settings/invite/page.tsx @@ -1,6 +1,6 @@ import { auth } from '@/lib/auth'; import { redirect } from 'next/navigation'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import { InviteForm } from './invite-form'; const prisma = createPrismaClient(); diff --git a/apps/web/src/app/admin/stores/page.tsx b/apps/web/src/app/admin/stores/page.tsx index cc37744..dabe101 100644 --- a/apps/web/src/app/admin/stores/page.tsx +++ b/apps/web/src/app/admin/stores/page.tsx @@ -1,5 +1,5 @@ import { revalidatePath } from 'next/cache'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import { reviewStoreService } from '@/services/store-service'; import StoreActionButtons from './StoreActionButtons'; diff --git a/apps/web/src/app/admin/subsidies/page.tsx b/apps/web/src/app/admin/subsidies/page.tsx index 12ac9d0..b026dfe 100644 --- a/apps/web/src/app/admin/subsidies/page.tsx +++ b/apps/web/src/app/admin/subsidies/page.tsx @@ -1,5 +1,5 @@ import { revalidatePath } from 'next/cache'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import { reviewSubsidyCaseService } from '@/services/subsidy-case-service'; import SubsidyActionButtons from './SubsidyActionButtons'; diff --git a/apps/web/src/app/admin/vendors/page.tsx b/apps/web/src/app/admin/vendors/page.tsx index cdcad03..8aa5dc2 100644 --- a/apps/web/src/app/admin/vendors/page.tsx +++ b/apps/web/src/app/admin/vendors/page.tsx @@ -1,5 +1,5 @@ import { revalidatePath } from 'next/cache'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import { reviewVendorCertificationService } from '@/services/vendor-certification-service'; import VendorActionButtons from './VendorActionButtons'; diff --git a/apps/web/src/app/api/auth/complete-profile/route.ts b/apps/web/src/app/api/auth/complete-profile/route.ts index 823c2a2..048e92e 100644 --- a/apps/web/src/app/api/auth/complete-profile/route.ts +++ b/apps/web/src/app/api/auth/complete-profile/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from 'next/server'; import { auth } from '@/lib/auth'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import type { ConsentType, ProfileType, UserRole } from '@prisma/client'; diff --git a/apps/web/src/app/api/auth/invite/accept/route.ts b/apps/web/src/app/api/auth/invite/accept/route.ts index b65c247..ac5cc34 100644 --- a/apps/web/src/app/api/auth/invite/accept/route.ts +++ b/apps/web/src/app/api/auth/invite/accept/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from 'next/server'; import argon2 from 'argon2'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; const prisma = createPrismaClient(); diff --git a/apps/web/src/app/api/auth/invite/create/route.ts b/apps/web/src/app/api/auth/invite/create/route.ts index a279732..137d5ee 100644 --- a/apps/web/src/app/api/auth/invite/create/route.ts +++ b/apps/web/src/app/api/auth/invite/create/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from 'next/server'; import { auth } from '@/lib/auth'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import type { UserRole } from '@prisma/client'; diff --git a/apps/web/src/app/api/v1/master-data/route.ts b/apps/web/src/app/api/v1/master-data/route.ts index 2696677..fbc7970 100644 --- a/apps/web/src/app/api/v1/master-data/route.ts +++ b/apps/web/src/app/api/v1/master-data/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from 'next/server'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; const prisma = createPrismaClient(); diff --git a/apps/web/src/app/api/v1/stores/[id]/submit/route.ts b/apps/web/src/app/api/v1/stores/[id]/submit/route.ts index bd3140f..f421d08 100644 --- a/apps/web/src/app/api/v1/stores/[id]/submit/route.ts +++ b/apps/web/src/app/api/v1/stores/[id]/submit/route.ts @@ -1,13 +1,10 @@ import { NextResponse, type NextRequest } from 'next/server'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import { submitStoreService } from '@/services/store-service'; const prisma = createPrismaClient(); -export async function POST( - request: NextRequest, - { params }: { params: Promise<{ id: string }> }, -) { +export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { const { id } = await params; const body = await request.json().catch(() => ({})); const actorUserId = (body as Record).actorUserId; diff --git a/apps/web/src/app/api/v1/stores/route.ts b/apps/web/src/app/api/v1/stores/route.ts index da5c1dc..8221bfb 100644 --- a/apps/web/src/app/api/v1/stores/route.ts +++ b/apps/web/src/app/api/v1/stores/route.ts @@ -1,5 +1,5 @@ import { NextResponse, type NextRequest } from 'next/server'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import { createStoreDraftService } from '@/services/store-service'; import { z } from 'zod'; diff --git a/apps/web/src/app/auth/register/actions.ts b/apps/web/src/app/auth/register/actions.ts index 4873484..c91dc9b 100644 --- a/apps/web/src/app/auth/register/actions.ts +++ b/apps/web/src/app/auth/register/actions.ts @@ -2,7 +2,7 @@ import { z } from 'zod'; import argon2 from 'argon2'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; const prisma = createPrismaClient(); import { randomBytes } from 'crypto'; diff --git a/apps/web/src/app/auth/verify/page.tsx b/apps/web/src/app/auth/verify/page.tsx index dfb5742..730f3c3 100644 --- a/apps/web/src/app/auth/verify/page.tsx +++ b/apps/web/src/app/auth/verify/page.tsx @@ -1,6 +1,6 @@ import { redirect } from 'next/navigation'; import Link from 'next/link'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; const prisma = createPrismaClient(); diff --git a/apps/web/src/app/contracts/page.tsx b/apps/web/src/app/contracts/page.tsx index 56f62f2..011f608 100644 --- a/apps/web/src/app/contracts/page.tsx +++ b/apps/web/src/app/contracts/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; export const dynamic = 'force-dynamic'; @@ -74,10 +74,19 @@ export default async function ContractsPage() {

데이터가 없습니다

) : ( contracts.map((contract) => { - const statusInfo = STATUS_MAP[contract.status] ?? { label: contract.status, color: 'bg-gray-100 text-gray-700' }; - const escrowInfo = ESCROW_MAP[contract.escrowStatus] ?? { label: contract.escrowStatus, color: 'text-gray-500' }; + const statusInfo = STATUS_MAP[contract.status] ?? { + label: contract.status, + color: 'bg-gray-100 text-gray-700', + }; + const escrowInfo = ESCROW_MAP[contract.escrowStatus] ?? { + label: contract.escrowStatus, + color: 'text-gray-500', + }; return ( -
+
@@ -90,7 +99,9 @@ export default async function ContractsPage() { 생성일: {new Date(contract.createdAt).toLocaleDateString('ko-KR')}

- + {statusInfo.label}
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 40fdc0c..52974ff 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -6,8 +6,8 @@ import { AuthButtons } from './auth-buttons'; import './globals.css'; export const metadata: Metadata = { - title: 'Re:Link', - description: 'Re:Link - 폐업 · 양도 · 창업을 잇는 중개 플랫폼', + title: 'Startover', + description: 'Startover - 폐업 · 양도 · 창업을 잇는 중개 플랫폼', }; const OPERATOR_ROLES = [ @@ -26,7 +26,7 @@ async function Navigation() {
diff --git a/apps/web/src/app/stores/[id]/page.tsx b/apps/web/src/app/stores/[id]/page.tsx index 8e6a2c7..1484f8e 100644 --- a/apps/web/src/app/stores/[id]/page.tsx +++ b/apps/web/src/app/stores/[id]/page.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; import { notFound } from 'next/navigation'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; export const dynamic = 'force-dynamic'; diff --git a/apps/web/src/app/stores/new/page.tsx b/apps/web/src/app/stores/new/page.tsx index db3aa70..dcc0725 100644 --- a/apps/web/src/app/stores/new/page.tsx +++ b/apps/web/src/app/stores/new/page.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; import { redirect } from 'next/navigation'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; export const dynamic = 'force-dynamic'; diff --git a/apps/web/src/app/stores/page.tsx b/apps/web/src/app/stores/page.tsx index 986f93d..442bd6a 100644 --- a/apps/web/src/app/stores/page.tsx +++ b/apps/web/src/app/stores/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; export const dynamic = 'force-dynamic'; diff --git a/apps/web/src/app/subsidies/page.tsx b/apps/web/src/app/subsidies/page.tsx index 2cf09e5..2be12ec 100644 --- a/apps/web/src/app/subsidies/page.tsx +++ b/apps/web/src/app/subsidies/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; export const dynamic = 'force-dynamic'; @@ -40,7 +40,7 @@ export default async function SubsidiesPage() {

가이드형 지원금 대행 서비스

- Re:Link은 지원금 신청 보조 파트너로서, 체크리스트·서류 업로드·진행 상태·운영자 피드백을 + Startover은 지원금 신청 보조 파트너로서, 체크리스트·서류 업로드·진행 상태·운영자 피드백을 제공합니다.

@@ -51,7 +51,10 @@ export default async function SubsidiesPage() {

데이터가 없습니다

) : ( cases.map((c) => { - const statusInfo = STATUS_MAP[c.status] ?? { label: c.status, color: 'bg-gray-100 text-gray-700' }; + const statusInfo = STATUS_MAP[c.status] ?? { + label: c.status, + color: 'bg-gray-100 text-gray-700', + }; const total = c.checklistItems.length; const checked = c.checklistItems.filter((item) => item.status === 'CHECKED').length; return ( @@ -63,7 +66,9 @@ export default async function SubsidiesPage() { 신청일: {new Date(c.createdAt).toLocaleDateString('ko-KR')}

- + {statusInfo.label}
diff --git a/apps/web/src/app/vendors/actions.ts b/apps/web/src/app/vendors/actions.ts index 140d867..9f851df 100644 --- a/apps/web/src/app/vendors/actions.ts +++ b/apps/web/src/app/vendors/actions.ts @@ -1,9 +1,9 @@ 'use server'; import { revalidatePath } from 'next/cache'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import { applyVendorCertificationService } from '@/services/vendor-certification-service'; -import type { VendorType } from '@relink/domain'; +import type { VendorType } from '@startover/domain'; const prisma = createPrismaClient(); diff --git a/apps/web/src/app/vendors/page.tsx b/apps/web/src/app/vendors/page.tsx index 4cf3949..018e8d5 100644 --- a/apps/web/src/app/vendors/page.tsx +++ b/apps/web/src/app/vendors/page.tsx @@ -1,4 +1,4 @@ -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import VendorApplicationForm from './vendor-application-form'; export const dynamic = 'force-dynamic'; diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts index b483efa..c7585ce 100644 --- a/apps/web/src/lib/auth.ts +++ b/apps/web/src/lib/auth.ts @@ -2,7 +2,7 @@ import NextAuth from 'next-auth'; import type { NextAuthConfig } from 'next-auth'; import Credentials from 'next-auth/providers/credentials'; import Kakao from 'next-auth/providers/kakao'; -import { createPrismaClient } from '@relink/database'; +import { createPrismaClient } from '@startover/database'; import argon2 from 'argon2'; import type { UserRole } from '@prisma/client'; @@ -193,7 +193,7 @@ const fullConfig: NextAuthConfig = { return { id: String(profile.id), name: kakaoProfile?.nickname ?? null, - email: (kakaoAccount?.email as string) || `kakao_${profile.id}@placeholder.relink`, + email: (kakaoAccount?.email as string) || `kakao_${profile.id}@placeholder.startover`, image: kakaoProfile?.profile_image_url ?? null, }; }, @@ -239,11 +239,11 @@ const fullConfig: NextAuthConfig = { }, async signIn({ user, account }) { if (account?.provider === 'kakao') { - // placeholder 이메일(@placeholder.relink)은 비즈 앱 전환 전 임시 처리 + // placeholder 이메일(@placeholder.startover)은 비즈 앱 전환 전 임시 처리 const email = user.email; if (!email) return false; - const isPlaceholder = email.endsWith('@placeholder.relink'); + const isPlaceholder = email.endsWith('@placeholder.startover'); const existing = !isPlaceholder ? await prisma.user.findFirst({ where: { emailNormalized: email.toLowerCase().trim() }, diff --git a/apps/web/src/services/contract-service.ts b/apps/web/src/services/contract-service.ts index 77ca29c..6d22878 100644 --- a/apps/web/src/services/contract-service.ts +++ b/apps/web/src/services/contract-service.ts @@ -5,9 +5,9 @@ import { checkIdempotency, openDispute, type ContractType, -} from '@relink/domain'; -import { createAuditLog, enqueueOutboxEvent } from '@relink/infrastructure'; -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +} from '@startover/domain'; +import { createAuditLog, enqueueOutboxEvent } from '@startover/infrastructure'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; // --------------------------------------------------------------------------- // I012: 계약 생성 서비스 diff --git a/apps/web/src/services/match-request-service.ts b/apps/web/src/services/match-request-service.ts index 5d2a3e3..58ac614 100644 --- a/apps/web/src/services/match-request-service.ts +++ b/apps/web/src/services/match-request-service.ts @@ -6,9 +6,9 @@ import { type MatchType, type MatchSourceType, type StoreSearchCriteria, -} from '@relink/domain'; -import { createAuditLog, enqueueOutboxEvent } from '@relink/infrastructure'; -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +} from '@startover/domain'; +import { createAuditLog, enqueueOutboxEvent } from '@startover/infrastructure'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface CreateMatchRequestServiceInput { readonly storePublicId: string; @@ -34,7 +34,9 @@ export async function createMatchRequestService( }); if (!store) { - return failure(appError('NOT_FOUND', '매장을 찾을 수 없습니다.', { storePublicId: input.storePublicId })); + return failure( + appError('NOT_FOUND', '매장을 찾을 수 없습니다.', { storePublicId: input.storePublicId }), + ); } const openRequest = await prisma.matchRequest.findFirst({ @@ -118,7 +120,9 @@ export async function acceptMatchRequestService( }); if (!matchRequest) { - return failure(appError('NOT_FOUND', '매칭 요청을 찾을 수 없습니다.', { matchRequestPublicId })); + return failure( + appError('NOT_FOUND', '매칭 요청을 찾을 수 없습니다.', { matchRequestPublicId }), + ); } const domainResult = acceptMatchRequest({ @@ -172,7 +176,17 @@ export async function acceptMatchRequestService( export async function searchStoresService( prisma: PrismaClient, criteria: StoreSearchCriteria, -): Promise; total: number; page: number; limit: number }, AppError>> { +): Promise< + Result< + { + stores: Array<{ publicId: string; listingTitle: string; dealStatus: string }>; + total: number; + page: number; + limit: number; + }, + AppError + > +> { const defaults = buildSearchDefaults(criteria); const where: Record = { diff --git a/apps/web/src/services/store-service.ts b/apps/web/src/services/store-service.ts index 276c0b2..7253768 100644 --- a/apps/web/src/services/store-service.ts +++ b/apps/web/src/services/store-service.ts @@ -12,9 +12,9 @@ import { type StoreData, type ViewerContext, type FilteredStoreData, -} from '@relink/domain'; -import { createAuditLog, enqueueOutboxEvent } from '@relink/infrastructure'; -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +} from '@startover/domain'; +import { createAuditLog, enqueueOutboxEvent } from '@startover/infrastructure'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; function buildRegionChecker(regions: { code: string; isBetaEnabled: boolean }[]): RegionChecker { const betaCodes = new Set(regions.filter((r) => r.isBetaEnabled).map((r) => r.code)); diff --git a/apps/web/src/services/subsidy-case-service.ts b/apps/web/src/services/subsidy-case-service.ts index ea0cce5..0742efe 100644 --- a/apps/web/src/services/subsidy-case-service.ts +++ b/apps/web/src/services/subsidy-case-service.ts @@ -4,9 +4,9 @@ import { reviewSubsidyCase, type ChecklistItemTemplate, type SubsidyReviewDecision, -} from '@relink/domain'; -import { createAuditLog, enqueueOutboxEvent } from '@relink/infrastructure'; -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +} from '@startover/domain'; +import { createAuditLog, enqueueOutboxEvent } from '@startover/infrastructure'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface CreateSubsidyCaseServiceInput { readonly storePublicId: string; diff --git a/apps/web/src/services/vendor-certification-service.ts b/apps/web/src/services/vendor-certification-service.ts index e673e72..57d9868 100644 --- a/apps/web/src/services/vendor-certification-service.ts +++ b/apps/web/src/services/vendor-certification-service.ts @@ -5,9 +5,9 @@ import { approveVendorCertification, type VendorType, type VendorCertificationDecision, -} from '@relink/domain'; -import { createAuditLog, enqueueOutboxEvent } from '@relink/infrastructure'; -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +} from '@startover/domain'; +import { createAuditLog, enqueueOutboxEvent } from '@startover/infrastructure'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface ApplyVendorCertificationServiceInput { readonly ownerUserId: string; diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts index 2e4e39c..25d3c26 100644 --- a/apps/web/vitest.config.ts +++ b/apps/web/vitest.config.ts @@ -3,7 +3,7 @@ import { resolve } from 'path'; export default defineConfig({ test: { - name: '@relink/web', + name: '@startover/web', globals: true, environment: 'node', testTimeout: 30000, diff --git a/deploy/deploy.sh b/deploy/deploy.sh index 26ac841..dd18a1c 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -2,14 +2,14 @@ set -euo pipefail # =========================================== -# Re:Link Auto Deploy Script +# Startover Auto Deploy Script # Triggered by Gitea webhook on push to main # =========================================== -APP_DIR="$HOME/relink" +APP_DIR="$HOME/startover" LOG_FILE="$APP_DIR/deploy.log" LOCK_FILE="$APP_DIR/deploy.lock" -GITEA_REPO="http://39.117.244.52:3000/geonhee/Re_Link.git" +GITEA_REPO="http://39.117.244.52:3000/geonhee/startover.git" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" diff --git a/deploy/poll-deploy.sh b/deploy/poll-deploy.sh index f6a0200..b3329d7 100644 --- a/deploy/poll-deploy.sh +++ b/deploy/poll-deploy.sh @@ -1,14 +1,14 @@ #!/bin/bash # =========================================== -# Re:Link Poll-based Auto Deploy +# Startover Poll-based Auto Deploy # Checks Gitea for new commits every minute # =========================================== -APP_DIR="$HOME/relink" +APP_DIR="$HOME/startover" REPO_DIR="$APP_DIR/repo" LOG_FILE="$APP_DIR/deploy.log" LOCK_FILE="$APP_DIR/deploy.lock" -GITEA_REPO="http://39.117.244.52:3000/geonhee/Re_Link.git" +GITEA_REPO="http://39.117.244.52:3000/geonhee/startover.git" BRANCH="main" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"; } diff --git a/deploy/setup-server.sh b/deploy/setup-server.sh index 213cdc6..b20ceef 100644 --- a/deploy/setup-server.sh +++ b/deploy/setup-server.sh @@ -2,11 +2,11 @@ set -euo pipefail # =========================================== -# Re:Link IDC Server Setup Script +# Startover IDC Server Setup Script # Run once on the IDC server to initialize # =========================================== -echo "=== Re:Link Server Setup ===" +echo "=== Startover Server Setup ===" # 1. Install Docker Compose V2 plugin echo "[1/5] Installing Docker Compose V2 plugin..." @@ -31,7 +31,7 @@ echo "Act Runner v${ACT_RUNNER_VERSION} installed." # 3. Create app directory echo "[3/5] Creating application directory..." -APP_DIR="$HOME/relink" +APP_DIR="$HOME/startover" mkdir -p "$APP_DIR" cd "$APP_DIR" @@ -71,7 +71,7 @@ echo " cd $APP_DIR" echo " act_runner register \\" echo " --instance http://39.117.244.52:3000 \\" echo " --token \\" -echo " --name relink-runner \\" +echo " --name startover-runner \\" echo " --labels ubuntu-latest:host" echo "" echo "2. Start the runner service:" diff --git a/deploy/webhook-receiver.py b/deploy/webhook-receiver.py index 5ee25ae..c8e4c91 100644 --- a/deploy/webhook-receiver.py +++ b/deploy/webhook-receiver.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Re:Link Gitea Webhook Receiver +Startover Gitea Webhook Receiver Listens for push events and triggers deployment. """ @@ -13,11 +13,11 @@ import sys from http.server import HTTPServer, BaseHTTPRequestHandler from datetime import datetime -WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "relink-deploy-secret") -DEPLOY_SCRIPT = os.path.expanduser("~/relink/deploy.sh") +WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "startover-deploy-secret") +DEPLOY_SCRIPT = os.path.expanduser("~/startover/deploy.sh") DEPLOY_BRANCH = "main" PORT = int(os.environ.get("WEBHOOK_PORT", "9000")) -LOG_FILE = os.path.expanduser("~/relink/webhook.log") +LOG_FILE = os.path.expanduser("~/startover/webhook.log") def log(msg: str): diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 459b3a8..505a62f 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -5,13 +5,13 @@ services: ports: - "127.0.0.1:5432:5432" environment: - POSTGRES_USER: ${DB_USER:-relink} + POSTGRES_USER: ${DB_USER:-startover} POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required} - POSTGRES_DB: ${DB_NAME:-relink_prod} + POSTGRES_DB: ${DB_NAME:-startover_prod} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-relink} -d ${DB_NAME:-relink_prod}"] + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-startover} -d ${DB_NAME:-startover_prod}"] interval: 10s timeout: 5s retries: 5 @@ -57,7 +57,7 @@ services: ports: - "127.0.0.1:3000:3000" environment: - DATABASE_URL: postgresql://${DB_USER:-relink}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-relink_prod}?schema=public + DATABASE_URL: postgresql://${DB_USER:-startover}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-startover_prod}?schema=public REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379 NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000} NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:?NEXTAUTH_SECRET is required} @@ -88,7 +88,7 @@ services: ports: - "127.0.0.1:3001:3000" environment: - DATABASE_URL: postgresql://${DB_USER:-relink}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-relink_prod}?schema=public + DATABASE_URL: postgresql://${DB_USER:-startover}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-startover_prod}?schema=public REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379 NODE_ENV: production labels: diff --git a/docker-compose.yml b/docker-compose.yml index db9a47e..2a6e7f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,13 +4,13 @@ services: ports: - "5432:5432" environment: - POSTGRES_USER: relink - POSTGRES_PASSWORD: relink_dev - POSTGRES_DB: relink_dev + POSTGRES_USER: startover + POSTGRES_PASSWORD: startover_dev + POSTGRES_DB: startover_dev volumes: - postgres_data:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U relink -d relink_dev"] + test: ["CMD-SHELL", "pg_isready -U startover -d startover_dev"] interval: 5s timeout: 5s retries: 5 @@ -20,11 +20,11 @@ services: ports: - "5433:5432" environment: - POSTGRES_USER: relink - POSTGRES_PASSWORD: relink_test - POSTGRES_DB: relink_test + POSTGRES_USER: startover + POSTGRES_PASSWORD: startover_test + POSTGRES_DB: startover_test healthcheck: - test: ["CMD-SHELL", "pg_isready -U relink -d relink_test"] + test: ["CMD-SHELL", "pg_isready -U startover -d startover_test"] interval: 5s timeout: 5s retries: 5 diff --git a/docs/analytics/event-schema.md b/docs/analytics/event-schema.md index 48ae28f..7b19a67 100644 --- a/docs/analytics/event-schema.md +++ b/docs/analytics/event-schema.md @@ -1,4 +1,4 @@ -# Re:Link 이벤트 스키마 정의서 +# Startover 이벤트 스키마 정의서 > 문서 코드: D004 > 버전: 1.0.0 @@ -23,7 +23,7 @@ ### 1-1. 목적 -이 문서는 Re:Link 플랫폼에서 `EventLog` 테이블에 저장되는 도메인 이벤트의 규격을 정의한다. +이 문서는 Startover 플랫폼에서 `EventLog` 테이블에 저장되는 도메인 이벤트의 규격을 정의한다. 이벤트 로그는 세 가지 목적으로 활용된다. diff --git a/docs/database/schema-prisma-draft.md b/docs/database/schema-prisma-draft.md index 0797af1..39f7d44 100644 --- a/docs/database/schema-prisma-draft.md +++ b/docs/database/schema-prisma-draft.md @@ -1,8 +1,8 @@ -# Re:Link `schema.prisma` 설계 초안 +# Startover `schema.prisma` 설계 초안 ## 목적 -이 문서는 Re:Link MVP의 데이터 모델을 실제 `schema.prisma`로 내리기 전에, 엔티티/필드/관계/인덱스/제약 조건을 고정하기 위한 초안이다. +이 문서는 Startover MVP의 데이터 모델을 실제 `schema.prisma`로 내리기 전에, 엔티티/필드/관계/인덱스/제약 조건을 고정하기 위한 초안이다. 대상 범위는 `assisted marketplace MVP`이며, 아래 원칙을 따른다. diff --git a/docs/master-data/beta-master-data.md b/docs/master-data/beta-master-data.md index d3b4157..dec7387 100644 --- a/docs/master-data/beta-master-data.md +++ b/docs/master-data/beta-master-data.md @@ -1,8 +1,8 @@ -# Re:Link 베타 마스터 데이터 초안 +# Startover 베타 마스터 데이터 초안 ## 목적 -이 문서는 Re:Link MVP 베타에서 사용할 지역/업종 마스터 데이터의 구조와 초기 seed 원칙을 정의한다. +이 문서는 Startover MVP 베타에서 사용할 지역/업종 마스터 데이터의 구조와 초기 seed 원칙을 정의한다. 핵심 목표는 아래 3가지다. diff --git a/docs/policies/contract-escrow-policy.md b/docs/policies/contract-escrow-policy.md index 0360e77..9364080 100644 --- a/docs/policies/contract-escrow-policy.md +++ b/docs/policies/contract-escrow-policy.md @@ -1,15 +1,15 @@ -# Re:Link 계약-에스크로-분쟁 정책서 +# Startover 계약-에스크로-분쟁 정책서 > 문서 버전: 1.0.0 > 기준일: 2026-03-07 -> 적용 범위: Re:Link MVP Phase 1 +> 적용 범위: Startover MVP Phase 1 > DRI: 대표 (제품 정책), FINANCE_OPERATOR (정산 실행), TRUST_OPERATOR (계약·검수·분쟁) --- ## 목적 -이 문서는 Re:Link 플랫폼에서 계약 생성, 전자서명 증적 수집, 에스크로 결제, 검수 절차, 정산 해제, 분쟁 처리, 환불, 수동 보정에 관한 운영 기준을 정의한다. 모든 개발 구현과 운영 의사결정은 이 문서를 최우선 기준으로 삼는다. +이 문서는 Startover 플랫폼에서 계약 생성, 전자서명 증적 수집, 에스크로 결제, 검수 절차, 정산 해제, 분쟁 처리, 환불, 수동 보정에 관한 운영 기준을 정의한다. 모든 개발 구현과 운영 의사결정은 이 문서를 최우선 기준으로 삼는다. --- @@ -91,7 +91,7 @@ DRAFT -> GENERATED -> SIGNING -> SIGNED -> IN_PROGRESS -> COMPLETED | 문서 버전 | `contractVersionId` | 서명 당시 적용된 `ContractVersion`의 ID | | 문서 해시 | `documentHash` | 서명 당시 문서의 SHA-256 해시 (저장 해시와 일치 여부 검증용) | | 증적 유형 | `evidenceType` | 서명 방식 코드 (아래 참조) | -| 제공자 코드 | `providerCode` | 서명 기술 제공자 (예: `RELINK_CHECKBOX`, `KAKAOPAY_SIGN`) | +| 제공자 코드 | `providerCode` | 서명 기술 제공자 (예: `STARTOVER_CHECKBOX`, `KAKAOPAY_SIGN`) | | 서명 해시 | `signatureHash` | 서명 행위 자체에 대한 해시 (재현 가능한 증적) | ### 2-2. MVP 서명 방식 @@ -582,4 +582,4 @@ MVP Phase 1에서 아래 기능은 feature flag 뒤에 두고 기본값은 비 --- -*이 문서는 Re:Link MVP 운영 기준 문서이며, 구현 코드보다 이 문서가 우선합니다. 정책 변경이 필요한 경우 이 문서를 먼저 개정하고 개정 이력을 기록한 뒤 코드 구현에 반영합니다.* +*이 문서는 Startover MVP 운영 기준 문서이며, 구현 코드보다 이 문서가 우선합니다. 정책 변경이 필요한 경우 이 문서를 먼저 개정하고 개정 이력을 기록한 뒤 코드 구현에 반영합니다.* diff --git a/docs/policies/data-exposure-policy.md b/docs/policies/data-exposure-policy.md index c73e44c..b051fad 100644 --- a/docs/policies/data-exposure-policy.md +++ b/docs/policies/data-exposure-policy.md @@ -1,6 +1,6 @@ # 폐업 정보 공개 정책서 -> Re:Link MVP — 정보 공개 기준 및 역할별 열람 권한 정책 +> Startover MVP — 정보 공개 기준 및 역할별 열람 권한 정책 > 문서 버전: v1.0.0 > 최초 작성: 2026-03-07 > 적용 범위: Phase 1 MVP 전체, Phase 2 데이터 구조 준비 포함 @@ -23,7 +23,7 @@ ## 1. 정보 공개 등급 체계 -Re:Link는 폐업자의 민감한 정보를 보호하면서 거래 목적에 맞는 정보만 단계적으로 공개하는 4단계 등급 체계를 운영한다. 폐업 등록 즉시 모든 정보가 공개되지 않으며, 매칭 진행 상황과 역할에 따라 정보 접근이 제한적으로 확대된다. +Startover는 폐업자의 민감한 정보를 보호하면서 거래 목적에 맞는 정보만 단계적으로 공개하는 4단계 등급 체계를 운영한다. 폐업 등록 즉시 모든 정보가 공개되지 않으며, 매칭 진행 상황과 역할에 따라 정보 접근이 제한적으로 확대된다. ### 1-1. 등급 정의 @@ -252,7 +252,7 @@ VENDOR_MANAGER의 경우 연락처는 운영자 중개 방식을 우선 사용 ### 5-1. 동의 수집 항목 -Re:Link는 `UserConsent` 모델을 통해 동의 유형별로 수집 시점, 버전, 철회 가능 여부를 관리한다. +Startover는 `UserConsent` 모델을 통해 동의 유형별로 수집 시점, 버전, 철회 가능 여부를 관리한다. | 동의 항목 | `consentType` 코드 | 수집 시점 | 철회 가능 여부 | |----------|-------------------|-----------|----------------| @@ -397,7 +397,7 @@ INTERNAL 정보에 접근하는 운영자 API는 아래 추가 보호를 적용 ### 8-1. PII 식별 및 분리 저장 -Re:Link에서 개인정보(PII)로 분류하는 항목과 저장 방식은 아래와 같다. +Startover에서 개인정보(PII)로 분류하는 항목과 저장 방식은 아래와 같다. | 분류 | 항목 | 저장 방식 | 접근 통제 | |------|------|----------|----------| @@ -407,11 +407,11 @@ Re:Link에서 개인정보(PII)로 분류하는 항목과 저장 방식은 아 | 위치 정보 | 상세 주소 | 암호화 저장 | MATCHED_ONLY 이상 | | 사업자 정보 | 사업자등록번호 | 평문 저장 (공개 정보) | MATCHED_ONLY 이상 | -사업자등록번호는 공개 정보이나, Re:Link 내에서는 계약 목적으로만 사용하며 검색 노출은 MATCHED_ONLY로 제한한다. +사업자등록번호는 공개 정보이나, Startover 내에서는 계약 목적으로만 사용하며 검색 노출은 MATCHED_ONLY로 제한한다. ### 8-2. 개인정보 수집 및 이용 목적 -Re:Link가 수집하는 개인정보는 아래 목적으로만 사용한다. +Startover가 수집하는 개인정보는 아래 목적으로만 사용한다. | 수집 목적 | 해당 항목 | 보관 기간 | |----------|----------|----------| @@ -446,7 +446,7 @@ B2B 알림 상품 출시 시(Phase 2) 업체에 대한 정보 제공 범위가 ### 8-5. 개인정보 처리 위탁 -Re:Link가 개인정보 처리를 위탁하는 수탁자와 위탁 업무 범위는 개인정보 처리방침에 공개한다. +Startover가 개인정보 처리를 위탁하는 수탁자와 위탁 업무 범위는 개인정보 처리방침에 공개한다. | 수탁자 | 위탁 업무 | |--------|----------| @@ -497,4 +497,4 @@ Re:Link가 개인정보 처리를 위탁하는 수탁자와 위탁 업무 범위 |------|------|----------|--------| | v1.0.0 | 2026-03-07 | 최초 작성 | 대표 + CTO | -> 이 문서는 Re:Link 운영 및 개발의 기준 정책 문서입니다. 변경 시 DRI(대표)의 승인을 거쳐야 하며, 변경 이력을 반드시 기록해야 합니다. +> 이 문서는 Startover 운영 및 개발의 기준 정책 문서입니다. 변경 시 DRI(대표)의 승인을 거쳐야 하며, 변경 이력을 반드시 기록해야 합니다. diff --git a/docs/policies/subsidy-policy.md b/docs/policies/subsidy-policy.md index b77bed5..3c6867b 100644 --- a/docs/policies/subsidy-policy.md +++ b/docs/policies/subsidy-policy.md @@ -1,4 +1,4 @@ -# Re:Link 지원금 대행 정책서 +# Startover 지원금 대행 정책서 > 문서 코드: D001 > 버전: 1.0.0 @@ -26,7 +26,7 @@ ### 1-1. 포지션 원칙 -Re:Link의 지원금 서비스는 **정부 사업의 경쟁자가 아닌 신청 보조 파트너**다. +Startover의 지원금 서비스는 **정부 사업의 경쟁자가 아닌 신청 보조 파트너**다. 플랫폼은 폐업자가 정부 지원금을 받기 위해 스스로 준비해야 할 서류와 절차를 안내하고, 운영자가 서류 구비 상태를 검토하여 제출 준비 완료 여부를 판정하는 역할까지만 담당한다. 정부 기관을 대신하여 자동으로 서류를 제출하거나, 심사 결과에 영향을 주는 행위는 MVP에서 하지 않는다. @@ -498,15 +498,15 @@ NOT_ELIGIBLE 판별 시: ELIGIBILITY_CHECKED 상태에서 케이스를 CLOSED **[이용자 안내 및 면책 고지]** -Re:Link 지원금 서비스는 소상공인의 정부 지원금 신청 절차를 안내하는 보조 서비스입니다. +Startover 지원금 서비스는 소상공인의 정부 지원금 신청 절차를 안내하는 보조 서비스입니다. -1. Re:Link는 정부 기관(소상공인시장진흥공단 등)을 대리하지 않으며, 지원금 심사 및 승인 결과에 영향을 주지 않습니다. +1. Startover는 정부 기관(소상공인시장진흥공단 등)을 대리하지 않으며, 지원금 심사 및 승인 결과에 영향을 주지 않습니다. 2. 플랫폼이 제공하는 자격 판별 결과는 입력 정보 기반의 안내용이며, 법적 효력이 없습니다. 3. 최종 지원금 심사 및 승인 여부는 소상공인시장진흥공단의 결정에 따릅니다. -4. Re:Link는 지원금 수령을 보장하거나 확약하지 않습니다. +4. Startover는 지원금 수령을 보장하거나 확약하지 않습니다. 5. 제출 서류의 진실성과 정확성에 대한 책임은 신청자 본인에게 있습니다. 6. 허위 서류 제출 또는 부정 수급 시 관련 법령에 따라 환수 및 제재를 받을 수 있습니다. -7. Re:Link는 세무·법률 전문가의 의견을 대체하는 자문을 제공하지 않습니다. +7. Startover는 세무·법률 전문가의 의견을 대체하는 자문을 제공하지 않습니다. ### 8-2. 정부 승인 불보장 원칙 diff --git a/package.json b/package.json index f3e1531..a3ccda1 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ "test": "turbo run test", "type-check": "turbo run type-check", "clean": "turbo run clean && rm -rf node_modules", - "db:generate": "pnpm --filter @relink/database prisma generate", - "db:push": "pnpm --filter @relink/database prisma db push", - "db:migrate": "pnpm --filter @relink/database prisma migrate dev", + "db:generate": "pnpm --filter @startover/database prisma generate", + "db:push": "pnpm --filter @startover/database prisma db push", + "db:migrate": "pnpm --filter @startover/database prisma migrate dev", "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"" }, "devDependencies": { diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 121dc22..e906cc3 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/analytics", + "name": "@startover/analytics", "version": "0.0.1", "private": false, "type": "module", @@ -19,7 +19,7 @@ "clean": "rm -rf dist" }, "dependencies": { - "@relink/shared": "workspace:*" + "@startover/shared": "workspace:*" }, "devDependencies": { "tsup": "^8.3.5", diff --git a/packages/analytics/src/index.ts b/packages/analytics/src/index.ts index 8b30635..e5ac26b 100644 --- a/packages/analytics/src/index.ts +++ b/packages/analytics/src/index.ts @@ -1,5 +1,5 @@ /** - * @relink/analytics - Event schemas and KPI calculations + * @startover/analytics - Event schemas and KPI calculations */ export type { AnalyticsEvent } from './event.js'; diff --git a/packages/analytics/tsup.config.ts b/packages/analytics/tsup.config.ts index 7488aa1..346d6b5 100644 --- a/packages/analytics/tsup.config.ts +++ b/packages/analytics/tsup.config.ts @@ -6,5 +6,5 @@ export default defineConfig({ dts: true, clean: true, sourcemap: true, - external: ['@relink/shared'], + external: ['@startover/shared'], }); diff --git a/packages/application/package.json b/packages/application/package.json index d37ef71..46fc9b2 100644 --- a/packages/application/package.json +++ b/packages/application/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/application", + "name": "@startover/application", "version": "0.0.1", "private": false, "type": "module", @@ -19,8 +19,8 @@ "clean": "rm -rf dist" }, "dependencies": { - "@relink/domain": "workspace:*", - "@relink/shared": "workspace:*" + "@startover/domain": "workspace:*", + "@startover/shared": "workspace:*" }, "devDependencies": { "tsup": "^8.3.5", diff --git a/packages/application/src/index.ts b/packages/application/src/index.ts index baa6c2c..987cce3 100644 --- a/packages/application/src/index.ts +++ b/packages/application/src/index.ts @@ -1,5 +1,5 @@ /** - * @relink/application - Use cases and application services + * @startover/application - Use cases and application services * * Orchestrates domain entities and defines application-level business rules. */ diff --git a/packages/application/src/use-case.ts b/packages/application/src/use-case.ts index 898ef05..4e5b1dc 100644 --- a/packages/application/src/use-case.ts +++ b/packages/application/src/use-case.ts @@ -1,4 +1,4 @@ -import type { Result } from '@relink/shared'; +import type { Result } from '@startover/shared'; /** * Base use case interface. All application use cases implement this. diff --git a/packages/application/tsup.config.ts b/packages/application/tsup.config.ts index 798d7cd..7a0a01f 100644 --- a/packages/application/tsup.config.ts +++ b/packages/application/tsup.config.ts @@ -6,5 +6,5 @@ export default defineConfig({ dts: true, clean: true, sourcemap: true, - external: ['@relink/domain', '@relink/shared'], + external: ['@startover/domain', '@startover/shared'], }); diff --git a/packages/database/.env b/packages/database/.env index 09af013..60d18d8 100644 --- a/packages/database/.env +++ b/packages/database/.env @@ -1 +1 @@ -DATABASE_URL="postgresql://relink:relink_dev@localhost:5432/relink_dev" +DATABASE_URL="postgresql://startover:startover_dev@localhost:5432/startover_dev" diff --git a/packages/database/package.json b/packages/database/package.json index b5fffe7..65bb8a4 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/database", + "name": "@startover/database", "version": "0.0.1", "private": false, "type": "module", diff --git a/packages/database/prisma/schema.prisma b/packages/database/prisma/schema.prisma index 87215da..8d3f6e4 100644 --- a/packages/database/prisma/schema.prisma +++ b/packages/database/prisma/schema.prisma @@ -1,5 +1,5 @@ // ============================================================================= -// Re:Link MVP - Prisma Schema +// Startover MVP - Prisma Schema // ============================================================================= // 설계 원칙: // - 내부 PK: BigInt @id @default(autoincrement()) diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts index 12d90be..c4a4cf8 100644 --- a/packages/database/src/index.ts +++ b/packages/database/src/index.ts @@ -1,5 +1,5 @@ /** - * @relink/database - Prisma client and database utilities + * @startover/database - Prisma client and database utilities */ export { createPrismaClient } from './client.js'; diff --git a/packages/database/src/test-helpers.ts b/packages/database/src/test-helpers.ts index 0f5f511..62f4543 100644 --- a/packages/database/src/test-helpers.ts +++ b/packages/database/src/test-helpers.ts @@ -8,7 +8,7 @@ const SCHEMA_PATH = resolve(__dirname, '../prisma/schema.prisma'); const TEST_DATABASE_URL = process.env['DATABASE_TEST_URL'] ?? - 'postgresql://relink:relink_test@localhost:5433/relink_test'; + 'postgresql://startover:startover_test@localhost:5433/startover_test'; let testClient: PrismaClient | null = null; @@ -26,10 +26,13 @@ export function getTestPrismaClient(): PrismaClient { } export async function setupTestDatabase(): Promise { - execSync(`DATABASE_URL="${TEST_DATABASE_URL}" npx prisma db push --schema="${SCHEMA_PATH}" --skip-generate --accept-data-loss`, { - stdio: 'pipe', - cwd: resolve(__dirname, '..'), - }); + execSync( + `DATABASE_URL="${TEST_DATABASE_URL}" npx prisma db push --schema="${SCHEMA_PATH}" --skip-generate --accept-data-loss`, + { + stdio: 'pipe', + cwd: resolve(__dirname, '..'), + }, + ); const client = getTestPrismaClient(); await client.$connect(); diff --git a/packages/domain/package.json b/packages/domain/package.json index 0ce0e88..b407455 100644 --- a/packages/domain/package.json +++ b/packages/domain/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/domain", + "name": "@startover/domain", "version": "0.0.1", "private": false, "type": "module", @@ -19,7 +19,7 @@ "clean": "rm -rf dist" }, "dependencies": { - "@relink/shared": "workspace:*" + "@startover/shared": "workspace:*" }, "devDependencies": { "tsup": "^8.3.5", diff --git a/packages/domain/src/contract/check-idempotency.ts b/packages/domain/src/contract/check-idempotency.ts index 6ceb58a..2469349 100644 --- a/packages/domain/src/contract/check-idempotency.ts +++ b/packages/domain/src/contract/check-idempotency.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface CheckIdempotencyInput { readonly idempotencyKey: string; diff --git a/packages/domain/src/contract/create-contract.ts b/packages/domain/src/contract/create-contract.ts index c6a4b34..0ebf519 100644 --- a/packages/domain/src/contract/create-contract.ts +++ b/packages/domain/src/contract/create-contract.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export type ContractType = 'ACQUISITION' | 'DEMOLITION' | 'INTERIOR'; @@ -16,9 +16,7 @@ export interface ContractDraft { readonly policyVersionId: string; } -export function createContract( - input: CreateContractInput, -): Result { +export function createContract(input: CreateContractInput): Result { // U021: 수락된 매칭만 계약 생성 가능 if (input.matchRequestStatus !== 'ACCEPTED') { return failure( diff --git a/packages/domain/src/contract/open-dispute.ts b/packages/domain/src/contract/open-dispute.ts index 2de9724..89e0ad3 100644 --- a/packages/domain/src/contract/open-dispute.ts +++ b/packages/domain/src/contract/open-dispute.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface OpenDisputeInput { readonly currentEscrowStatus: string; @@ -17,9 +17,7 @@ const DISPUTABLE_CONTRACT_STATUSES = new Set(['ACTIVE', 'SIGNED']); const DISPUTABLE_ESCROW_STATUSES = new Set(['HOLDING', 'RELEASE_REVIEW']); // U025: 분쟁이 열리면 에스크로는 DISPUTED로 전환 -export function openDispute( - input: OpenDisputeInput, -): Result { +export function openDispute(input: OpenDisputeInput): Result { if (!DISPUTABLE_CONTRACT_STATUSES.has(input.contractStatus)) { return failure( appError('INVALID_CONTRACT_STATUS', '활성 상태의 계약만 분쟁을 열 수 있습니다.', { @@ -30,9 +28,13 @@ export function openDispute( if (!DISPUTABLE_ESCROW_STATUSES.has(input.currentEscrowStatus)) { return failure( - appError('INVALID_ESCROW_STATUS', 'HOLDING 또는 RELEASE_REVIEW 상태에서만 분쟁을 열 수 있습니다.', { - currentEscrowStatus: input.currentEscrowStatus, - }), + appError( + 'INVALID_ESCROW_STATUS', + 'HOLDING 또는 RELEASE_REVIEW 상태에서만 분쟁을 열 수 있습니다.', + { + currentEscrowStatus: input.currentEscrowStatus, + }, + ), ); } diff --git a/packages/domain/src/contract/release-escrow.ts b/packages/domain/src/contract/release-escrow.ts index c3c2804..6baa171 100644 --- a/packages/domain/src/contract/release-escrow.ts +++ b/packages/domain/src/contract/release-escrow.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface ReleaseEscrowInput { readonly currentEscrowStatus: string; @@ -10,9 +10,7 @@ export interface ReleaseEscrowResult { readonly escrowStatus: 'RELEASE_REVIEW'; } -export function releaseEscrow( - input: ReleaseEscrowInput, -): Result { +export function releaseEscrow(input: ReleaseEscrowInput): Result { // U023: HOLDING 상태에서만 정산 해제 가능 if (input.currentEscrowStatus !== 'HOLDING') { return failure( @@ -31,9 +29,7 @@ export function releaseEscrow( // U026: 분쟁이 열려있으면 정산 해제 차단 if (input.hasOpenDispute) { - return failure( - appError('DISPUTE_OPEN', '열린 분쟁이 있어 정산 해제가 차단됩니다.'), - ); + return failure(appError('DISPUTE_OPEN', '열린 분쟁이 있어 정산 해제가 차단됩니다.')); } return success({ diff --git a/packages/domain/src/index.ts b/packages/domain/src/index.ts index b4b736d..8d35894 100644 --- a/packages/domain/src/index.ts +++ b/packages/domain/src/index.ts @@ -1,5 +1,5 @@ /** - * @relink/domain - Pure TypeScript domain layer + * @startover/domain - Pure TypeScript domain layer * * Contains entities, value objects, and business rules. * No external dependencies allowed. diff --git a/packages/domain/src/matching/accept-match-request.ts b/packages/domain/src/matching/accept-match-request.ts index aebd6f1..546868c 100644 --- a/packages/domain/src/matching/accept-match-request.ts +++ b/packages/domain/src/matching/accept-match-request.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export type MatchRequestStatus = | 'OPEN' @@ -26,9 +26,13 @@ export function acceptMatchRequest( if (!acceptableStatuses.includes(input.currentStatus)) { return failure( - appError('INVALID_STATUS_TRANSITION', 'OPEN 또는 REVIEWING 상태의 요청만 수락할 수 있습니다.', { - currentStatus: input.currentStatus, - }), + appError( + 'INVALID_STATUS_TRANSITION', + 'OPEN 또는 REVIEWING 상태의 요청만 수락할 수 있습니다.', + { + currentStatus: input.currentStatus, + }, + ), ); } diff --git a/packages/domain/src/matching/create-match-request.ts b/packages/domain/src/matching/create-match-request.ts index a50159b..bd18c06 100644 --- a/packages/domain/src/matching/create-match-request.ts +++ b/packages/domain/src/matching/create-match-request.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export type MatchType = 'ACQUISITION' | 'DEMOLITION' | 'INTERIOR'; export type MatchSourceType = 'USER_REQUEST' | 'OPERATOR_RECOMMENDATION' | 'SYSTEM_MATCH'; diff --git a/packages/domain/src/store/create-store-draft.ts b/packages/domain/src/store/create-store-draft.ts index 2f449f7..39f3994 100644 --- a/packages/domain/src/store/create-store-draft.ts +++ b/packages/domain/src/store/create-store-draft.ts @@ -1,6 +1,10 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; import { StoreLease, type StoreLeaseInput, type StoreLeaseProps } from './store-lease.js'; -import { StoreFacility, type StoreFacilityInput, type StoreFacilityProps } from './store-facility.js'; +import { + StoreFacility, + type StoreFacilityInput, + type StoreFacilityProps, +} from './store-facility.js'; // --------------------------------------------------------------------------- // Dependency interfaces (DIP) @@ -92,9 +96,7 @@ export function createStoreDraft( if (fieldErrors.length === 1) { const err = fieldErrors[0]!; - return failure( - appError('VALIDATION_ERROR', err.message, { field: err.field }), - ); + return failure(appError('VALIDATION_ERROR', err.message, { field: err.field })); } if (fieldErrors.length > 1) { diff --git a/packages/domain/src/store/publish-store.ts b/packages/domain/src/store/publish-store.ts index 680e207..cbb7b78 100644 --- a/packages/domain/src/store/publish-store.ts +++ b/packages/domain/src/store/publish-store.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface PublishStoreInput { readonly currentReviewStatus: string; @@ -11,9 +11,7 @@ export interface PublishStoreResult { readonly policyVersionId: string; } -export function publishStore( - input: PublishStoreInput, -): Result { +export function publishStore(input: PublishStoreInput): Result { if (input.currentReviewStatus !== 'APPROVED') { return failure( appError('NOT_APPROVED', '승인된 매장만 공개할 수 있습니다.', { diff --git a/packages/domain/src/store/review-store.ts b/packages/domain/src/store/review-store.ts index df35ce1..8e66cc1 100644 --- a/packages/domain/src/store/review-store.ts +++ b/packages/domain/src/store/review-store.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export type ReviewDecision = 'APPROVED' | 'REJECTED'; export type ReviewableStatus = 'DRAFT' | 'SUBMITTED' | 'REVIEWING' | 'APPROVED' | 'REJECTED'; @@ -17,9 +17,7 @@ export interface ReviewStoreResult { readonly memo?: string; } -export function reviewStore( - input: ReviewStoreInput, -): Result { +export function reviewStore(input: ReviewStoreInput): Result { if (input.currentReviewStatus !== 'SUBMITTED') { return failure( appError('INVALID_STATUS_TRANSITION', 'SUBMITTED 상태의 매장만 검토할 수 있습니다.', { @@ -35,9 +33,7 @@ export function reviewStore( ); } if (!input.memo?.trim()) { - return failure( - appError('VALIDATION_ERROR', '반려 시 메모는 필수입니다.', { field: 'memo' }), - ); + return failure(appError('VALIDATION_ERROR', '반려 시 메모는 필수입니다.', { field: 'memo' })); } } diff --git a/packages/domain/src/store/store-facility.ts b/packages/domain/src/store/store-facility.ts index dcaf3bb..e0fc59e 100644 --- a/packages/domain/src/store/store-facility.ts +++ b/packages/domain/src/store/store-facility.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface StoreFacilityInput { readonly exclusiveAreaSqm: number; @@ -28,7 +28,9 @@ export const StoreFacility = { create(input: StoreFacilityInput): Result { if (input.exclusiveAreaSqm <= 0) { return failure( - appError('VALIDATION_ERROR', '전용면적은 0보다 커야 합니다.', { field: 'exclusiveAreaSqm' }), + appError('VALIDATION_ERROR', '전용면적은 0보다 커야 합니다.', { + field: 'exclusiveAreaSqm', + }), ); } diff --git a/packages/domain/src/store/store-lease.ts b/packages/domain/src/store/store-lease.ts index 11a826d..34de0ef 100644 --- a/packages/domain/src/store/store-lease.ts +++ b/packages/domain/src/store/store-lease.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export interface StoreLeaseInput { readonly depositAmount: number; @@ -42,7 +42,9 @@ export const StoreLease = { if (input.maintenanceFeeAmount !== undefined && input.maintenanceFeeAmount < 0) { return failure( - appError('VALIDATION_ERROR', '관리비는 0 이상이어야 합니다.', { field: 'maintenanceFeeAmount' }), + appError('VALIDATION_ERROR', '관리비는 0 이상이어야 합니다.', { + field: 'maintenanceFeeAmount', + }), ); } diff --git a/packages/domain/src/subsidy/advance-subsidy-to-ready.ts b/packages/domain/src/subsidy/advance-subsidy-to-ready.ts index 8a11ab8..049609c 100644 --- a/packages/domain/src/subsidy/advance-subsidy-to-ready.ts +++ b/packages/domain/src/subsidy/advance-subsidy-to-ready.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export type ChecklistItemStatus = 'PENDING' | 'CHECKED' | 'NOT_APPLICABLE'; @@ -25,9 +25,13 @@ export function advanceSubsidyToReady( // DOCUMENTS_PENDING 상태에서만 READY_TO_SUBMIT으로 전이 가능 if (input.currentStatus !== 'DOCUMENTS_PENDING') { return failure( - appError('INVALID_STATUS_TRANSITION', 'DOCUMENTS_PENDING 상태에서만 제출 준비로 전환할 수 있습니다.', { - currentStatus: input.currentStatus, - }), + appError( + 'INVALID_STATUS_TRANSITION', + 'DOCUMENTS_PENDING 상태에서만 제출 준비로 전환할 수 있습니다.', + { + currentStatus: input.currentStatus, + }, + ), ); } diff --git a/packages/domain/src/subsidy/create-subsidy-case.ts b/packages/domain/src/subsidy/create-subsidy-case.ts index b5f96e8..7f3deff 100644 --- a/packages/domain/src/subsidy/create-subsidy-case.ts +++ b/packages/domain/src/subsidy/create-subsidy-case.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export type SubsidyCaseStatus = | 'DRAFT' @@ -45,18 +45,20 @@ export function createSubsidyCase( if (!isPublished && !isReviewable) { return failure( - appError('STORE_NOT_ELIGIBLE', '공개되었거나 검토 가능한 매장만 지원금 케이스를 시작할 수 있습니다.', { - reviewStatus: input.storeReviewStatus, - publicationStatus: input.storePublicationStatus, - }), + appError( + 'STORE_NOT_ELIGIBLE', + '공개되었거나 검토 가능한 매장만 지원금 케이스를 시작할 수 있습니다.', + { + reviewStatus: input.storeReviewStatus, + publicationStatus: input.storePublicationStatus, + }, + ), ); } // U017: 체크리스트 항목이 없으면 케이스를 생성할 수 없다 if (input.checklistItems.length === 0) { - return failure( - appError('EMPTY_CHECKLIST', '체크리스트 항목이 최소 1개 이상 있어야 합니다.'), - ); + return failure(appError('EMPTY_CHECKLIST', '체크리스트 항목이 최소 1개 이상 있어야 합니다.')); } return success({ diff --git a/packages/domain/src/subsidy/review-subsidy-case.ts b/packages/domain/src/subsidy/review-subsidy-case.ts index 317a9fa..9fd64cc 100644 --- a/packages/domain/src/subsidy/review-subsidy-case.ts +++ b/packages/domain/src/subsidy/review-subsidy-case.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export type SubsidyReviewDecision = 'APPROVED' | 'REJECTED'; @@ -22,9 +22,13 @@ export function reviewSubsidyCase( ): Result { if (!REVIEWABLE_STATUSES.has(input.currentStatus)) { return failure( - appError('INVALID_STATUS_TRANSITION', 'SUBMITTED 또는 REVIEWING 상태의 케이스만 검토할 수 있습니다.', { - currentStatus: input.currentStatus, - }), + appError( + 'INVALID_STATUS_TRANSITION', + 'SUBMITTED 또는 REVIEWING 상태의 케이스만 검토할 수 있습니다.', + { + currentStatus: input.currentStatus, + }, + ), ); } diff --git a/packages/domain/src/vendor/apply-vendor-certification.ts b/packages/domain/src/vendor/apply-vendor-certification.ts index 67bcf57..cb1ffa7 100644 --- a/packages/domain/src/vendor/apply-vendor-certification.ts +++ b/packages/domain/src/vendor/apply-vendor-certification.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; export type VendorCertificationStatus = | 'APPLIED' @@ -29,9 +29,7 @@ export function applyVendorCertification( input: ApplyVendorCertificationInput, ): Result { if (!input.businessName.trim()) { - return failure( - appError('VALIDATION_ERROR', '업체명은 필수입니다.', { field: 'businessName' }), - ); + return failure(appError('VALIDATION_ERROR', '업체명은 필수입니다.', { field: 'businessName' })); } if (!input.contactName.trim()) { @@ -41,9 +39,7 @@ export function applyVendorCertification( } if (!input.hasBusinessRegistration) { - return failure( - appError('MISSING_BUSINESS_REGISTRATION', '사업자등록증이 필요합니다.'), - ); + return failure(appError('MISSING_BUSINESS_REGISTRATION', '사업자등록증이 필요합니다.')); } if (input.coverageRegionCount === 0) { diff --git a/packages/domain/src/vendor/approve-vendor-certification.ts b/packages/domain/src/vendor/approve-vendor-certification.ts index ead351c..f61f4e5 100644 --- a/packages/domain/src/vendor/approve-vendor-certification.ts +++ b/packages/domain/src/vendor/approve-vendor-certification.ts @@ -1,4 +1,4 @@ -import { success, failure, appError, type Result, type AppError } from '@relink/shared'; +import { success, failure, appError, type Result, type AppError } from '@startover/shared'; import type { VendorCertificationStatus } from './apply-vendor-certification.js'; @@ -24,9 +24,13 @@ export function approveVendorCertification( ): Result { if (!REVIEWABLE_STATUSES.has(input.currentStatus)) { return failure( - appError('INVALID_STATUS_TRANSITION', 'APPLIED 또는 REVIEWING 상태의 인증만 검토할 수 있습니다.', { - currentStatus: input.currentStatus, - }), + appError( + 'INVALID_STATUS_TRANSITION', + 'APPLIED 또는 REVIEWING 상태의 인증만 검토할 수 있습니다.', + { + currentStatus: input.currentStatus, + }, + ), ); } @@ -41,7 +45,10 @@ export function approveVendorCertification( // U018: 서비스 권역이 없으면 승인 불가 if (input.coverageRegionCount === 0) { return failure( - appError('MISSING_COVERAGE_REGION', '서비스 권역이 최소 1개 이상 있어야 승인할 수 있습니다.'), + appError( + 'MISSING_COVERAGE_REGION', + '서비스 권역이 최소 1개 이상 있어야 승인할 수 있습니다.', + ), ); } } diff --git a/packages/infrastructure/package.json b/packages/infrastructure/package.json index 2f059f0..9e7b4c8 100644 --- a/packages/infrastructure/package.json +++ b/packages/infrastructure/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/infrastructure", + "name": "@startover/infrastructure", "version": "0.0.1", "private": false, "type": "module", @@ -20,10 +20,10 @@ }, "dependencies": { "@prisma/client": "^6.1.0", - "@relink/application": "workspace:*", - "@relink/database": "workspace:*", - "@relink/domain": "workspace:*", - "@relink/shared": "workspace:*" + "@startover/application": "workspace:*", + "@startover/database": "workspace:*", + "@startover/domain": "workspace:*", + "@startover/shared": "workspace:*" }, "devDependencies": { "@types/node": "^22.10.2", diff --git a/packages/infrastructure/tsup.config.ts b/packages/infrastructure/tsup.config.ts index d4bdc85..a3aab7c 100644 --- a/packages/infrastructure/tsup.config.ts +++ b/packages/infrastructure/tsup.config.ts @@ -6,5 +6,5 @@ export default defineConfig({ dts: true, clean: true, sourcemap: true, - external: ['@relink/application', '@relink/domain', '@relink/shared'], + external: ['@startover/application', '@startover/domain', '@startover/shared'], }); diff --git a/packages/shared/package.json b/packages/shared/package.json index 2d55fdc..0930a4f 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/shared", + "name": "@startover/shared", "version": "0.0.1", "private": false, "type": "module", diff --git a/packages/shared/src/constants.ts b/packages/shared/src/constants.ts index 1e2e4b3..4718699 100644 --- a/packages/shared/src/constants.ts +++ b/packages/shared/src/constants.ts @@ -1,4 +1,4 @@ -export const APP_NAME = 'Re:Link' as const; +export const APP_NAME = 'Startover' as const; export const PAGINATION = { DEFAULT_PAGE: 1, diff --git a/packages/shared/src/test-utils/index.ts b/packages/shared/src/test-utils/index.ts index 925f6d9..a692d78 100644 --- a/packages/shared/src/test-utils/index.ts +++ b/packages/shared/src/test-utils/index.ts @@ -25,7 +25,7 @@ export function expectFailure(result: Result): E { * Create a test database URL with a unique database name for isolation. */ export function createTestDatabaseUrl(baseUrl?: string): string { - const base = baseUrl ?? 'postgresql://relink:relink_test@localhost:5433/relink_test'; + const base = baseUrl ?? 'postgresql://startover:startover_test@localhost:5433/startover_test'; return base; } diff --git a/packages/ui/package.json b/packages/ui/package.json index ac6fb6a..987da8a 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,5 +1,5 @@ { - "name": "@relink/ui", + "name": "@startover/ui", "version": "0.0.1", "private": false, "type": "module", diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index af919d0..fd35218 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,5 +1,5 @@ /** - * @relink/ui - Shared UI components + * @startover/ui - Shared UI components */ export { Button } from './button.js'; diff --git a/plan.md b/plan.md index 7e734bc..dc8f20d 100644 --- a/plan.md +++ b/plan.md @@ -1,8 +1,8 @@ -# 프로젝트: Re:Link MVP 구현 plan +# 프로젝트: Startover MVP 구현 plan ## 개요 -Re:Link MVP의 목표는 폐업자 매장 정보를 기반으로 창업자, 철거업체, 인테리어업체를 연결하는 `assisted marketplace`를 베타 수준으로 출시하는 것이다. +Startover MVP의 목표는 폐업자 매장 정보를 기반으로 창업자, 철거업체, 인테리어업체를 연결하는 `assisted marketplace`를 베타 수준으로 출시하는 것이다. 이 plan은 `DEVELOPMENT_PLAN.md`를 실제 구현 단위로 쪼갠 작업 문서다. 정책 문서, 구조 작업, 테스트 시나리오, 에러 시나리오, 우선순위, 진행 상태를 한 곳에서 관리한다. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f0d32b..d861d6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,10 +41,10 @@ importers: apps/admin: dependencies: - '@relink/shared': + '@startover/shared': specifier: workspace:* version: link:../../packages/shared - '@relink/ui': + '@startover/ui': specifier: workspace:* version: link:../../packages/ui next: @@ -93,19 +93,19 @@ importers: '@prisma/client': specifier: ^6.1.0 version: 6.19.2(prisma@6.19.2)(typescript@5.9.3) - '@relink/database': + '@startover/database': specifier: workspace:* version: link:../../packages/database - '@relink/domain': + '@startover/domain': specifier: workspace:* version: link:../../packages/domain - '@relink/infrastructure': + '@startover/infrastructure': specifier: workspace:* version: link:../../packages/infrastructure - '@relink/shared': + '@startover/shared': specifier: workspace:* version: link:../../packages/shared - '@relink/ui': + '@startover/ui': specifier: workspace:* version: link:../../packages/ui argon2: @@ -160,7 +160,7 @@ importers: packages/analytics: dependencies: - '@relink/shared': + '@startover/shared': specifier: workspace:* version: link:../shared devDependencies: @@ -176,10 +176,10 @@ importers: packages/application: dependencies: - '@relink/domain': + '@startover/domain': specifier: workspace:* version: link:../domain - '@relink/shared': + '@startover/shared': specifier: workspace:* version: link:../shared devDependencies: @@ -220,7 +220,7 @@ importers: packages/domain: dependencies: - '@relink/shared': + '@startover/shared': specifier: workspace:* version: link:../shared devDependencies: @@ -239,16 +239,16 @@ importers: '@prisma/client': specifier: ^6.1.0 version: 6.19.2(prisma@6.19.2)(typescript@5.9.3) - '@relink/application': + '@startover/application': specifier: workspace:* version: link:../application - '@relink/database': + '@startover/database': specifier: workspace:* version: link:../database - '@relink/domain': + '@startover/domain': specifier: workspace:* version: link:../domain - '@relink/shared': + '@startover/shared': specifier: workspace:* version: link:../shared devDependencies: