feat: serve uploaded media, delete a decision, and stop orphaning publications
Three gaps that all showed up as something broken on screen. An uploaded image could never be fetched. Every asset address the backend builds pointed at a path nothing served, so a figure was missing in the Studio preview and in the published page alike. There is now a public media endpoint under the prefix that is already anonymous and already routed, so no edge configuration has to change for an image to appear. Only READY assets are served; narrowing it to assets a published record uses would break previewing, which is the one thing an author does before publishing. Deleting a working copy left wreckage. Publications, validations and previews address a document by (kind, id) with no foreign key, so deleting one left rows pointing at nothing — and the dashboard and publication history read a title off that nothing and crashed. Both screens went down in production this way. A record with publication history is now refused outright, because that history says what was once public; validations and previews go with the record, because they are its scaffolding. A Decision could not be deleted at all while every other kind could. It can now, under the same two rules, and refuses when another decision supersedes it or the home page features it — neither cascades, so deleting would have been a foreign-key violation reaching the author as a 500. The persistence tests cover every one of these queries against real PostgreSQL. That suite exists because a column name I assumed rather than checked reached production once already; three more assumptions were caught here before it could happen again.
This commit is contained in:
@@ -3390,6 +3390,83 @@ paths:
|
||||
$ref: '#/components/schemas/DecisionUpdateRequest'
|
||||
security:
|
||||
- sessionCookie: []
|
||||
delete:
|
||||
operationId: deleteProjectDecision
|
||||
tags:
|
||||
- Decisions
|
||||
description: |-
|
||||
작업본 목록에서 Decision 을 지운다. Case·Reference·Question 에는 이 경로가 있었는데
|
||||
Decision 에만 없어서, 작성자가 연 초안을 접을 방법이 없었다.
|
||||
|
||||
수락·기각·대체는 무슨 일이 있었는지 남기는 수명주기이고 이것은 그것과 다르다 — 아직
|
||||
아무 판단도 하지 않은 초안을 없애는 일이다. 그래서 이미 게시된 Decision 은 거절한다.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: decisionId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- $ref: '#/components/parameters/CsrfToken'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExpectedVersionRequest'
|
||||
responses:
|
||||
'204':
|
||||
description: No Content
|
||||
'400':
|
||||
description: Bad Request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'403':
|
||||
description: Forbidden
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'404':
|
||||
description: Not Found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'409':
|
||||
description: Conflict
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'422':
|
||||
description: Unprocessable Content
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
'500':
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
security:
|
||||
- sessionCookie: []
|
||||
/api/v1/studio/projects/{id}/decisions/{decisionId}/accept:
|
||||
post:
|
||||
operationId: acceptProjectDecision
|
||||
@@ -5276,6 +5353,8 @@ components:
|
||||
- DOCUMENT_IN_USE
|
||||
- QUESTION_NOT_FOUND
|
||||
- QUESTION_IN_USE
|
||||
- DECISION_NOT_FOUND
|
||||
- DECISION_IN_USE
|
||||
- INTERNAL_ERROR
|
||||
description: '`INTERNAL_ERROR` 는 이 기능이 아니라 스켈레톤의 공통 처리기가 내는 코드다. 계약이 그것까지 열거해야 500 응답이 계약을 벗어나지 않는다.'
|
||||
category:
|
||||
|
||||
Reference in New Issue
Block a user