Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2277648
chore(cleanup): 0단계 정리·정합성 — 쿼리 병렬화, 스킨 style 차단, discovery 정합, 503 메…
mack-erel Jul 3, 2026
b1c3d8b
fix(security): 1단계 보안 하드닝 — SAML SLO/TOTP/rate-limit/CSRF/LDAP
mack-erel Jul 3, 2026
f1e6dc6
chore(types): D1 바인딩을 Platform.env 의 optional 타입으로 선언
mack-erel Jul 3, 2026
440fb4c
feat(oidc): Refresh Token 발급·회전 구현 (offline_access)
mack-erel Jul 3, 2026
ece79db
feat(oidc): Token Introspection(RFC 7662) + Revocation(RFC 7009) 엔드포인트
mack-erel Jul 3, 2026
00b4b8c
feat(admin): 사용자 목록 커서 페이지네이션 + 검색
mack-erel Jul 3, 2026
52448d1
feat(oidc): authorize prompt/max_age/id_token_hint/login_hint 처리 (OID…
mack-erel Jul 3, 2026
cb4a895
feat(saml): Assertion 암호화(EncryptedAssertion) 구현 (A2)
mack-erel Jul 3, 2026
97b53cc
test: vitest 도입 + 보안 로직 유닛 테스트 + 스키마 parity + CI 테스트 단계 (B1/E1/E3)
mack-erel Jul 3, 2026
660761d
feat(ops): 헬스체크 엔드포인트 + 루트 에러 페이지 + Workers 관측성 (B2/B3/B4)
mack-erel Jul 3, 2026
8bc3217
feat(audit): 감사 로그 행 단위 무결성 HMAC (H-ADMIN-2) + find-password 타이밍
mack-erel Jul 4, 2026
02e47b0
refactor(admin): loopback 호스트 판정 공통 모듈로 추출 (E5)
mack-erel Jul 4, 2026
a49fb5d
docs: 최종 처리 현황 — 3/4단계 완료·보류 항목 정리
mack-erel Jul 4, 2026
e279ed2
feat(auth): TOTP 이중 등록 방지 — totp_owner_id unique index + enroll 원자화
mack-erel Jul 5, 2026
8097a7d
feat(i18n): 영어 로케일 + SSR 로케일 결정 + ko 폴백 + 언어 토글
mack-erel Jul 5, 2026
03e1370
refactor(admin): 조직 CRUD 팩토리 + zod 폼 검증 + teams/parts FK 검증
mack-erel Jul 5, 2026
7cf9c30
docs: 보류 3건 처리 기획·투두 (deferred-followups)
mack-erel Jul 5, 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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ jobs:
filters: |
build-affected:
- "src/**"
- "test/**"
- "static/**"
- "drizzle/**"
- "vitest.config.*"
- "package.json"
- "bun.lock"
- "bunfig.toml"
Expand Down Expand Up @@ -77,6 +79,9 @@ jobs:
- name: Type Check
run: bun run check

- name: Test
run: bun run test

- name: Build
run: bun run build

Expand Down
66 changes: 59 additions & 7 deletions bun.lock

Large diffs are not rendered by default.

221 changes: 221 additions & 0 deletions docs/improvement-report-2026-07-02.md

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions docs/plans/deferred-followups/PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# 보류 3건 처리 기획서 — TOTP TOCTOU / i18n(en) / CRUD 팩토리+zod

> 대상: `improvement-report-2026-07-02.md` 에서 "의도적 보류"로 남긴 3건(argon2 제외).
> 조사(2026-07-04, 3개 Explore 에이전트) 결과를 종합한 실행 기획. 스키마 변경은 CLAUDE.md 규칙대로 `db:generate` 까지만.

---

## 배경 / 목표

앞서 이 3건은 (검증 그물 부재 / mysql partial-index drift 재생산 / 품질 담보 불가) 사유로 보류했으나, 조사 결과 **각각 저위험 실행 경로가 확인**되어 진행한다.

- **TOTP TOCTOU** — 동시 이중 등록을 방언 무관하게 DB 레벨로 차단.
- **i18n(en)** — 엔진에 ko 폴백 + SSR 로케일 결정을 넣고, 엔드유저 인증 플로우를 영어화.
- **CRUD 팩토리+zod** — 정형 4라우트(teams/parts/positions/departments)를 팩토리로 통합하고 zod 로 폼 검증 표준화.

---

## 범위 (In / Out)

### Phase A — TOTP TOCTOU (스키마 + 로직)

- **In**: `credentials` 3방언 스키마에 nullable `totpOwnerId` + 일반 unique index 추가. enroll/confirm 이 INSERT 를 try/catch 로 감싸 unique 위반을 409 로 변환. TOTP+백업코드 INSERT 원자화(dialect 분기). `db:generate` 로 마이그레이션 생성(적용 안 함).
- **Out**: 실제 마이그레이션 적용(사용자 몫). password 중복(별개 사안).

### Phase B — i18n(en) 엔진 + 엔드유저 영어화

- **In**: 엔진에 `en` 로케일 + **ko 폴백**(누락 키는 원본 key 대신 한국어). 루트 `+layout.server.ts` 신설로 SSR 로케일 결정(쿠키 → Accept-Language → 기본 ko), `+layout.svelte` 에서 `setLocale`, `app.html` lang 동적화. `en.json`(엔드유저 플로우: login/signup/mfa_login/find_id/find_password/reset_password + common ≈ 72키). 5개 auth `.svelte` 잔존 하드코딩을 t() 로. 간단한 언어 전환 UI(쿠키 설정).
- **Out**: **admin 콘솔 영어화**(내부 운영자용 → ko 폴백으로 한국어 유지). **서버(+page.server.ts) 에러 메시지 t() 전환**(서버 로케일 인지 리팩터가 커서 별도). 원어민 최종 검수(문구는 표준 auth 용어 기준으로 작성하되 검수 권고 명시).

### Phase C — CRUD 팩토리 + zod

- **In**: zod 도입(dependencies). `$lib/server/validation.ts` 확장 또는 신규 `$lib/server/admin/crud-factory.ts` 로 정형 4라우트(teams/parts/positions/departments) create/update/delete 를 팩토리화. 라우트별 훅으로 departments 계층검증·positions level 주입. zod 스키마로 폼 검증 표준화(status enum, 숫자 coerce). **부수 개선**: teams/parts 의 FK 무검증(존재하지 않는/타 tenant departmentId·teamId 삽입 가능한 latent 결함)을 팩토리 FK-검증 훅으로 차단. 팩토리·zod 로직 유닛 테스트.
- **Out**: 비정형 라우트(users/oidc-clients/saml-sps/skins/ldap-providers) 팩토리화(액션 다양성 커서 별도). signing-keys(방언 분기 특수).

---

## 코드베이스 사실관계 (조사 근거)

### TOTP

- `api/totp/enroll/confirm/+server.ts`: SELECT(43-48) → verifyTotp(50) → INSERT(56-64) 사이 락/제약 없음 = 완전한 TOCTOU. rate-limit(34-38)은 브루트포스용이라 동시요청 무관.
- `credentials`(schema.sqlite.ts:75-98 등 3방언): `type` enum(password/totp/webauthn/backup_code). webauthn/backup_code 는 사용자당 다수 정상 → `UNIQUE(userId,type)` 불가. `credentials_user_type_idx` 는 **비-unique**.
- signing-keys 는 sqlite/pg 는 partial unique index(`WHERE active=1`), **mysql 은 인덱스 없이 앱 트랜잭션만**(schema.mysql.ts:513-516 주석). → partial index 는 mysql drift 재생산이라 회피.
- **해법(조사 제안)**: nullable `totpOwnerId`(type='totp' 행만 userId 채움, 나머지 NULL) + **일반** `uniqueIndex(totpOwnerId)`. NULL 은 unique 검사 제외(4방언 표준) → totp 만 사용자당 1개 강제, 다른 type 무영향. 최종 방어는 INSERT try/catch → 409. `grant.ts:findAndConsumeGrant` 의 방언별 affectedRows/RETURNING 패턴이 참고 선례.

### i18n

- `i18n.svelte.ts`(53줄): `Locale="ko"` 만(3), `messages={ko}`(13-15), 누락 키 시 `return key`(37,42). `$state("ko")`(17) 클라이언트 전용.
- SSR 로케일 결정 **전무**: 루트 `+layout.server.ts`/`+layout.ts` 없음, `hooks.server.ts` 로케일 로직 없음. (profile 의 `locale` 필드는 OIDC claim 용, UI 언어와 무관.)
- `app.html:2` `lang="en"` 하드코딩(현재 ko 콘텐츠와 불일치 = 버그).
- `setLocale` 호출부 0건(전환 UI 없음).
- ko.json 469줄 27섹션. 엔드유저: login(5)/signup(9)/mfa_login(9)/find_id(8)/find_password(7)/reset_password(6) + common(28).
- auth `.svelte` 잔존 하드코딩: login(서브타이틀/구분자/패스키 라벨·에러), signup·reset placeholder. 서버 에러 메시지는 리터럴 하드코딩(범위 밖).

### CRUD/zod

- teams(118)/parts(126)/positions(94)/departments(173) create/update/delete 가 near-identical: requireAdminContext → formData → 수동 캐스팅 → fail(400) → tenant-scoped drizzle → recordAuditEvent(kind=`<entity>_<action>`) → `{created/updated/deleted:true}`.
- 고유부: departments `validateParentHierarchy`(self/순환/깊이8/참조무결성), positions `level` parseInt+isNaN. teams/parts 는 FK(departmentId/teamId) 무검증.
- UI 계약: `{error}` 와 `{create:true,error}` 만 의존. 성공 플래그(created 등)는 **어느 svelte 도 미소비**(enhance `result.type` 로 판정). → 성공 shape 자유.
- zod/valibot 미설치. 통합/e2e 테스트·DB 하네스 전무(순수 유닛 테스트만). audit `kind`는 `string`(enum 아님) → 파라미터화 용이.

---

## 접근법 / 대안 / 리스크

| Phase | 접근 | 대안(기각) | 리스크 & 완화 |
| ------ | ---------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| A TOTP | nullable `totpOwnerId` + 일반 unique index + INSERT try/catch | partial unique(mysql 미지원→drift), app 트랜잭션만(phantom-row race 잔존) | 스키마 변경 → parity 테스트가 3방언 동기화 강제. 적용은 사용자. 유닛 테스트 어려움(DB 필요) → 로직 리뷰 + build 로 검증 |
| B i18n | 엔진 ko 폴백 + SSR 로케일(layout.server) + 엔드유저 en + 전환 UI | 클라이언트-only 로케일(하이드레이션 미스매치), 전면 번역(품질/UX 리스크) | admin 은 ko 폴백 유지(부분영어 UX 문제 회피). SSR 로케일로 하이드레이션 안전. 번역 검수 권고 명시 |
| C CRUD | zod + 정형 4라우트 팩토리(훅 주입) | 전 라우트 일괄 팩토리(비정형 리스크), zod-only(중복 잔존) | **통합 테스트 부재가 최대 리스크** → (1) 팩토리·zod 를 순수 유닛 테스트로 커버, (2) 기존 동작 정확 보존(에러 shape/tenant 스코프/audit kind 회귀 금지), (3) **작성과 분리된 독립 에이전트 검증**(작성자≠검증자), (4) build/lint/typecheck 게이트 |

**공통 리스크**: Phase C 가 보안 민감(tenant 스코프/audit) 라우트를 건드림 → 검증 레인 분리 필수. Phase A/B/C 는 파일이 겹치지 않아 병렬 가능(단 각 Phase 내부 쓰기는 파일 겹침 시 직렬).

---

## 열린 질문 (승인 시 확정)

1. **Phase C teams/parts FK 검증 추가** — 현재 존재하지 않는/타 tenant FK 도 통과하는 latent 결함이 있다. 팩토리에서 FK 참조 검증을 **추가**할지(권장), 기존 동작 유지할지?
2. **Phase B 언어 전환 UI 위치** — auth 레이아웃(로그인 화면)에 소형 토글 배치로 충분한지, 아니면 이번엔 쿠키/Accept-Language 자동 판별만 하고 명시 토글은 생략할지?
3. **진행 순서** — 리스크 낮은 순(A → B → C) 직렬 권장. 병렬 원하면 A/B/C 동시 착수 가능(파일 비겹침).

---

## 산출물

- `docs/plans/deferred-followups/PLAN.md` (본 문서)
- 승인 후 `docs/plans/deferred-followups/TODO.md` (페이즈별 상세 투두)
119 changes: 119 additions & 0 deletions docs/plans/deferred-followups/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# 실행 투두 — 보류 3건 (병렬 A/B/C)

> **상태(2026-07-05): A/B/C 전부 구현 완료 → 메인 브랜치 통합 완료 → 독립 검증 통과.**
> 게이트: `bun run test` 54 passed(9 files) · `svelte-check` 0 errors · `eslint`·`build` 통과.
> 마이그레이션 생성만(d1 0019, pg/mysql/sqlite 0002) — **적용은 사용자 몫**. 커밋 승인 대기.

> 승인된 결정: 기획서 승인 · Phase C FK 검증 **추가** · Phase B 자동판별+**수동 토글** · **병렬 진행**(worktree 격리).
> 규칙: 스키마 변경은 `db:generate` 까지만(적용 금지). 커밋은 검증 통과 + 승인 후. 쓰기=Opus 위임, 검증=작성자와 분리된 독립 Opus 에이전트.
> 파일 경계: A/B/C 는 서로 파일이 겹치지 않아 worktree 병렬 안전. 각 Phase 내부는 단일 에이전트 직렬.

---

## Phase A — TOTP TOCTOU (스키마 + 로직)

**목적**: 동시 이중 TOTP 등록을 방언 무관하게 DB 레벨로 차단.

### A-1. `credentials` 3방언 스키마에 섀도 컬럼 + unique index 추가

- 파일: `src/lib/server/db/schema.sqlite.ts`, `schema.pg.ts`, `schema.mysql.ts`
- 작업: `credentials` 테이블에 `totpOwnerId: text("totp_owner_id")` (nullable) 추가. 각 파일 인덱스 배열에 **일반**(partial 아님) `uniqueIndex("credentials_totp_owner_uidx").on(t.totpOwnerId)` 추가.
- 수용 기준: 3방언 모두 동일 컬럼명(`totp_owner_id`)·동일 인덱스명. `where(...)` 절 없는 순수 unique index(mysql 포함 지원). `test/unit/schema-parity.test.ts` 통과(컬럼 parity).

### A-2. enroll/confirm 원자화 + unique 위반 → 409

- 파일: `src/routes/api/totp/enroll/confirm/+server.ts`
- 작업: (a) TOTP 크레덴셜 INSERT 시 `totpOwnerId: userId` 채움(다른 type INSERT 는 미설정=NULL 유지). (b) 사전 SELECT 체크는 사용자 친화적 빠른 실패로 유지하되, **실제 방어는 INSERT 를 try/catch 로 감싸 unique 위반 시 `throw error(409, "TOTP already enrolled for this user")`**. (c) TOTP INSERT + 백업코드 10개 INSERT 를 dialect 분기로 원자화: d1/sqlite → `db.batch([...])`, postgres/mysql → `db.transaction(async (tx) => {...})` (signing-keys `+page.server.ts` 패턴 참고). 실패 시 백업코드 고아 방지.
- 수용 기준: 두 동시 confirm 중 하나만 성공(두 번째는 409). 백업코드는 TOTP INSERT 성공 시에만 커밋. 기존 성공 응답 shape(`{ ok:true, backupCodes }`) 보존. rate-limit(C3) 유지.

### A-3. 마이그레이션 생성 (적용 금지)

- 작업: `DB_DIALECT=d1 bun run db:generate` + `db:generate:pg` + `db:generate:mysql` + `db:generate:sqlite`. 생성된 `drizzle/**` SQL·meta 커밋 대상. `migrate`/`push` **실행 금지**.
- 수용 기준: 4트랙 모두 `totp_owner_id` 컬럼 + unique index ADD 마이그레이션 생성. 적용은 사용자 몫으로 보고.

### A-검증 (독립 에이전트)

- `bun run test`(schema-parity 포함), `svelte-check`, `bun run lint`. TOCTOU 로직 리뷰(SELECT~INSERT 창이 있어도 DB unique 가 최종 결정하는지). 가짜 완료(스텁/skip) 없는지.

---

## Phase B — i18n(en) 엔진 + 엔드유저 영어화

**목적**: 엔진 ko 폴백 + SSR 로케일 결정, 엔드유저 인증 플로우 영어 제공, 자동판별+수동 토글.

### B-1. 엔진: en 로케일 + ko 폴백

- 파일: `src/lib/i18n.svelte.ts`
- 작업: `Locale` 타입에 `"en"` 추가. `import en from "./i18n/en.json"`, `messages = { ko, en }`. `t()` 를 수정해 **현재 로케일에서 키 실패 시 ko 로 폴백 후, ko 도 실패 시에만 원본 key 반환**(37·42행 로직 교체, `lookup(dict, keys)` 헬퍼 도입). `setLocale` 이 초기값을 서버 주입값으로 받을 수 있게 유지.
- 수용 기준: en 에 없는 키는 한국어로 표시(원본 key 노출 0). ko 로케일 동작 회귀 없음.

### B-2. SSR 로케일 결정 (루트 layout 신설)

- 파일: `src/routes/+layout.server.ts`(신규), `src/routes/+layout.svelte`(수정), `src/app.html`(수정)
- 작업: `+layout.server.ts` load 에서 로케일 판별 — **쿠키(`idp_locale`) → Accept-Language 헤더 → 기본 "ko"** 순. `data.locale` 반환. `+layout.svelte` 에서 `data.locale` 로 `setLocale()` 을 렌더 전(모듈 초기화 시점 또는 `$derived`/즉시 호출)에 적용해 하이드레이션 미스매치 방지. `app.html` 의 하드코딩 `lang="en"` 을 SSR 로케일 반영(`%sveltekit.html.lang%` 류 치환 또는 hooks transform)으로 동적화.
- 수용 기준: 쿠키/Accept-Language 에 따라 SSR·클라이언트 로케일 일치(하이드레이션 경고 없음). `<html lang>` 이 실제 로케일과 일치.

### B-3. en.json 작성 (엔드유저 플로우)

- 파일: `src/lib/i18n/en.json`(신규)
- 작업: ko.json 의 엔드유저 섹션을 영어로 번역 — `common`(28), `app`, `nav`, `login`(5), `signup`(9), `mfa_login`(9), `find_id`(8), `find_password`(7), `reset_password`(6) + 신규 추출 키(아래 B-4). admin/조직/oidc/saml/skins 등 내부 섹션은 **생략**(ko 폴백). 표준 auth 용어 사용, 문구는 자연스럽게. 파일 상단 주석에 "원어민 검수 권고".
- 수용 기준: 포함 섹션 키가 ko.json 과 1:1 대응(구조 동일). JSON 유효.

### B-4. auth `.svelte` 잔존 하드코딩 → t()

- 파일: `src/routes/(auth)/login/+page.svelte`, `signup/+page.svelte`, `reset-password/+page.svelte`
- 작업: login 의 서브타이틀·구분자("또는")·패스키 로딩/버튼 라벨·패스키 클라이언트 에러 4종, signup·reset placeholder 를 t() 키로 전환하고 해당 키를 ko.json·en.json 양쪽에 추가. (서버 `+page.server.ts` 에러 메시지는 범위 밖 — 손대지 않음.)
- 수용 기준: 대상 화면에 하드코딩 한국어 리터럴 0(서버 파일 제외). ko/en 양쪽 키 존재.

### B-5. 언어 전환 UI (수동 토글)

- 파일: auth 레이아웃 또는 소형 컴포넌트(신규, 예: `src/lib/components/LocaleToggle.svelte`) + 쿠키 설정 경로
- 작업: ko/en 토글. 클릭 시 `idp_locale` 쿠키 설정 후 리로드(또는 setLocale + 쿠키). auth 화면(로그인 등)에 배치.
- 수용 기준: 토글로 언어 전환되고 새로고침 후 유지(쿠키). SSR 로케일과 일관.

### B-검증 (독립 에이전트)

- `bun run build`(SSR), `svelte-check`, `lint`. 하이드레이션 미스매치 없는지, ko 폴백 동작, en 화면 확인. 가짜 완료 없는지.

---

## Phase C — CRUD 팩토리 + zod (정형 4라우트)

**목적**: teams/parts/positions/departments 를 팩토리로 통합, zod 로 폼 검증 표준화, teams/parts FK 검증 **추가**.

### C-1. zod 도입

- 파일: `package.json`
- 작업: `bun add zod` (dependencies). 락파일 동기화.
- 수용 기준: zod dependencies 등재. build 영향 없음.

### C-2. CRUD 팩토리 모듈

- 파일: `src/lib/server/admin/crud-factory.ts`(신규)
- 작업: tenant-scoped CRUD 팩토리 작성. 파라미터: drizzle 테이블, zod create/update 스키마, audit kind 접두사(`team`/`part`/…), 성공 shape, 그리고 **훅**: `beforeCreate/beforeUpdate`(검증·FK 참조 확인용, 실패 시 에러 메시지 반환), 선택적 load 조인 셀렉터. 공통 골격(requireAdminContext → formData → zod 검증 → tenant 스코프 insert/update/delete → recordAuditEvent → 성공 반환)을 생성. **에러 반환은 반드시 기존 계약**: create 실패 `fail(400,{create:true,error})`, update/delete 실패 `fail(400,{error})`.
- 수용 기준: 팩토리가 create/update/delete 액션 객체를 생성. tenant 필터·audit kind·에러 shape 를 기존과 동일하게 재현.

### C-3. 4라우트 팩토리 적용 + 훅

- 파일: `src/routes/admin/{teams,parts,positions,departments}/+page.server.ts`
- 작업: 각 라우트를 팩토리 호출로 재작성. zod 스키마 정의(status `z.enum`, level/displayOrder `z.coerce.number().int()`, optional `.trim()→null`). 훅 주입: **departments** = `validateParentHierarchy`(self/순환/깊이8/참조무결성 보존), **positions** = level 검증(zod 로 흡수), **teams** = departmentId 존재+동일 tenant FK 검증(신규 추가), **parts** = teamId 존재+동일 tenant FK 검증(신규 추가). load 도 팩토리/공통화(조인은 라우트별 셀렉터).
- 수용 기준: 네 라우트의 기존 동작 보존(생성/수정/삭제/목록, audit kind, tenant 스코프). teams/parts 는 존재하지 않는/타 tenant FK 를 이제 `fail(400)` 로 거부. UI(`{error}`/`{create:true,error}`) 무변경으로 동작.

### C-4. 유닛 테스트

- 파일: `test/unit/crud-factory.test.ts`(신규), `test/unit/admin-zod.test.ts`(신규)
- 작업: zod 스키마 검증(유효/무효 입력, status enum 거부, 숫자 coerce), 팩토리의 순수 로직(에러 shape 매핑, audit kind 생성) 테스트. DB 없이 가능한 범위.
- 수용 기준: 신규 테스트 통과. 전체 `bun run test` 그린.

### C-검증 (독립 에이전트)

- **작성자와 분리된 독립 Opus 에이전트**가 검증: 4라우트의 tenant 스코프·audit kind·에러 shape 회귀 없는지 diff 리뷰, FK 검증 추가가 올바른지, departments 계층검증 보존 확인. `bun run test`·`svelte-check`·`build`·`lint`. 가짜 완료 없는지.

---

## 통합 & 커밋 (STEP 7-8)

- 3개 worktree 변경을 기준 브랜치(`fix/security-hardening-phase1`)로 통합, 충돌 정리.
- 전체 게이트: `bun run test` · `svelte-check`(또는 `bun run check`) · `bun run lint` · `bun run build` 그린.
- Phase 별 논리 커밋(승인 후). 푸시는 사용자 요청 시에만.
- 스키마 마이그레이션 실제 적용은 사용자에게 요청.
1 change: 1 addition & 0 deletions drizzle/0018_fancy_master_chief.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE `audit_events` ADD `hash` text;
2 changes: 2 additions & 0 deletions drizzle/0019_regular_screwball.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE `credentials` ADD `totp_owner_id` text;--> statement-breakpoint
CREATE UNIQUE INDEX `credentials_totp_owner_uidx` ON `credentials` (`totp_owner_id`);
Loading
Loading