@omnifyjp/ts 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,11 @@
1
1
  /**
2
- * Generates the CleanupExpiredFiles artisan command.
2
+ * Generates two-phase file cleanup commands:
3
+ * - Phase 1 (CleanupExpiredFiles): Soft-delete expired temp records — keeps physical files
4
+ * - Phase 2 (PurgeDeletedFiles): Force-delete old soft-deleted records + remove physical files
5
+ *
3
6
  * Only generated when fileConfig.tempFlow is enabled.
4
7
  */
5
8
  import { SchemaReader } from './schema-reader.js';
6
9
  import type { GeneratedFile, PhpConfig } from './types.js';
7
- /** Generate the cleanup command (only when tempFlow is enabled). */
10
+ /** Generate the cleanup commands (only when tempFlow is enabled). */
8
11
  export declare function generateFileCleanup(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
@@ -1,19 +1,40 @@
1
1
  /**
2
- * Generates the CleanupExpiredFiles artisan command.
2
+ * Generates two-phase file cleanup commands:
3
+ * - Phase 1 (CleanupExpiredFiles): Soft-delete expired temp records — keeps physical files
4
+ * - Phase 2 (PurgeDeletedFiles): Force-delete old soft-deleted records + remove physical files
5
+ *
3
6
  * Only generated when fileConfig.tempFlow is enabled.
4
7
  */
5
8
  import { baseFile } from './types.js';
6
- /** Generate the cleanup command (only when tempFlow is enabled). */
9
+ /** Generate the cleanup commands (only when tempFlow is enabled). */
7
10
  export function generateFileCleanup(reader, config) {
8
11
  const fileConfig = reader.getFileConfig();
9
12
  if (!fileConfig?.tempFlow)
10
13
  return [];
11
14
  const modelNamespace = config.models.namespace;
15
+ const files = [];
16
+ files.push(generateCleanupCommand(modelNamespace));
17
+ files.push(generatePurgeCommand(modelNamespace, fileConfig.purgeAfter ?? '72h'));
18
+ return files;
19
+ }
20
+ /** Parse a duration string like "72h" to hours. */
21
+ function parseDurationHours(duration) {
22
+ const match = duration.match(/^(\d+)(h|d)$/);
23
+ if (!match)
24
+ return 72;
25
+ const value = parseInt(match[1], 10);
26
+ const unit = match[2];
27
+ return unit === 'd' ? value * 24 : value;
28
+ }
29
+ function generateCleanupCommand(modelNamespace) {
12
30
  const content = `<?php
13
31
 
14
32
  namespace App\\Console\\Commands;
15
33
 
16
34
  /**
35
+ * Phase 1: Soft-delete expired temporary file records.
36
+ * Physical files are kept on disk for the grace period.
37
+ *
17
38
  * DO NOT EDIT - This file is auto-generated by Omnify.
18
39
  * Any changes will be overwritten on next generation.
19
40
  *
@@ -21,7 +42,6 @@ namespace App\\Console\\Commands;
21
42
  */
22
43
 
23
44
  use Illuminate\\Console\\Command;
24
- use Illuminate\\Support\\Facades\\Storage;
25
45
  use ${modelNamespace}\\File;
26
46
 
27
47
  class CleanupExpiredFiles extends Command
@@ -35,7 +55,7 @@ class CleanupExpiredFiles extends Command
35
55
  /**
36
56
  * The console command description.
37
57
  */
38
- protected $description = 'Delete expired temporary files';
58
+ protected $description = 'Soft-delete expired temporary file records (keeps physical files)';
39
59
 
40
60
  /**
41
61
  * Execute the console command.
@@ -51,29 +71,103 @@ class CleanupExpiredFiles extends Command
51
71
  }
52
72
 
53
73
  if ($this->option('dry-run')) {
54
- $this->info("Would delete {$count} expired file(s).");
74
+ $this->info("Would soft-delete {$count} expired file record(s).");
75
+ return self::SUCCESS;
76
+ }
77
+
78
+ // Soft-delete only — physical files stay on disk
79
+ $deleted = $query->delete();
80
+
81
+ $this->info("Soft-deleted {$deleted} expired file record(s). Physical files retained.");
82
+ return self::SUCCESS;
83
+ }
84
+ }
85
+ `;
86
+ return baseFile('app/Console/Commands/CleanupExpiredFiles.php', content);
87
+ }
88
+ function generatePurgeCommand(modelNamespace, purgeAfter) {
89
+ const hours = parseDurationHours(purgeAfter);
90
+ const content = `<?php
91
+
92
+ namespace App\\Console\\Commands;
93
+
94
+ /**
95
+ * Phase 2: Force-delete soft-deleted file records older than the grace period
96
+ * and remove their physical files from storage.
97
+ *
98
+ * DO NOT EDIT - This file is auto-generated by Omnify.
99
+ * Any changes will be overwritten on next generation.
100
+ *
101
+ * @generated by omnify
102
+ */
103
+
104
+ use Illuminate\\Console\\Command;
105
+ use Illuminate\\Support\\Facades\\Storage;
106
+ use ${modelNamespace}\\File;
107
+
108
+ class PurgeDeletedFiles extends Command
109
+ {
110
+ /**
111
+ * The name and signature of the console command.
112
+ */
113
+ protected $signature = 'omnify:purge-deleted-files
114
+ {--hours=${hours} : Grace period in hours before physical deletion}
115
+ {--dry-run : Show what would be purged without actually purging}';
116
+
117
+ /**
118
+ * The console command description.
119
+ */
120
+ protected $description = 'Force-delete old soft-deleted file records and remove physical files from disk';
121
+
122
+ /**
123
+ * Execute the console command.
124
+ */
125
+ public function handle(): int
126
+ {
127
+ $hours = (int) $this->option('hours');
128
+ $cutoff = now()->subHours($hours);
129
+
130
+ $query = File::onlyTrashed()->where('deleted_at', '<', $cutoff);
131
+ $count = $query->count();
132
+
133
+ if ($count === 0) {
134
+ $this->info("No soft-deleted files older than {$hours}h found.");
135
+ return self::SUCCESS;
136
+ }
137
+
138
+ if ($this->option('dry-run')) {
139
+ $this->info("Would purge {$count} file(s) older than {$hours}h.");
55
140
  return self::SUCCESS;
56
141
  }
57
142
 
58
- $deleted = 0;
59
- $query->chunkById(100, function ($files) use (&$deleted) {
143
+ $purged = 0;
144
+ $errors = 0;
145
+
146
+ $query->chunkById(100, function ($files) use (&$purged, &$errors) {
60
147
  foreach ($files as $file) {
61
- // Delete from storage
62
- if ($file->path && Storage::disk($file->disk)->exists($file->path)) {
63
- Storage::disk($file->disk)->delete($file->path);
148
+ try {
149
+ // Remove physical file from storage
150
+ if ($file->path && Storage::disk($file->disk)->exists($file->path)) {
151
+ Storage::disk($file->disk)->delete($file->path);
152
+ }
153
+ // Force-delete the database record
154
+ $file->forceDelete();
155
+ $purged++;
156
+ } catch (\\Throwable $e) {
157
+ $this->error("Failed to purge file {$file->id}: {$e->getMessage()}");
158
+ $errors++;
64
159
  }
65
- // Soft delete the record
66
- $file->delete();
67
- $deleted++;
68
160
  }
69
161
  });
70
162
 
71
- $this->info("Deleted {$deleted} expired file(s).");
72
- return self::SUCCESS;
163
+ $this->info("Purged {$purged} file(s). Physical files removed from disk.");
164
+ if ($errors > 0) {
165
+ $this->warn("{$errors} file(s) failed to purge.");
166
+ }
167
+
168
+ return $errors > 0 ? self::FAILURE : self::SUCCESS;
73
169
  }
74
170
  }
75
171
  `;
76
- return [
77
- baseFile('app/Console/Commands/CleanupExpiredFiles.php', content),
78
- ];
172
+ return baseFile('app/Console/Commands/PurgeDeletedFiles.php', content);
79
173
  }
package/dist/types.d.ts CHANGED
@@ -10,6 +10,8 @@ export interface FileConfigExport {
10
10
  readonly tempFlow?: boolean;
11
11
  readonly tempTtl?: string;
12
12
  readonly cleanupSchedule?: string;
13
+ readonly purgeAfter?: string;
14
+ readonly purgeSchedule?: string;
13
15
  readonly defaultDisk?: string;
14
16
  }
15
17
  /** Top-level schemas.json structure. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnifyjp/ts",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "TypeScript model type generator from Omnify schemas.json",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",