@@ -10,6 +10,7 @@ import logger from "../logger.js";
10
10
import { getExpirationTime } from "../rules/index.js" ;
11
11
import { forgetBlobAccessed , updateBlobAccess } from "../db/methods.js" ;
12
12
import { readUpload , removeUpload , UploadDetails } from "./upload.js" ;
13
+ import { mapParams } from "../admin-api/helpers.js" ;
13
14
14
15
async function createStorage ( ) {
15
16
if ( config . storage . backend === "local" ) {
@@ -82,6 +83,7 @@ export async function pruneStorage() {
82
83
const now = dayjs ( ) . unix ( ) ;
83
84
const checked = new Set < string > ( ) ;
84
85
86
+ /** Remove all blobs that no longer fall under any rules */
85
87
for ( const rule of config . storage . rules ) {
86
88
const expiration = getExpirationTime ( rule , now ) ;
87
89
let blobs : ( BlobMetadata & { pubkey : string ; accessed : number | null } ) [ ] = [ ] ;
@@ -137,6 +139,25 @@ export async function pruneStorage() {
137
139
}
138
140
if ( n > 0 ) log ( "Checked" , n , "blobs for rule #" + config . storage . rules . indexOf ( rule ) ) ;
139
141
}
142
+
143
+ // remove blobs with no owners
144
+ if ( config . storage . removeWhenNoOwners ) {
145
+ const blobs = db
146
+ . prepare < [ ] , { sha256 : string } > (
147
+ `
148
+ SELECT blobs.sha256
149
+ FROM blobs
150
+ LEFT JOIN owners ON owners.blob = sha256
151
+ WHERE owners.blob is NULL
152
+ ` ,
153
+ )
154
+ . all ( ) ;
155
+
156
+ if ( blobs . length > 0 ) {
157
+ log ( `Removing ${ blobs . length } because they have no owners` ) ;
158
+ db . prepare < string [ ] > ( `DELETE FROM blobs WHERE sha256 IN ${ mapParams ( blobs ) } ` ) . run ( ...blobs . map ( ( b ) => b . sha256 ) ) ;
159
+ }
160
+ }
140
161
}
141
162
142
163
export default storage ;
0 commit comments