PluginProbe
Media Cloud Sync / 1.2.3
Media Cloud Sync v1.2.3
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / base / services / s3compatible.php

s3compatible.php in Media Cloud Sync 1.2.3, at includes/base/services/s3compatible.php

734 lines 28.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Dudlewebs\WPMCS;
3
4 defined('ABSPATH') || exit;
5
6 // Libraries
7 use Dudlewebs\WPMCS\s3\Aws\S3\S3Client;
8 use Dudlewebs\WPMCS\s3\Aws\Exception\AwsException;
9 use Dudlewebs\WPMCS\s3\Aws\S3\Exception\S3Exception;
10 use Dudlewebs\WPMCS\s3\Aws\S3\MultipartUploader;
11 use Dudlewebs\WPMCS\s3\Aws\Exception\MultipartUploadException;
12 use Exception;
13
14 class S3Compatible {
15 private $assets_url;
16 private $version;
17 private $token;
18
19 protected $config;
20 protected $bucketConfig;
21 protected $settings;
22 protected $credentials;
23 protected $bucket_name;
24
25 public $service = 's3compatible';
26 public $S3Client = false;
27
28 /**
29 * Admin constructor.
30 * @since 1.0.0
31 */
32 public function __construct() {
33 $this->assets_url = WPMCS_ASSETS_URL;
34 $this->version = WPMCS_VERSION;
35 $this->token = WPMCS_TOKEN;
36
37 // Initialize setup
38 $this->init();
39 }
40
41 /**
42 * Initialise Client
43 */
44 public function init() {
45 $this->settings = Utils::get_settings();
46 $this->credentials = Utils::get_credentials();
47 $this->config = isset($this->credentials['config']) && !empty($this->credentials['config'])
48 ? $this->credentials['config']
49 : [];
50 $this->bucketConfig = isset($this->credentials['bucketConfig']) && !empty($this->credentials['bucketConfig'])
51 ? $this->credentials['bucketConfig']
52 : [];
53 $this->bucket_name = isset($this->bucketConfig['bucket_name']) && !empty($this->bucketConfig['bucket_name'])
54 ? $this->bucketConfig['bucket_name']
55 : '';
56
57 if (
58 isset($this->config['region']) && !empty($this->config['region']) &&
59 isset($this->config['access_key']) && !empty($this->config['access_key']) &&
60 isset($this->config['secret_key']) && !empty($this->config['secret_key']) &&
61 isset($this->config['endpoint']) && !empty($this->config['endpoint'])
62 ) {
63 $this->S3Client = new S3Client([
64 'version' => '2006-03-01',
65 'region' => $this->config['region'],
66 'endpoint' => $this->config['endpoint'], // DigitalOcean Spaces requires a custom endpoint
67 'use_accelerate_endpoint' => isset($this->bucketConfig['transfer_acceleration'])
68 ? $this->bucketConfig['transfer_acceleration'] : false,
69 'use_path_style_endpoint' => true, // DigitalOcean Spaces often requires path-style endpoints
70 'use_aws_shared_config_files' => false,
71 'credentials' => [
72 'key' => $this->config['access_key'],
73 'secret' => $this->config['secret_key'],
74 ],
75 ]);
76 }
77
78 }
79
80
81 /**
82 * Verify Credentials
83 * @since 1.0.0
84 * @return boolean
85 */
86 public function verifyCredentials($endpoint, $access_key, $secret_key, $region){
87 if (
88 isset($region) && !empty($region) &&
89 isset($access_key) && !empty($access_key) &&
90 isset($secret_key) && !empty($secret_key) &&
91 isset($endpoint) && !empty($endpoint)
92 ) {
93 try {
94 $S3Client = new S3Client([
95 'version' => '2006-03-01',
96 'region' => $region ? $region : 'us-east-1',
97 'endpoint' => $endpoint,
98 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
99 'use_accelerate_endpoint' => false,
100 'use_aws_shared_config_files' => false,
101 'credentials' => [
102 'key' => $access_key,
103 'secret' => $secret_key,
104 ],
105 ]);
106
107 $result = [
108 'success' => false,
109 'code' => 200,
110 'message' => esc_html__('Please check the authorization details', 'media-cloud-sync'),
111 ];
112
113 try {
114 $S3Client->listObjectsV2([
115 'Bucket' => $this->token . '_dummy-bucket-for-auth-check'
116 ]);
117
118 // If we reach here, the credentials are valid
119 $result = [
120 'success' => true,
121 'code' => 200,
122 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'),
123 ];
124 } catch (AwsException $e) {
125 $code = $e->getAwsErrorCode();
126
127 $validErrors = [
128 'AccessDenied',
129 'NoSuchBucket',
130 'AllAccessDisabled',
131 'AuthorizationHeaderMalformed',
132 'PermanentRedirect',
133 ];
134
135 if (in_array($code, $validErrors)) {
136 // If we reach here, the credentials are valid
137 $result = [
138 'success' => true,
139 'code' => 200,
140 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'),
141 ];
142 }
143 }
144
145 if($result['success'] == false) {
146 return $result;
147 }
148 $result['buckets_data']['buckets'] = [];
149 $result['buckets_data']['message'] = esc_html__('Buckets listed successfully', 'media-cloud-sync');
150 $result['buckets_data']['status'] = true;
151 return $result;
152 } catch (S3Exception $ex) {
153 return array('message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
154 } catch (Exception $ex) {
155 return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
156 }
157 }
158 return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false);
159 }
160
161 /**
162 * Verify Bucket
163 * @since 1.0.0
164 * @return boolean
165 */
166 public function verifyBucketExist($endpoint, $access_key, $secret_key, $region, $bucket_name){
167 if (
168 isset($endpoint) && !empty($endpoint) &&
169 isset($access_key) && !empty($access_key) &&
170 isset($secret_key) && !empty($secret_key) &&
171 isset($bucket_name) && !empty($bucket_name)
172 ) {
173 try {
174 $S3Client = new S3Client([
175 'version' => '2006-03-01',
176 'region' => $region ? $region : 'us-east-1',
177 'endpoint' => $endpoint,
178 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
179 'use_accelerate_endpoint' => false,
180 'use_aws_shared_config_files' => false,
181 'credentials' => [
182 'key' => $access_key,
183 'secret' => $secret_key,
184 ],
185 ]);
186
187 //get S3 object
188 $bucket_found = false;
189 try {
190 $S3Client->getObject([
191 'Bucket' => $bucket_name,
192 'Key' => $this->token . '_dummy-object-for-bucket-exist-check'
193 ]);
194 $bucket_found = true;
195 } catch (AwsException $e) {
196 $code = $e->getAwsErrorCode();
197 if ($code === 'NoSuchKey') {
198 $bucket_found = true;
199 }
200 }
201
202 if($bucket_found) {
203 return array('message' => esc_html__('Bucket exist', 'media-cloud-sync'), 'code' => 200, 'success' => true);
204 } else {
205 return array('message' => esc_html__("Bucket choosen does not exist / does not have read permission", 'media-cloud-sync'), 'code' => 200, 'success' => false);
206 }
207 }
208 catch (S3Exception $ex) {
209 return array('message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
210 } catch (Exception $ex) {
211 return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
212 }
213 }
214 return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false);
215 }
216
217 /**
218 * Create Bucket
219 * @since 1.0.0
220 * @return boolean
221 */
222 public function createBucket($endpoint, $access_key, $secret_key, $region, $bucket_name){
223 if (empty($endpoint) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
224 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
225 }
226
227 try {
228 $S3Client = new S3Client([
229 'version' => '2006-03-01',
230 'region' => $region ? $region : 'us-east-1',
231 'endpoint' => $endpoint,
232 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
233 'use_accelerate_endpoint' => false,
234 'use_aws_shared_config_files' => false,
235 'credentials' => [
236 'key' => $access_key,
237 'secret' => $secret_key,
238 ],
239 ]);
240
241 // Create Bucket
242 $S3Client->createBucket([
243 'Bucket' => $bucket_name,
244 ]);
245
246 // Optionally wait for bucket existence (recommended)
247 $S3Client->waitUntil('BucketExists', ['Bucket' => $bucket_name]);
248
249 try {
250 $this->putBucketPolicy($bucket_name, $S3Client);
251
252 return [
253 'message' => esc_html__('Bucket created successfully.', 'media-cloud-sync'),
254 'data' => [
255 'Name' => $bucket_name,
256 'CreationDate' => date('Y-m-d\TH:i:s\Z'),
257 ],
258 'code' => 200,
259 'success' => true,
260 ];
261
262 } catch (AwsException $ex) {
263 return ['message' => esc_html__('Bucket created. But the following error happened while setting the public access,', 'media-cloud-sync') . ' ' . $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
264 }
265 } catch (AwsException $ex) {
266 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
267 } catch (S3Exception $ex) {
268 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
269 } catch (Exception $ex) {
270 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
271 }
272 }
273
274
275 /**
276 * Add Bucket Policy
277 */
278 private function putBucketPolicy($bucket, $S3Client = false) {
279 if($S3Client == false) {
280 $S3Client = $this->S3Client;
281 }
282
283 if(empty($bucket)) return false;
284
285 $policy = json_encode([
286 "Version" => "2012-10-17",
287 "Statement" => [
288 [
289 "Effect" => "Allow",
290 "Principal" => "*",
291 "Action" => [
292 "s3:DeleteObjectTagging",
293 "s3:ListBucketMultipartUploads",
294 "s3:DeleteObjectVersion",
295 "s3:ListBucket",
296 "s3:DeleteObjectVersionTagging",
297 "s3:GetBucketAcl",
298 "s3:ListMultipartUploadParts",
299 "s3:PutObject",
300 "s3:GetObjectAcl",
301 "s3:GetObject",
302 "s3:AbortMultipartUpload",
303 "s3:DeleteObject",
304 "s3:GetBucketLocation",
305 "s3:PutObjectAcl",
306 "s3:putBucketOwnershipControls",
307 "s3:putBucketPolicy"
308 ],
309 "Resource" => [
310 "arn:aws:s3:::$bucket/*",
311 "arn:aws:s3:::$bucket"
312 ]
313 ]
314 ]
315 ]);
316
317 // Add bucket policy
318 $S3Client->putBucketPolicy(['Bucket' => $bucket, 'Policy' => $policy]);
319
320 return true;
321 }
322
323
324
325 /**
326 * Check Bucket Write Permission
327 * @since 1.0.0
328 */
329 public function verifyObjectWritePermission($endpoint, $access_key, $secret_key, $region, $bucket_name){
330 if (empty($endpoint) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
331 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
332 }
333
334 try {
335 $S3Client = new S3Client([
336 'version' => '2006-03-01',
337 'region' => $region ? $region : 'us-east-1',
338 'endpoint' => $endpoint,
339 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
340 'use_accelerate_endpoint' => false,
341 'use_aws_shared_config_files' => false,
342 'credentials' => [
343 'key' => $access_key,
344 'secret' => $secret_key,
345 ],
346 ]);
347
348 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
349
350
351 // Create a dummy object to check write permission
352 $S3Client->putObject([
353 'Bucket' => $bucket_name,
354 'Key' => $object_key,
355 'Body' => 'This is a test object to check write permission.',
356 ]);
357 // Check if the object was created successfully
358 if ($S3Client->doesObjectExist($bucket_name, $object_key)) {
359 return ['message' => esc_html__('Bucket write permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true];
360 } else {
361 return ['message' => esc_html__('Bucket write permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false];
362 }
363
364 } catch (AwsException $ex) {
365 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
366 } catch (S3Exception $ex) {
367 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
368 } catch (Exception $ex) {
369 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
370 }
371 }
372
373
374
375 /**
376 * Check Bucket Delete Permission
377 * @since 1.0.0
378 */
379 public function verifyObjectDeletePermission($endpoint, $access_key, $secret_key, $region, $bucket_name){
380 if (empty($endpoint) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
381 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
382 }
383
384 try {
385 $S3Client = new S3Client([
386 'version' => '2006-03-01',
387 'region' => $region ? $region : 'us-east-1',
388 'endpoint' => $endpoint,
389 'use_path_style_endpoint' => true, // Required for DigitalOcean Spaces
390 'use_accelerate_endpoint' => false,
391 'use_aws_shared_config_files' => false,
392 'credentials' => [
393 'key' => $access_key,
394 'secret' => $secret_key,
395 ],
396 ]);
397
398 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
399
400 // Create a dummy object to check dlete permission
401 $S3Client->deleteObject([
402 'Bucket' => $bucket_name,
403 'Key' => $object_key,
404 ]);
405
406 // Check if the object was created successfully
407 if (!$S3Client->doesObjectExist($bucket_name, $object_key)) {
408 return ['message' => esc_html__('Bucket delete permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true];
409 } else {
410 return ['message' => esc_html__('Bucket delete permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false];
411 }
412
413 } catch (AwsException $ex) {
414 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
415 } catch (S3Exception $ex) {
416 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
417 } catch (Exception $ex) {
418 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
419 }
420 }
421
422 /**
423 * isConfigured Function To Identify the congfigurations are correct
424 * @since 1.0.0
425 */
426 public function isConfigured(){
427 if ($this->S3Client) {
428 try {
429 $buckets = $S3Client->listBuckets();
430 if(!empty($buckets)){
431 foreach($buckets as $bucket) {
432 if ($bucket['Name']==$this->bucket_name) {
433 return true;
434 }
435 }
436 }
437 return false;
438 } catch (AwsException $ex) {
439 return false;
440 }
441 }
442 return false;
443 }
444
445 /**
446 * Make Object Private
447 * @since 1.0.0
448 *
449 */
450 public function toPrivate($key) {
451 if(!$key) return false;
452 try {
453 $this->S3Client->putObjectAcl([
454 'Bucket' => $this->bucket_name,
455 'Key' => $key,
456 'ACL' => 'private'
457 ]);
458 return true;
459 } catch (AwsException $ex) {
460 return false;
461 }
462 return false;
463 }
464
465
466
467 /**
468 * Make Object Public
469 * @since 1.0.0
470 *
471 */
472 public function toPublic($key) {
473 if(!$key) return false;
474 try {
475 $this->S3Client->putObjectAcl([
476 'Bucket' => $this->bucket_name,
477 'Key' => $key,
478 'ACL' => 'public-read'
479 ]);
480 return true;
481 } catch (AwsException $ex) {
482 return false;
483 }
484 return false;
485 }
486
487
488
489 /**
490 * Check the object exist
491 * @since 1.1.8
492 */
493 public function exists($key) {
494 if(!$key) return false;
495
496 if($this->S3Client->doesObjectExist($this->bucket_name, $key)) {
497 return true;
498 }
499
500 return false;
501 }
502
503 /**
504 * Upload Single
505 * @since 1.0.0
506 * @return boolean
507 */
508 public function uploadSingle($media_absolute_path, $media_path, $prefix='') {
509 $result = array();
510 if (
511 isset($media_absolute_path) && !empty($media_absolute_path) &&
512 isset($media_path) && !empty($media_path)
513 ) {
514 $file_name = wp_basename( $media_path );
515 if ($file_name) {
516 $upload_path = Utils::generate_object_key($media_path, $prefix);
517
518 // Decide Multipart upload or normal put object
519 if (filesize($media_absolute_path) <= Schema::getConstant('DOCEAN_MULTIPART_MIN_FILE_SIZE')) {
520 // Upload a publicly accessible file. The file size and type are determined by the SDK.
521 try {
522 $upload = $this->S3Client->putObject([
523 'Bucket' => $this->bucket_name,
524 'Key' => $upload_path,
525 'Body' => fopen($media_absolute_path, 'r'),
526 ]);
527
528 $result = array(
529 'success' => true,
530 'code' => 200,
531 'file_url' => $this->generate_file_url($upload_path),
532 'key' => $upload_path,
533 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync')
534 );
535 } catch (AwsException $e) {
536 $result = array(
537 'success' => false,
538 'code' => 200,
539 'message' => $e->getMessage()
540 );
541 }
542 } else {
543 $multiUploader = new MultipartUploader($this->S3Client, $media_absolute_path, [
544 'bucket' => $this->bucket_name,
545 'key' => $upload_path
546 ]);
547
548 try {
549 do {
550 try {
551 $uploaded = $multiUploader->upload();
552 } catch (MultipartUploadException $e) {
553 $multiUploader = new MultipartUploader($this->S3Client, $media_absolute_path, [
554 'state' => $e->getState(),
555 ]);
556 }
557 } while (!isset($uploaded));
558
559 if (isset($uploaded['ObjectURL']) && !empty($uploaded['ObjectURL'])) {
560 $result = array(
561 'success' => true,
562 'code' => 200,
563 'file_url' => $this->generate_file_url($upload_path),
564 'key' => $upload_path,
565 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync')
566 );
567 } else {
568 $result = array(
569 'success' => false,
570 'code' => 200,
571 'message' => esc_html__('Something happened while uploading to server', 'media-cloud-sync')
572 );
573 }
574 } catch (MultipartUploadException $e) {
575 $result = array(
576 'success' => false,
577 'code' => 200,
578 'message' => $e->getMessage()
579 );
580 }
581 }
582 } else {
583 $result = array(
584 'success' => false,
585 'code' => 200,
586 'message' => esc_html__('Check the file you are trying to upload. Please try again', 'media-cloud-sync')
587 );
588 }
589 } else {
590 $result = array(
591 'success' => false,
592 'code' => 200,
593 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
594 );
595 }
596 return $result;
597 }
598
599 /**
600 * Save object to server
601 * @since 1.0.0
602 */
603 public function object_to_server($key, $save_path) {
604 try {
605 $getObject = $this->S3Client->GetObject([
606 'Bucket' => $this->bucket_name,
607 'Key' => $key,
608 'SaveAs' => $save_path
609 ]);
610 if (file_exists($save_path)) {
611 return true;
612 }
613 } catch (AwsException $e) {
614 return false;
615 }
616 return false;
617 }
618
619
620 /**
621 * Delete Single
622 * @since 1.0.0
623 * @return boolean
624 */
625 public function deleteSingle($key) {
626 $result = array();
627 if (isset($key) && !empty($key)) {
628 try {
629 $this->S3Client->deleteObject([
630 'Bucket' => $this->bucket_name,
631 'Key' => $key
632 ]);
633
634 if (!$this->S3Client->doesObjectExist($this->bucket_name, $key)) {
635 $result = array(
636 'success' => true,
637 'code' => 200,
638 'message' => esc_html__('Deleted Successfully', 'media-cloud-sync')
639 );
640 } else {
641 $result = array(
642 'success' => false,
643 'code' => 200,
644 'message' => esc_html__('File not deleted', 'media-cloud-sync')
645 );
646 }
647 } catch (AwsException $e) {
648 $result = array(
649 'success' => false,
650 'code' => 200,
651 'message' => $e->getMessage()
652 );
653 }
654 } else {
655 $result = array(
656 'success' => false,
657 'code' => 200,
658 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
659 );
660 }
661 return $result;
662 }
663
664 /**
665 * get presigned URL
666 * @since 1.0.0
667 * @return boolean
668 */
669 public function get_presigned_url($key) {
670 $result = array();
671 if (isset($key) && !empty($key)) {
672 try {
673 $cmd = $this->S3Client->getCommand('GetObject', [
674 'Bucket' => $this->bucket_name,
675 'Key' => $key
676 ]);
677
678 $expires = isset($this->settings['presigned_expire']) ? $this->settings['presigned_expire'] : 20;
679
680 $request = $this->S3Client->createPresignedRequest($cmd, sprintf('+%s minutes', $expires));
681
682 if ($presignedUrl = (string)$request->getUri()) {
683 $result = array(
684 'success' => true,
685 'code' => 200,
686 'file_url' => $presignedUrl,
687 'message' => esc_html__('Got Presigned URL Successfully', 'media-cloud-sync')
688 );
689 } else {
690 $result = array(
691 'success' => false,
692 'code' => 200,
693 'message' => esc_html__('Error getting presigned URL', 'media-cloud-sync')
694 );
695 }
696 } catch (AwsException $e) {
697 $result = array(
698 'success' => false,
699 'code' => 200,
700 'message' => $e->getMessage()
701 );
702 }
703 } else {
704 $result = array(
705 'success' => false,
706 'code' => 200,
707 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
708 );
709 }
710 return $result;
711 }
712
713 /**
714 * Generate file URL
715 */
716 private function generate_file_url($key){
717 $domain = $this->get_domain();
718
719 return apply_filters('wpmcs_generate_do_file_url',
720 $domain . '/' . $this->bucket_name . '/' . $key,
721 $domain,
722 $this->bucket_name,
723 $key
724 );
725 }
726
727 /**
728 * Get domain URL
729 */
730 public function get_domain($region = '') {
731 return $this->config['endpoint'];
732 }
733
734 }