@@ -181,6 +181,67 @@ func (h *TenantHandler) Terminate(c *gin.Context) {
181181 c .Status (http .StatusNoContent )
182182}
183183
184+ // Reactivate godoc
185+ //
186+ // @Summary Reactivate a terminated tenant
187+ // @Description Flips a TERMINATED tenant back to ACTIVE. Fails if the tenant is not terminated.
188+ // @Tags Tenants
189+ // @Security BearerAuth
190+ // @Produce json
191+ // @Param id path string true "Tenant id"
192+ // @Success 200 {object} domain.Tenant
193+ // @Failure 400 {object} map[string]string
194+ // @Failure 403 {object} map[string]string
195+ // @Failure 404 {object} map[string]string
196+ // @Router /tenants/{id}/reactivate [post]
197+ func (h * TenantHandler ) Reactivate (c * gin.Context ) {
198+ tid , ok := pathTenantID (c )
199+ if ! ok {
200+ return
201+ }
202+ t , err := h .uc .Reactivate (c .Request .Context (), tid )
203+ audit .Record (c , audit_connectors.Event {
204+ Action : "tenant.reactivate" ,
205+ ResourceType : "tenant" ,
206+ ResourceID : c .Param ("id" ),
207+ }, audit_domain .TENANT_REACTIVATE_ATTEMPT , audit_domain .TENANT_REACTIVATE_SUCCESS , err )
208+ if err != nil {
209+ writeError (c , err )
210+ return
211+ }
212+ c .JSON (http .StatusOK , t )
213+ }
214+
215+ // PermanentlyDelete godoc
216+ //
217+ // @Summary Permanently delete a terminated tenant
218+ // @Description Hard-deletes the tenant row and all rows scoped by tenant_id across the schema. Only allowed when the tenant is TERMINATED.
219+ // @Tags Tenants
220+ // @Security BearerAuth
221+ // @Param id path string true "Tenant id"
222+ // @Success 204
223+ // @Failure 400 {object} map[string]string
224+ // @Failure 403 {object} map[string]string
225+ // @Failure 404 {object} map[string]string
226+ // @Router /tenants/{id}/permanent [delete]
227+ func (h * TenantHandler ) PermanentlyDelete (c * gin.Context ) {
228+ tid , ok := pathTenantID (c )
229+ if ! ok {
230+ return
231+ }
232+ err := h .uc .PermanentlyDelete (c .Request .Context (), tid )
233+ audit .Record (c , audit_connectors.Event {
234+ Action : "tenant.purge" ,
235+ ResourceType : "tenant" ,
236+ ResourceID : c .Param ("id" ),
237+ }, audit_domain .TENANT_PURGE_ATTEMPT , audit_domain .TENANT_PURGE_SUCCESS , err )
238+ if err != nil {
239+ writeError (c , err )
240+ return
241+ }
242+ c .Status (http .StatusNoContent )
243+ }
244+
184245func writeError (c * gin.Context , err error ) {
185246 switch {
186247 case errors .Is (err , domain .ErrNotFound ):
@@ -192,7 +253,8 @@ func writeError(c *gin.Context, err error) {
192253 case errors .Is (err , domain .ErrNameRequired ), errors .Is (err , domain .ErrDomainRequired ),
193254 errors .Is (err , domain .ErrDomainInvalid ), errors .Is (err , domain .ErrStatusInvalid ),
194255 errors .Is (err , domain .ErrAlreadyTerminated ), errors .Is (err , domain .ErrSupportInvalid ),
195- errors .Is (err , domain .ErrLimitNegative ), errors .Is (err , domain .ErrLimitInvalid ):
256+ errors .Is (err , domain .ErrLimitNegative ), errors .Is (err , domain .ErrLimitInvalid ),
257+ errors .Is (err , domain .ErrNotTerminated ):
196258 c .JSON (http .StatusBadRequest , gin.H {"error" : err .Error ()})
197259 default :
198260 c .JSON (http .StatusInternalServerError , gin.H {"error" : err .Error ()})
0 commit comments