@artilingo/artiframe-cli 1.1.2 → 1.1.3

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,377 +0,0 @@
1
- <?php
2
-
3
- namespace Service;
4
-
5
- use Aws\S3\S3Client;
6
- use Aws\S3\Exception\S3Exception;
7
-
8
- /**
9
- * S3Storage — Wrapper for AWS S3 SDK.
10
- *
11
- * Provides simple methods for uploading, downloading, deleting, listing,
12
- * and managing files on Amazon S3, MinIO, DigitalOcean Spaces, or any
13
- * S3-compatible object storage.
14
- *
15
- * Required ENV variables:
16
- * AWS_ACCESS_KEY_ID — IAM access key
17
- * AWS_SECRET_ACCESS_KEY — IAM secret key
18
- * AWS_DEFAULT_REGION — e.g. "eu-central-1"
19
- * AWS_BUCKET — Default bucket name
20
- *
21
- * Optional ENV variables:
22
- * AWS_ENDPOINT — Custom endpoint URL (MinIO, Spaces, etc.)
23
- *
24
- * @package Service
25
- */
26
- class S3Storage
27
- {
28
- /** @var S3Client AWS S3 client instance */
29
- private S3Client $client;
30
-
31
- /** @var string Default bucket name */
32
- private string $bucket;
33
-
34
- /**
35
- * Create the S3 client from environment variables.
36
- *
37
- * When AWS_ENDPOINT is set the client connects to a custom
38
- * S3-compatible service (MinIO, DigitalOcean Spaces, Cloudflare R2, etc.).
39
- */
40
- public function __construct()
41
- {
42
- $region = $_ENV['AWS_DEFAULT_REGION'] ?? 'us-east-1';
43
- $endpoint = $_ENV['AWS_ENDPOINT'] ?? null;
44
-
45
- $config = [
46
- 'version' => 'latest',
47
- 'region' => $region,
48
- 'credentials' => [
49
- 'key' => $_ENV['AWS_ACCESS_KEY_ID'],
50
- 'secret' => $_ENV['AWS_SECRET_ACCESS_KEY'],
51
- ],
52
- ];
53
-
54
- // Custom endpoint for S3-compatible services
55
- if ($endpoint) {
56
- $config['endpoint'] = $endpoint;
57
- $config['use_path_style_endpoint'] = true;
58
- }
59
-
60
- $this->client = new S3Client($config);
61
- $this->bucket = $_ENV['AWS_BUCKET'];
62
- }
63
-
64
- // ---------------------------------------------------------------
65
- // Upload
66
- // ---------------------------------------------------------------
67
-
68
- /**
69
- * Upload a local file to S3.
70
- *
71
- * @param string $localPath Absolute path to the local file.
72
- * @param string $remotePath Object key (path) in the bucket.
73
- * @param string $acl Canned ACL: private, public-read, etc.
74
- * @return array{status: bool, url: string|null, error: string|null}
75
- */
76
- public function upload(string $localPath, string $remotePath, string $acl = 'private'): array
77
- {
78
- try {
79
- $result = $this->client->putObject([
80
- 'Bucket' => $this->bucket,
81
- 'Key' => $remotePath,
82
- 'SourceFile' => $localPath,
83
- 'ACL' => $acl,
84
- 'ContentType' => mime_content_type($localPath) ?: 'application/octet-stream',
85
- ]);
86
-
87
- return [
88
- 'status' => true,
89
- 'url' => $result['ObjectURL'] ?? $this->url($remotePath),
90
- 'error' => null,
91
- ];
92
- } catch (S3Exception $e) {
93
- error_log('[S3Storage::upload] ' . $e->getMessage());
94
- return ['status' => false, 'url' => null, 'error' => $e->getMessage()];
95
- }
96
- }
97
-
98
- // ---------------------------------------------------------------
99
- // Upload from String
100
- // ---------------------------------------------------------------
101
-
102
- /**
103
- * Upload content from a string directly to S3.
104
- *
105
- * Useful for storing generated data (JSON, CSV, HTML) without
106
- * writing a temporary file to disk first.
107
- *
108
- * @param string $content The raw string content to store.
109
- * @param string $remotePath Object key (path) in the bucket.
110
- * @param string $contentType MIME type, e.g. "application/json".
111
- * @param string $acl Canned ACL: private, public-read, etc.
112
- * @return array{status: bool, url: string|null, error: string|null}
113
- */
114
- public function uploadFromString(string $content, string $remotePath, string $contentType, string $acl = 'private'): array
115
- {
116
- try {
117
- $result = $this->client->putObject([
118
- 'Bucket' => $this->bucket,
119
- 'Key' => $remotePath,
120
- 'Body' => $content,
121
- 'ACL' => $acl,
122
- 'ContentType' => $contentType,
123
- ]);
124
-
125
- return [
126
- 'status' => true,
127
- 'url' => $result['ObjectURL'] ?? $this->url($remotePath),
128
- 'error' => null,
129
- ];
130
- } catch (S3Exception $e) {
131
- error_log('[S3Storage::uploadFromString] ' . $e->getMessage());
132
- return ['status' => false, 'url' => null, 'error' => $e->getMessage()];
133
- }
134
- }
135
-
136
- // ---------------------------------------------------------------
137
- // Download
138
- // ---------------------------------------------------------------
139
-
140
- /**
141
- * Download a file from S3 to a local path.
142
- *
143
- * @param string $remotePath Object key in the bucket.
144
- * @param string $localPath Absolute local destination path.
145
- * @return bool True on success.
146
- */
147
- public function download(string $remotePath, string $localPath): bool
148
- {
149
- try {
150
- $this->client->getObject([
151
- 'Bucket' => $this->bucket,
152
- 'Key' => $remotePath,
153
- 'SaveAs' => $localPath,
154
- ]);
155
-
156
- return true;
157
- } catch (S3Exception $e) {
158
- error_log('[S3Storage::download] ' . $e->getMessage());
159
- return false;
160
- }
161
- }
162
-
163
- // ---------------------------------------------------------------
164
- // Delete
165
- // ---------------------------------------------------------------
166
-
167
- /**
168
- * Delete a single object from S3.
169
- *
170
- * @param string $remotePath Object key to delete.
171
- * @return bool True on success.
172
- */
173
- public function delete(string $remotePath): bool
174
- {
175
- try {
176
- $this->client->deleteObject([
177
- 'Bucket' => $this->bucket,
178
- 'Key' => $remotePath,
179
- ]);
180
-
181
- return true;
182
- } catch (S3Exception $e) {
183
- error_log('[S3Storage::delete] ' . $e->getMessage());
184
- return false;
185
- }
186
- }
187
-
188
- /**
189
- * Delete multiple objects from S3 in a single request.
190
- *
191
- * @param array<string> $remotePaths List of object keys to delete.
192
- * @return bool True when all objects are deleted successfully.
193
- */
194
- public function deleteMany(array $remotePaths): bool
195
- {
196
- try {
197
- $objects = array_map(fn(string $key) => ['Key' => $key], $remotePaths);
198
-
199
- $this->client->deleteObjects([
200
- 'Bucket' => $this->bucket,
201
- 'Delete' => [
202
- 'Objects' => $objects,
203
- 'Quiet' => true,
204
- ],
205
- ]);
206
-
207
- return true;
208
- } catch (S3Exception $e) {
209
- error_log('[S3Storage::deleteMany] ' . $e->getMessage());
210
- return false;
211
- }
212
- }
213
-
214
- // ---------------------------------------------------------------
215
- // URL helpers
216
- // ---------------------------------------------------------------
217
-
218
- /**
219
- * Get the public URL for an object.
220
- *
221
- * @param string $remotePath Object key in the bucket.
222
- * @return string The public URL.
223
- */
224
- public function url(string $remotePath): string
225
- {
226
- return $this->client->getObjectUrl($this->bucket, $remotePath);
227
- }
228
-
229
- /**
230
- * Generate a pre-signed (temporary) URL for private objects.
231
- *
232
- * @param string $remotePath Object key in the bucket.
233
- * @param int $minutes Link validity in minutes (default 60).
234
- * @return string Pre-signed URL.
235
- */
236
- public function signedUrl(string $remotePath, int $minutes = 60): string
237
- {
238
- $command = $this->client->getCommand('GetObject', [
239
- 'Bucket' => $this->bucket,
240
- 'Key' => $remotePath,
241
- ]);
242
-
243
- $request = $this->client->createPresignedRequest($command, "+{$minutes} minutes");
244
-
245
- return (string) $request->getUri();
246
- }
247
-
248
- // ---------------------------------------------------------------
249
- // Existence check
250
- // ---------------------------------------------------------------
251
-
252
- /**
253
- * Check whether an object exists in the bucket.
254
- *
255
- * @param string $remotePath Object key to check.
256
- * @return bool True if the object exists.
257
- */
258
- public function exists(string $remotePath): bool
259
- {
260
- try {
261
- return $this->client->doesObjectExistV2($this->bucket, $remotePath);
262
- } catch (S3Exception $e) {
263
- error_log('[S3Storage::exists] ' . $e->getMessage());
264
- return false;
265
- }
266
- }
267
-
268
- // ---------------------------------------------------------------
269
- // List objects
270
- // ---------------------------------------------------------------
271
-
272
- /**
273
- * List objects under a given prefix (folder).
274
- *
275
- * Returns an array of associative arrays with key, size, and
276
- * last_modified for each object.
277
- *
278
- * @param string $prefix Key prefix to filter by (e.g. "uploads/").
279
- * @return array<int, array{key: string, size: int, last_modified: string}>
280
- */
281
- public function list(string $prefix = ''): array
282
- {
283
- try {
284
- $results = [];
285
- $params = [
286
- 'Bucket' => $this->bucket,
287
- 'Prefix' => $prefix,
288
- ];
289
-
290
- $paginator = $this->client->getPaginator('ListObjectsV2', $params);
291
-
292
- foreach ($paginator as $page) {
293
- $contents = $page['Contents'] ?? [];
294
- foreach ($contents as $object) {
295
- $results[] = [
296
- 'key' => $object['Key'],
297
- 'size' => $object['Size'],
298
- 'last_modified' => $object['LastModified']->format('Y-m-d H:i:s'),
299
- ];
300
- }
301
- }
302
-
303
- return $results;
304
- } catch (S3Exception $e) {
305
- error_log('[S3Storage::list] ' . $e->getMessage());
306
- return [];
307
- }
308
- }
309
-
310
- // ---------------------------------------------------------------
311
- // Copy & Move
312
- // ---------------------------------------------------------------
313
-
314
- /**
315
- * Copy an object to a new key within the same bucket.
316
- *
317
- * @param string $from Source object key.
318
- * @param string $to Destination object key.
319
- * @return bool True on success.
320
- */
321
- public function copy(string $from, string $to): bool
322
- {
323
- try {
324
- $this->client->copyObject([
325
- 'Bucket' => $this->bucket,
326
- 'CopySource' => "{$this->bucket}/{$from}",
327
- 'Key' => $to,
328
- ]);
329
-
330
- return true;
331
- } catch (S3Exception $e) {
332
- error_log('[S3Storage::copy] ' . $e->getMessage());
333
- return false;
334
- }
335
- }
336
-
337
- /**
338
- * Move an object to a new key (copy + delete).
339
- *
340
- * @param string $from Source object key.
341
- * @param string $to Destination object key.
342
- * @return bool True when copy and delete both succeed.
343
- */
344
- public function move(string $from, string $to): bool
345
- {
346
- if ($this->copy($from, $to)) {
347
- return $this->delete($from);
348
- }
349
-
350
- return false;
351
- }
352
-
353
- // ---------------------------------------------------------------
354
- // File size
355
- // ---------------------------------------------------------------
356
-
357
- /**
358
- * Get the size of an object in bytes.
359
- *
360
- * @param string $remotePath Object key in the bucket.
361
- * @return int File size in bytes, or -1 on failure.
362
- */
363
- public function size(string $remotePath): int
364
- {
365
- try {
366
- $result = $this->client->headObject([
367
- 'Bucket' => $this->bucket,
368
- 'Key' => $remotePath,
369
- ]);
370
-
371
- return (int) ($result['ContentLength'] ?? 0);
372
- } catch (S3Exception $e) {
373
- error_log('[S3Storage::size] ' . $e->getMessage());
374
- return -1;
375
- }
376
- }
377
- }
@@ -1,139 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Sentry Error Tracking Configuration
5
- *
6
- * Initializes the Sentry SDK for error reporting and provides
7
- * helper functions for capturing exceptions and messages.
8
- *
9
- * Required ENV variables:
10
- * SENTRY_DSN — Sentry Data Source Name (project DSN from sentry.io)
11
- *
12
- * Optional ENV variables:
13
- * APP_ENV — "0" for production, "1" for development (default: 0)
14
- * APP_VERSION — Application version string, used as Sentry release tag
15
- *
16
- * Usage:
17
- * require_once __DIR__ . '/sentry.php';
18
- *
19
- * // Capture an exception
20
- * try { ... } catch (\Throwable $e) {
21
- * $eventId = captureError($e);
22
- * }
23
- *
24
- * // Capture an informational message
25
- * captureMessage('Deployment completed', 'info');
26
- */
27
-
28
- // ---------------------------------------------------------------
29
- // Guard — only initialize when a DSN is available
30
- // ---------------------------------------------------------------
31
- if (empty($_ENV['SENTRY_DSN'])) {
32
- /**
33
- * No-op fallback when Sentry is not configured.
34
- *
35
- * @param \Throwable $e The exception (ignored).
36
- * @return string|null Always returns null.
37
- */
38
- function captureError(\Throwable $e): ?string
39
- {
40
- return null;
41
- }
42
-
43
- /**
44
- * No-op fallback when Sentry is not configured.
45
- *
46
- * @param string $message The message (ignored).
47
- * @param string $level Severity level (ignored).
48
- * @return string|null Always returns null.
49
- */
50
- function captureMessage(string $message, string $level = 'info'): ?string
51
- {
52
- return null;
53
- }
54
-
55
- return;
56
- }
57
-
58
- // ---------------------------------------------------------------
59
- // Determine environment & sample rate
60
- // ---------------------------------------------------------------
61
-
62
- /** @var bool Whether the application is running in production mode */
63
- $isProduction = ($_ENV['APP_ENV'] ?? '0') === '0';
64
-
65
- /** @var string Human-readable environment name for Sentry */
66
- $environment = $isProduction ? 'production' : 'development';
67
-
68
- /**
69
- * Sample rate:
70
- * 1.0 — capture 100 % of events in production
71
- * 0.0 — capture nothing in development (use captureError/captureMessage explicitly)
72
- */
73
- $sampleRate = $isProduction ? 1.0 : 0.0;
74
-
75
- // ---------------------------------------------------------------
76
- // Sentry initialization
77
- // ---------------------------------------------------------------
78
-
79
- $options = [
80
- 'dsn' => $_ENV['SENTRY_DSN'],
81
- 'environment' => $environment,
82
- 'sample_rate' => $sampleRate,
83
- ];
84
-
85
- // Attach release version when available
86
- if (!empty($_ENV['APP_VERSION'])) {
87
- $options['release'] = $_ENV['APP_VERSION'];
88
- }
89
-
90
- \Sentry\init($options);
91
-
92
- // ---------------------------------------------------------------
93
- // Helper functions
94
- // ---------------------------------------------------------------
95
-
96
- /**
97
- * Capture a Throwable and send it to Sentry.
98
- *
99
- * @param \Throwable $e The exception or error to report.
100
- * @return string|null The Sentry event ID, or null on failure.
101
- */
102
- function captureError(\Throwable $e): ?string
103
- {
104
- try {
105
- $eventId = \Sentry\captureException($e);
106
- return $eventId ? (string) $eventId : null;
107
- } catch (\Throwable $inner) {
108
- error_log('[Sentry::captureError] Failed to report: ' . $inner->getMessage());
109
- return null;
110
- }
111
- }
112
-
113
- /**
114
- * Capture a plain-text message and send it to Sentry.
115
- *
116
- * Supported severity levels: fatal, error, warning, info, debug.
117
- *
118
- * @param string $message Human-readable message to log.
119
- * @param string $level Sentry severity level (default: info).
120
- * @return string|null The Sentry event ID, or null on failure.
121
- */
122
- function captureMessage(string $message, string $level = 'info'): ?string
123
- {
124
- try {
125
- $severity = match ($level) {
126
- 'fatal' => \Sentry\Severity::fatal(),
127
- 'error' => \Sentry\Severity::error(),
128
- 'warning' => \Sentry\Severity::warning(),
129
- 'debug' => \Sentry\Severity::debug(),
130
- default => \Sentry\Severity::info(),
131
- };
132
-
133
- $eventId = \Sentry\captureMessage($message, $severity);
134
- return $eventId ? (string) $eventId : null;
135
- } catch (\Throwable $inner) {
136
- error_log('[Sentry::captureMessage] Failed to report: ' . $inner->getMessage());
137
- return null;
138
- }
139
- }