# Label Studio Integration - Server 모듈 구조

Things-Factory Label Studio 통합 서버 모듈의 최종 정리된 구조입니다.

---

## 📁 디렉토리 구조

```
server/
├── controller/                          # 비즈니스 로직
│   └── label-studio-role-mapper.ts     # 권한 매핑 로직
├── service/                             # GraphQL 서비스
│   ├── user-provisioning/
│   │   ├── user-provisioning-service.ts # 사용자 동기화 서비스
│   │   ├── user-sync-mutation.ts        # GraphQL Mutation
│   │   └── index.ts
│   └── index.ts                         # 서비스 통합
├── index.ts                             # 서버 모듈 진입점
└── route.ts                             # 메뉴 등록
```

---

## 📋 파일별 역할

### 1. **controller/label-studio-role-mapper.ts**

Things-Factory 사용자 권한을 Label Studio 권한으로 매핑

**주요 기능**:

- `mapUserPermissions(domain, user)`: 권한 매핑
- `getPermissionsDescription(permissions)`: 권한 설명

**매핑 규칙**:

```typescript
// 1. label-studio 카테고리 권한 있음 → Staff
// 2. user.owner === true → Admin
```

**반환값**:

```typescript
interface LabelStudioPermissions {
  is_superuser: boolean // Admin 여부
  is_staff: boolean // Django admin 접근
  is_active: boolean // 활성화 여부
}
```

---

### 2. **service/user-provisioning/user-provisioning-service.ts**

Label Studio API를 통한 사용자 동기화 서비스

**주요 메서드**:

#### `syncUser(domain, user): Promise<SyncResult>`

개별 사용자를 Label Studio에 동기화

```typescript
// 1. 권한 확인
const hasLSPrivilege = await User.hasPrivilege('label-studio', ...)

// 2. 권한 매핑
const lsPermissions = await LabelStudioRoleMapper.mapUserPermissions(domain, user)

// 3. Label Studio API 호출
const result = await createOrUpdateLabelStudioUser(...)
```

#### `syncAllUsers(domain): Promise<SyncSummary>`

도메인의 모든 사용자를 일괄 동기화

```typescript
const users = await getDomainUsers(domain)
for (const user of users) {
  await syncUser(domain, user)
}
```

#### `getDomainUsers(domain): Promise<User[]>`

도메인의 모든 사용자 조회 (users_domains 테이블 조인)

**Helper 메서드**:

- `createOrUpdateLabelStudioUser()`: 사용자 생성/수정
- `deactivateUser()`: 사용자 비활성화
- `buildApiUrl()`: API URL 생성
- `generateRandomPassword()`: 랜덤 패스워드 생성
- `sleep()`: Rate Limiting 방지

---

### 3. **service/user-provisioning/user-sync-mutation.ts**

GraphQL Mutation 정의

**Mutations**:

#### `syncMyUserToLabelStudio`

```graphql
mutation {
  syncMyUserToLabelStudio {
    success
    email
    action
    lsPermissions
  }
}
```

**권한**: `@privilege(category: "label-studio", privilege: "staff")`

#### `syncAllUsersToLabelStudio`

```graphql
mutation {
  syncAllUsersToLabelStudio {
    total
    created
    updated
    deactivated
    skipped
    errors
    results {
      email
      action
      lsPermissions
    }
  }
}
```

**권한**: `@privilege(category: "label-studio", privilege: "admin")`

---

### 4. **service/index.ts**

GraphQL 리졸버 통합

```typescript
export const resolvers = [UserSyncMutation]
export * from './user-provisioning/index.js'
```

---

### 5. **index.ts**

서버 모듈 진입점

```typescript
export * from './service/index.js'
import './route.js'
```

---

## 🔄 데이터 흐름

### 사용자 동기화 플로우

```
1. GraphQL Mutation 호출
   ↓
2. user-sync-mutation.ts
   - 설정 검증 (apiToken 확인)
   ↓
3. user-provisioning-service.ts
   - 권한 확인 (User.hasPrivilege)
   ↓
4. label-studio-role-mapper.ts
   - 권한 매핑 (label-studio 카테고리 → Staff/Admin)
   ↓
5. Label Studio API 호출
   - POST/PATCH /api/users
   - is_superuser, is_staff, is_active 설정
   ↓
6. 결과 반환
   - SyncResult / SyncSummary
```

---

## 🔧 설정

### config/config.development.js

```javascript
labelStudio: {
  serverUrl: 'http://localhost:8080',
  apiToken: '',  // Label Studio API Token
  interfaces: 'panel,controls,annotations:menu'
}
```

### 설정 사용

```typescript
import { config } from '@things-factory/env'

const labelStudioConfig = config.get('labelStudio', {
  serverUrl: '',
  apiToken: '',
  interfaces: 'panel,controls,annotations:menu'
})
```

---

## 🗑️ 제거된 항목

### 1. **LabelStudioConfig Entity**

- 엔티티 DB 저장 제거
- config 파일로 대체

### 2. **label-studio-config 폴더**

- `label-studio-config.ts` (Entity)
- `label-studio-config-query.ts` (Query)
- `label-studio-config-mutation.ts` (Mutation)

### 3. **role-mapping 폴더**

- `label-studio-role-mapper.ts` → controller로 이동

### 4. **불필요한 메서드**

- `getOrganizationId()` - Community Edition 미사용
- `createOrganizationMembership()` - Community Edition 미사용
- `updateOrganizationMembership()` - Community Edition 미사용

### 5. **entities 배열**

- `service/index.ts`의 빈 entities 배열 제거

---

## 📊 권한 체계

### Things-Factory 권한

| Privilege Category      | 설명                   |
| ----------------------- | ---------------------- |
| `label-studio:query`    | Label Studio 조회 권한 |
| `label-studio:mutation` | Label Studio 수정 권한 |
| `label-studio:staff`    | Staff 전용 mutation    |
| `label-studio:admin`    | Admin 전용 mutation    |

### Label Studio 권한 (Community Edition)

| 플래그                | 설명              |
| --------------------- | ----------------- |
| `is_superuser: true`  | Admin (모든 권한) |
| `is_superuser: false` | Staff (라벨링만)  |
| `is_active: false`    | 비활성화          |

---

## 🧪 테스트

### 1. 개인 동기화

```graphql
mutation {
  syncMyUserToLabelStudio {
    success
    email
    action
    lsPermissions
  }
}
```

### 2. 전체 동기화

```graphql
mutation {
  syncAllUsersToLabelStudio {
    total
    created
    updated
    results {
      email
      lsPermissions
      error
    }
  }
}
```

---

## 📝 코드 규칙

### 1. 권한 확인

```typescript
// User.hasPrivilege 사용
const hasPrivilege = await User.hasPrivilege('label-studio', 'query', domain, user)
```

### 2. 설정 가져오기

```typescript
// config.get 사용
const config = config.get('labelStudio', {
  serverUrl: '',
  apiToken: '',
  interfaces: 'panel,controls,annotations:menu'
})
```

### 3. API 호출

```typescript
// axios 사용
await axios.patch(`${apiUrl}/users/${id}/`, data, {
  headers: {
    Authorization: `Token ${config.apiToken}`,
    'Content-Type': 'application/json'
  }
})
```

---

## 🚀 향후 확장

### 가능한 개선사항

1. **Batch API 지원**: 여러 사용자 한 번에 동기화
2. **Webhook 연동**: Label Studio → Things-Factory 이벤트
3. **동기화 스케줄러**: 주기적 자동 동기화
4. **에러 재시도 로직**: 실패 시 재시도

---

**문서 버전**: 1.0
**작성일**: 2025-10-03
**정리 완료**: ✅
