@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3/**
4 * Amazon S3 file storage engine. This engine scales well but is relatively
5 * high-latency since data has to be pulled off S3.
6 *
7 * @task internal Internals
8 */
9final class PhabricatorS3FileStorageEngine
10 extends PhabricatorFileStorageEngine {
11
12
13/* -( Engine Metadata )---------------------------------------------------- */
14
15
16 /**
17 * This engine identifies as `amazon-s3`.
18 */
19 public function getEngineIdentifier() {
20 return 'amazon-s3';
21 }
22
23 public function getEnginePriority() {
24 return 100;
25 }
26
27 public function canWriteFiles() {
28 $bucket = PhabricatorEnv::getEnvConfig('storage.s3.bucket');
29 $access_key = PhabricatorEnv::getEnvConfig('amazon-s3.access-key');
30 $secret_key = PhabricatorEnv::getEnvConfig('amazon-s3.secret-key');
31 $endpoint = PhabricatorEnv::getEnvConfig('amazon-s3.endpoint');
32 $region = PhabricatorEnv::getEnvConfig('amazon-s3.region');
33
34 return phutil_nonempty_string($bucket) &&
35 phutil_nonempty_string($access_key) &&
36 phutil_nonempty_string($secret_key) &&
37 phutil_nonempty_string($endpoint) &&
38 phutil_nonempty_string($region);
39 }
40
41
42/* -( Managing File Data )------------------------------------------------- */
43
44
45 /**
46 * Writes file data into Amazon S3.
47 */
48 public function writeFile($data, array $params) {
49 $s3 = $this->newS3API();
50
51 // Generate a random name for this file. We add some directories to it
52 // (e.g. 'abcdef123456' becomes 'ab/cd/ef123456') to make large numbers of
53 // files more browsable with web/debugging tools like the S3 administration
54 // tool.
55 $seed = Filesystem::readRandomCharacters(20);
56 $parts = array();
57 $parts[] = 'phabricator';
58
59 $instance_name = PhabricatorEnv::getEnvConfig('cluster.instance');
60 if (phutil_nonempty_string($instance_name)) {
61 $parts[] = $instance_name;
62 }
63
64 $parts[] = substr($seed, 0, 2);
65 $parts[] = substr($seed, 2, 2);
66 $parts[] = substr($seed, 4);
67
68 $name = implode('/', $parts);
69
70 AphrontWriteGuard::willWrite();
71 $profiler = PhutilServiceProfiler::getInstance();
72 $call_id = $profiler->beginServiceCall(
73 array(
74 'type' => 's3',
75 'method' => 'putObject',
76 ));
77
78 $s3
79 ->setParametersForPutObject($name, $data)
80 ->resolve();
81
82 $profiler->endServiceCall($call_id, array());
83
84 return $name;
85 }
86
87
88 /**
89 * Load a stored blob from Amazon S3.
90 */
91 public function readFile($handle) {
92 $s3 = $this->newS3API();
93
94 $profiler = PhutilServiceProfiler::getInstance();
95 $call_id = $profiler->beginServiceCall(
96 array(
97 'type' => 's3',
98 'method' => 'getObject',
99 ));
100
101 $result = $s3
102 ->setParametersForGetObject($handle)
103 ->resolve();
104
105 $profiler->endServiceCall($call_id, array());
106
107 return $result;
108 }
109
110
111 /**
112 * Delete a blob from Amazon S3.
113 */
114 public function deleteFile($handle) {
115 $s3 = $this->newS3API();
116
117 AphrontWriteGuard::willWrite();
118 $profiler = PhutilServiceProfiler::getInstance();
119 $call_id = $profiler->beginServiceCall(
120 array(
121 'type' => 's3',
122 'method' => 'deleteObject',
123 ));
124
125 $s3
126 ->setParametersForDeleteObject($handle)
127 ->resolve();
128
129 $profiler->endServiceCall($call_id, array());
130 }
131
132
133/* -( Internals )---------------------------------------------------------- */
134
135
136 /**
137 * Retrieve the S3 bucket name.
138 *
139 * @task internal
140 */
141 private function getBucketName() {
142 $bucket = PhabricatorEnv::getEnvConfig('storage.s3.bucket');
143 if (!$bucket) {
144 throw new PhabricatorFileStorageConfigurationException(
145 pht(
146 "No '%s' specified!",
147 'storage.s3.bucket'));
148 }
149 return $bucket;
150 }
151
152 /**
153 * Create a new S3 API object.
154 *
155 * @task internal
156 */
157 private function newS3API() {
158 $access_key = PhabricatorEnv::getEnvConfig('amazon-s3.access-key');
159 $secret_key = PhabricatorEnv::getEnvConfig('amazon-s3.secret-key');
160 $region = PhabricatorEnv::getEnvConfig('amazon-s3.region');
161 $endpoint = PhabricatorEnv::getEnvConfig('amazon-s3.endpoint');
162
163 return id(new PhutilAWSS3Future())
164 ->setAccessKey($access_key)
165 ->setSecretKey(new PhutilOpaqueEnvelope($secret_key))
166 ->setRegion($region)
167 ->setEndpoint($endpoint)
168 ->setBucket($this->getBucketName());
169 }
170
171}