Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
46a30e4
feat(project): 프로젝트 리소스 한도 조회 유즈케이스 추가
jmj732 Mar 4, 2026
96a8730
refactor(project): project usecase export에 GetProjectResourceLimitUse…
jmj732 Mar 4, 2026
2e4aa59
feat(project): 프로젝트 리소스 한도 응답 스키마 추가
jmj732 Mar 4, 2026
45dbb67
feat(project): 리소스 한도 조회용 owner 권한 예외 추가
jmj732 Mar 4, 2026
abada41
feat(api): 프로젝트 리소스 한도 조회 엔드포인트에 owner 검증 연동
jmj732 Mar 4, 2026
d22c197
test(project): 리소스 한도 조회 유즈케이스 owner 검증 테스트 추가
jmj732 Mar 4, 2026
47a02ed
test(api): 리소스 한도 조회 엔드포인트 계약 테스트 추가
jmj732 Mar 4, 2026
f7f118c
docs(api): resource-limit 엔드포인트 권한 및 에러 문서화
jmj732 Mar 4, 2026
650d4b9
docs(api): API 경로 /v1 제거 및 owner/resource-limit 문서 갱신
jmj732 Mar 4, 2026
98b2b1a
refactor(api): 메인 라우터를 routes_api_router로 연결
jmj732 Mar 4, 2026
2540777
refactor(api): routes 라우터 조립 변수명을 routes_api_router로 변경
jmj732 Mar 4, 2026
2c30670
refactor(api): available/owner/resource-limit 응답을 SuccessResponse로 통일
jmj732 Mar 4, 2026
dd2e7fc
chore(config): app_version 기본값을 0.0.1로 변경
jmj732 Mar 4, 2026
3ec0397
test(api): available/owner 엔드포인트 테스트를 SuccessResponse 기준으로 수정
jmj732 Mar 4, 2026
fa71479
test(api): DNS 엔드포인트 테스트 import를 routes 경로로 변경
jmj732 Mar 4, 2026
f5c7589
test(api): 포트 엔드포인트 테스트 import를 routes 경로로 변경
jmj732 Mar 4, 2026
b4ea361
test(api): 프로젝트 엔드포인트 테스트 import 및 resource-limit 검증 갱신
jmj732 Mar 4, 2026
6125556
Merge pull request #19 from M-ADP/feature/MADP-120
pdh0128 Mar 5, 2026
c56bbd9
chore(github workerflow): deploy, rollback dev
pdh0128 Mar 5, 2026
d9b7cbf
chore(github workerflow): dockerfile
pdh0128 Mar 5, 2026
3552962
fix(env): application server config env 경로 수ㅓㅇ
pdh0128 Mar 5, 2026
afd7999
refactor(env): vault env 상수화
pdh0128 Mar 5, 2026
ba01c9e
fix(db): password, usesr encoding
pdh0128 Mar 5, 2026
73e0c5f
feat(env): 로그 추가
pdh0128 Mar 5, 2026
f05389b
fix(external): user id string 변환
pdh0128 Mar 5, 2026
8e0141e
fix(exception): resource server 통신 예외 추가
pdh0128 Mar 5, 2026
11e8e0e
fix(config): PROJECT_LIMIT 전역변수 제거 및 config 사용
pdh0128 Mar 5, 2026
bfe1166
fix(http client): body 소비
pdh0128 Mar 6, 2026
c14d87c
fix(resource manager): porject id int -> str
pdh0128 Mar 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/deploy-dev.yaml.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Deploy to Dev

on:
push:
branches:
- 'dev-*' # dev-0.1.2 등 버전 브랜치 푸시 시 동작

jobs:
call-dev:
permissions:
contents: write
packages: write
pull-requests: write
uses: M-ADP/M-ADP-GITHUB-ACTIONS/.github/workflows/dev.yaml@master
with:
deploy_repo: "M-ADP/M-ADP-ARGOCD"
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
23 changes: 23 additions & 0 deletions .github/workflows/rollback-dev.yaml.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Rollback Dev Environment

on:
workflow_dispatch:
inputs:
version:
description: 'Deploy version to restore (e.g. v1.0.0, hotfix-1)'
required: true
type: string

jobs:
call-rollback:
permissions:
contents: write
packages: write
pull-requests: write
uses: M-ADP/M-ADP-GITHUB-ACTIONS/.github/workflows/rollback.yaml@master
with:
target_env: "dev"
target_version: ${{ inputs.version }}
deploy_repo: "M-ADP/M-ADP-ARGOCD"
secrets:
PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.12-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application source
COPY . .

ENV TZ=Asia/Seoul

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
135 changes: 93 additions & 42 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# MADP Project Service API 문서

## 요약
- Base Path는 `/v1`이며 Health Check는 `/health`입니다.
- `/v1` 이하 모든 엔드포인트는 헤더 `user-id`, `role`이 필요합니다.
- Base Path는 `/`이며 Health Check는 `/health`입니다.
- `/` 이하 모든 엔드포인트는 헤더 `user-id`, `role`이 필요합니다.
- 주요 리소스는 Projects, Ports, DNS이며 공통 응답 형식과 페이지네이션 규칙을 공유합니다.
- 공통 에러는 422(Validation)와 500(Internal Server Error)입니다.

Expand All @@ -15,33 +15,35 @@
- [Health (상태 확인)](#health)
- [GET /health (서비스 상태)](#get-health)
- [Projects (프로젝트)](#projects)
- [POST /v1/projects (프로젝트 생성)](#post-v1projects)
- [GET /v1/projects (프로젝트 목록 조회)](#get-v1projects)
- [GET /v1/projects/{project_id} (프로젝트 상세 조회)](#get-v1projectsproject_id)
- [PATCH /v1/projects/{project_id}/name (프로젝트 이름 변경)](#patch-v1projectsproject_idname)
- [PATCH /v1/projects/{project_id}/resource (프로젝트 리소스 변경)](#patch-v1projectsproject_idresource)
- [DELETE /v1/projects/{project_id} (프로젝트 삭제)](#delete-v1projectsproject_id)
- [POST /projects (프로젝트 생성)](#post-projects)
- [GET /projects (프로젝트 목록 조회)](#get-projects)
- [GET /projects/owner (프로젝트 소유자 여부 조회)](#get-projectsowner)
- [GET /projects/resource-limit (프로젝트 최대 리소스 조회)](#get-projectsresource-limit)
- [GET /projects/{project_id} (프로젝트 상세 조회)](#get-projectsproject_id)
- [PATCH /projects/{project_id}/name (프로젝트 이름 변경)](#patch-projectsproject_idname)
- [PATCH /projects/{project_id}/resource (프로젝트 리소스 변경)](#patch-projectsproject_idresource)
- [DELETE /projects/{project_id} (프로젝트 삭제)](#delete-projectsproject_id)
- [Project Members (프로젝트 멤버)](#project-members)
- [GET /v1/projects/{project_id}/members (멤버 목록 조회)](#get-v1projectsproject_idmembers)
- [POST /v1/projects/{project_id}/members (멤버 추가)](#post-v1projectsproject_idmembers)
- [DELETE /v1/projects/{project_id}/members/{target_user_id} (멤버 제거)](#delete-v1projectsproject_idmemberstarget_user_id)
- [PATCH /v1/projects/{project_id}/owner (소유권 이전)](#patch-v1projectsproject_idowner)
- [GET /projects/{project_id}/members (멤버 목록 조회)](#get-projectsproject_idmembers)
- [POST /projects/{project_id}/members (멤버 추가)](#post-projectsproject_idmembers)
- [DELETE /projects/{project_id}/members/{target_user_id} (멤버 제거)](#delete-projectsproject_idmemberstarget_user_id)
- [PATCH /projects/{project_id}/owner (소유권 이전)](#patch-projectsproject_idowner)
- [Ports (포트)](#ports)
- [POST /v1/projects/{project_id}/ports (포트 공개)](#post-v1projectsproject_idports)
- [GET /v1/projects/{project_id}/ports (포트 목록 조회)](#get-v1projectsproject_idports)
- [PUT /v1/projects/{project_id}/ports/{port_id} (포트 수정)](#put-v1projectsproject_idportsport_id)
- [DELETE /v1/projects/{project_id}/ports/{port_id} (포트 삭제)](#delete-v1projectsproject_idportsport_id)
- [POST /projects/{project_id}/ports (포트 공개)](#post-projectsproject_idports)
- [GET /projects/{project_id}/ports (포트 목록 조회)](#get-projectsproject_idports)
- [PUT /projects/{project_id}/ports/{port_id} (포트 수정)](#put-projectsproject_idportsport_id)
- [DELETE /projects/{project_id}/ports/{port_id} (포트 삭제)](#delete-projectsproject_idportsport_id)
- [DNS (도메인)](#dns)
- [POST /v1/projects/{project_id}/dns (DNS 생성)](#post-v1projectsproject_iddns)
- [GET /v1/projects/{project_id}/dns-records (DNS 조회)](#get-v1projectsproject_iddns-records)
- [DELETE /v1/projects/{project_id}/dns-records/{dns_id} (DNS 삭제)](#delete-v1projectsproject_iddns-recordsdns_id)
- [PATCH /v1/projects/{project_id}/dns-records/{dns_id} (DNS 서브도메인 변경)](#patch-v1projectsproject_iddns-recordsdns_id)
- [PATCH /v1/projects/{project_id}/dns-records/{dns_id}/port (DNS에 포트 바인딩)](#patch-v1projectsproject_iddns-recordsdns_idport)
- [POST /projects/{project_id}/dns (DNS 생성)](#post-projectsproject_iddns)
- [GET /projects/{project_id}/dns-records (DNS 조회)](#get-projectsproject_iddns-records)
- [DELETE /projects/{project_id}/dns-records/{dns_id} (DNS 삭제)](#delete-projectsproject_iddns-recordsdns_id)
- [PATCH /projects/{project_id}/dns-records/{dns_id} (DNS 서브도메인 변경)](#patch-projectsproject_iddns-recordsdns_id)
- [PATCH /projects/{project_id}/dns-records/{dns_id}/port (DNS에 포트 바인딩)](#patch-projectsproject_iddns-recordsdns_idport)

## 개요
- Base Path: `/v1`
- Base Path: `/`
- Health Check: `/health`
- 인증/인가: `/v1` 이하 모든 엔드포인트는 헤더 `user-id`, `role` 필요
- 인증/인가: `/` 이하 모든 엔드포인트는 헤더 `user-id`, `role` 필요

## 공통 응답 형식

Expand Down Expand Up @@ -99,7 +101,7 @@

## Projects

### POST /v1/projects
### POST /projects
프로젝트 생성

요청 바디:
Expand Down Expand Up @@ -139,7 +141,7 @@

---

### GET /v1/projects
### GET /projects
프로젝트 목록 조회

쿼리 파라미터: `cursor`, `limit`
Expand Down Expand Up @@ -169,7 +171,7 @@

---

### GET /v1/projects/{project_id}
### GET /projects/{project_id}
프로젝트 상세 조회

응답 데이터: `ProjectDetailResponse`
Expand Down Expand Up @@ -220,7 +222,56 @@

---

### PATCH /v1/projects/{project_id}/name
### GET /projects/owner
프로젝트 소유자 여부 조회

쿼리 파라미터:
- `project_id` (integer, 필수): 확인할 프로젝트 ID

권한:
- 요청 사용자(`X-User-Id`) 기준으로 소유자 여부 확인

응답 데이터:
```json
{
"message": "프로젝트 소유자 여부를 조회했습니다.",
"data": {
"status": true
}
}
```

---

### GET /projects/resource-limit
프로젝트 최대 리소스 한도 조회 (App SVC 내부 연동용)

쿼리 파라미터:
- `project_id` (integer, 필수): 조회할 프로젝트 ID

권한:
- 프로젝트 OWNER만 조회 가능

응답 데이터:
```json
{
"message": "프로젝트 최대 리소스 한도를 조회했습니다.",
"data": {
"project_id": 1234567890123,
"max_cpu": 2.0,
"max_memory": 1024.0,
"max_disk": 2048.0
}
}
```

에러:
- 404: "프로젝트를 찾을 수 없습니다."
- 403: "프로젝트 소유자만 리소스 한도를 조회할 수 있습니다."

---

### PATCH /projects/{project_id}/name
프로젝트 이름 변경

요청 바디:
Expand All @@ -241,7 +292,7 @@

---

### PATCH /v1/projects/{project_id}/resource
### PATCH /projects/{project_id}/resource
프로젝트 리소스 변경

요청 바디 (모두 선택):
Expand Down Expand Up @@ -269,7 +320,7 @@

---

### DELETE /v1/projects/{project_id}
### DELETE /projects/{project_id}
프로젝트 삭제

응답 데이터: `ProjectResponse`
Expand All @@ -288,7 +339,7 @@
- 소유권은 기존 OWNER가 같은 프로젝트의 MEMBER에게만 이전할 수 있습니다.
- 프로젝트 내 OWNER는 항상 1명입니다.

### GET /v1/projects/{project_id}/members
### GET /projects/{project_id}/members
프로젝트 멤버 목록 조회

쿼리 파라미터: `cursor`, `limit`
Expand Down Expand Up @@ -321,7 +372,7 @@

---

### POST /v1/projects/{project_id}/members
### POST /projects/{project_id}/members
프로젝트에 멤버 추가 (사용자 초대)

요청 바디:
Expand Down Expand Up @@ -356,7 +407,7 @@

---

### DELETE /v1/projects/{project_id}/members/{target_user_id}
### DELETE /projects/{project_id}/members/{target_user_id}
프로젝트에서 멤버 제거

응답 데이터: `ProjectMemberResponse`
Expand All @@ -369,7 +420,7 @@

---

### PATCH /v1/projects/{project_id}/owner
### PATCH /projects/{project_id}/owner
프로젝트 소유권 이전

요청 바디:
Expand All @@ -395,7 +446,7 @@

## Ports

### POST /v1/projects/{project_id}/ports
### POST /projects/{project_id}/ports
포트 공개

요청 바디:
Expand Down Expand Up @@ -425,7 +476,7 @@

---

### GET /v1/projects/{project_id}/ports
### GET /projects/{project_id}/ports
포트 목록 조회

쿼리 파라미터: `cursor`, `limit`
Expand All @@ -437,7 +488,7 @@

---

### PUT /v1/projects/{project_id}/ports/{port_id}
### PUT /projects/{project_id}/ports/{port_id}
포트 수정

요청 바디 (`PortCreate`와 동일):
Expand Down Expand Up @@ -468,7 +519,7 @@

---

### DELETE /v1/projects/{project_id}/ports/{port_id}
### DELETE /projects/{project_id}/ports/{port_id}
포트 삭제

응답 데이터: `PortResponse`
Expand All @@ -484,7 +535,7 @@

## DNS

### POST /v1/projects/{project_id}/dns
### POST /projects/{project_id}/dns
DNS 생성

요청 바디:
Expand Down Expand Up @@ -518,7 +569,7 @@ DNS 생성

---

### GET /v1/projects/{project_id}/dns-records
### GET /projects/{project_id}/dns-records
DNS 조회

응답 데이터: `DNSResponse | null`
Expand All @@ -528,7 +579,7 @@ DNS 조회

---

### DELETE /v1/projects/{project_id}/dns-records/{dns_id}
### DELETE /projects/{project_id}/dns-records/{dns_id}
DNS 삭제

응답 데이터: `DNSResponse`
Expand All @@ -542,7 +593,7 @@ DNS 삭제

---

### PATCH /v1/projects/{project_id}/dns-records/{dns_id}
### PATCH /projects/{project_id}/dns-records/{dns_id}
DNS 서브도메인 변경

요청 바디:
Expand All @@ -567,7 +618,7 @@ DNS 서브도메인 변경

---

### PATCH /v1/projects/{project_id}/dns-records/{dns_id}/port
### PATCH /projects/{project_id}/dns-records/{dns_id}/port
DNS에 포트 바인딩

요청 바디:
Expand Down
7 changes: 7 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import logging

logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
)

from src.api import create_app

app = create_app()
Expand Down
3 changes: 2 additions & 1 deletion src/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from fastapi import FastAPI

from src.api.routers import api_router
from src.common.config.settings import get_app_config
from src.common.config.settings import get_app_config, load_all_configs
from src.core.db import create_all_tables
from src.core.exceptions import register_exception_handlers

Expand All @@ -14,6 +14,7 @@ def create_app() -> FastAPI:

@application.on_event("startup")
async def startup_event() -> None:
load_all_configs()
await create_all_tables()

return application
4 changes: 2 additions & 2 deletions src/api/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from fastapi import APIRouter

from src.api.routers import health
from src.api.routers.v1 import v1_api_router
from src.api.routers.routes import routes_api_router

api_router = APIRouter()
api_router.include_router(health.router, tags=["health"])
api_router.include_router(v1_api_router)
api_router.include_router(routes_api_router)
Loading