Skip to content

Commit 3317ce2

Browse files
authored
Merge pull request #1236 from nupplaphil/feat/s3
Add S3 Storage Backend
2 parents 95fcf98 + 3425cd3 commit 3317ce2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+8107
-0
lines changed

s3_storage/composer.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "friendica-addons/s3_storage",
3+
"description": "Adds the possibility to use S3 as a selectable storage backend",
4+
"type": "friendica-addon",
5+
"authors": [
6+
{
7+
"name": "Philipp Holzer",
8+
"email": "[email protected]",
9+
"homepage": "https://blog.philipp.info",
10+
"role": "Developer"
11+
}
12+
],
13+
"require": {
14+
"php": ">=7.0",
15+
"akeeba/s3": "^2.0"
16+
},
17+
"license": "3-clause BSD license",
18+
"config": {
19+
"optimize-autoloader": true,
20+
"autoloader-suffix": "S3StorageAddon",
21+
"preferred-install": "dist"
22+
}
23+
}

s3_storage/composer.lock

Lines changed: 65 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

s3_storage/lang/C/messages.po

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# ADDON s3_storage
2+
# Copyright (C)
3+
# This file is distributed under the same license as the Friendica s3_storage addon package.
4+
#
5+
#
6+
#, fuzzy
7+
msgid ""
8+
msgstr ""
9+
"Project-Id-Version: \n"
10+
"Report-Msgid-Bugs-To: \n"
11+
"POT-Creation-Date: 2022-02-24 23:25+0100\n"
12+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14+
"Language-Team: LANGUAGE <[email protected]>\n"
15+
"Language: \n"
16+
"MIME-Version: 1.0\n"
17+
"Content-Type: text/plain; charset=UTF-8\n"
18+
"Content-Transfer-Encoding: 8bit\n"
19+
20+
#: src/S3Config.php:113
21+
msgid "Access Key"
22+
msgstr ""
23+
24+
#: src/S3Config.php:115
25+
msgid "Set the Access Key of the S3 storage"
26+
msgstr ""
27+
28+
#: src/S3Config.php:120
29+
msgid "Secret Key"
30+
msgstr ""
31+
32+
#: src/S3Config.php:122
33+
msgid "Set the Secret Key of the S3 storage"
34+
msgstr ""
35+
36+
#: src/S3Config.php:127
37+
msgid "Bucket"
38+
msgstr ""
39+
40+
#: src/S3Config.php:129
41+
msgid "The S3 Bucket (name), you want to use with Friendica"
42+
msgstr ""
43+
44+
#: src/S3Config.php:134
45+
msgid "Signature Method"
46+
msgstr ""
47+
48+
#: src/S3Config.php:136
49+
msgid ""
50+
"Set the signature method to use (BEWARE: v4 will be automatically set to v2 "
51+
"in case the endpoint isn't amazon)"
52+
msgstr ""
53+
54+
#: src/S3Config.php:141
55+
#, php-format
56+
msgid "Amazon S3 compatible endpoint (leave empty for '%s')"
57+
msgstr ""
58+
59+
#: src/S3Config.php:143
60+
msgid ""
61+
"Set the S3 endpoint. Do NOT use a protocol (You can use a custom endpoint "
62+
"with v2 signatures to access third party services which offer S3 "
63+
"compatibility, e.g. OwnCloud, Google Storage etc.)"
64+
msgstr ""
65+
66+
#: src/S3Config.php:147
67+
#, php-format
68+
msgid "AWS region (leave empty for '%s')"
69+
msgstr ""
70+
71+
#: src/S3Config.php:149
72+
msgid "The AWS region is mandatory for v4 signatures"
73+
msgstr ""
74+
75+
#: src/S3Config.php:153
76+
msgid "Use the dualstack URL (which will ship traffic over ipv6 in most cases)"
77+
msgstr ""
78+
79+
#: src/S3Config.php:155
80+
msgid ""
81+
"For more information on these endpoints please read https://docs.aws.amazon."
82+
"com/AmazonS3/latest/dev/dual-stack-endpoints.html"
83+
msgstr ""
84+
85+
#: src/S3Config.php:159
86+
msgid "Use legacy, path-style access to the bucket"
87+
msgstr ""
88+
89+
#: src/S3Config.php:161
90+
msgid ""
91+
"When it's turned off (default) we use virtual hosting stylepaths which are "
92+
"RECOMMENDED BY AMAZON per http://docs.aws.amazon.com/AmazonS3/latest/API/"
93+
"APIRest.html"
94+
msgstr ""
95+
96+
#: src/S3Config.php:175
97+
msgid "Invalid input"
98+
msgstr ""
99+
100+
#: src/S3Config.php:214
101+
#, php-format
102+
msgid "error '%s', message '%s'"
103+
msgstr ""
104+
105+
#: src/S3Config.php:222
106+
#, php-format
107+
msgid "Bucket %s cannot be not found, possible buckets: %s"
108+
msgstr ""

s3_storage/s3_storage.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/*
3+
* Name: S3 Storage
4+
* Description: Adds the possibility to use Amazon S3 as a selectable storage backend
5+
* Version: 1.0
6+
* Author: Philipp Holzer
7+
*/
8+
9+
use Friendica\Addon\s3_storage\src\S3Client;
10+
use Friendica\Addon\s3_storage\src\S3Config;
11+
use Friendica\App;
12+
use Friendica\Core\Hook;
13+
use Friendica\DI;
14+
15+
require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
16+
17+
function s3_storage_install($a)
18+
{
19+
Hook::register('storage_instance' , __FILE__, 's3_storage_instance');
20+
Hook::register('storage_config' , __FILE__, 's3_storage_config');
21+
DI::storageManager()->register(S3Client::class);
22+
}
23+
24+
function s3_storage_uninstall()
25+
{
26+
DI::storageManager()->unregister(S3Client::class);
27+
}
28+
29+
function s3_storage_instance(App $a, array &$data)
30+
{
31+
if ($data['name'] == S3Client::getName()) {
32+
$config = new S3Config(DI::l10n(), DI::config());
33+
$data['storage'] = new S3Client($config->getConfig(), $config->getBucket());
34+
}
35+
}
36+
37+
function s3_storage_config(App $a, array &$data)
38+
{
39+
if ($data['name'] == S3Client::getName()) {
40+
$data['storage_config'] = new S3Config(DI::l10n(), DI::config());
41+
}
42+
}

s3_storage/src/S3Client.php

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
namespace Friendica\Addon\s3_storage\src;
4+
5+
defined('AKEEBAENGINE') or define('AKEEBAENGINE', 1);
6+
7+
use Akeeba\Engine\Postproc\Connector\S3v4\Configuration;
8+
use Akeeba\Engine\Postproc\Connector\S3v4\Connector;
9+
use Akeeba\Engine\Postproc\Connector\S3v4\Exception\CannotDeleteFile;
10+
use Akeeba\Engine\Postproc\Connector\S3v4\Input;
11+
use Friendica\Core\Storage\Capability\ICanWriteToStorage;
12+
use Friendica\Core\Storage\Exception\StorageException;
13+
use Friendica\Util\Strings;
14+
15+
/**
16+
* A WebDav Backend Storage class
17+
*/
18+
class S3Client implements ICanWriteToStorage
19+
{
20+
const NAME = 'S3';
21+
22+
/** @var Connector */
23+
protected $connector;
24+
25+
/** @var string The name of the bucket used for the backend */
26+
protected $bucket;
27+
28+
public function __construct(Configuration $config, string $bucket)
29+
{
30+
$this->connector = new Connector($config);
31+
$this->bucket = $bucket;
32+
}
33+
34+
/**
35+
* Split data ref and return file path
36+
*
37+
* @param string $reference Data reference
38+
*
39+
* @return string
40+
*/
41+
private function pathForRef(string $reference): string
42+
{
43+
$fold1 = substr($reference, 0, 2);
44+
$fold2 = substr($reference, 2, 2);
45+
$file = substr($reference, 4);
46+
47+
return implode('/', [$fold1, $fold2, $file]);
48+
}
49+
50+
/** {@inheritDoc} */
51+
public function __toString(): string
52+
{
53+
return self::getName();
54+
}
55+
56+
/** {@inheritDoc} */
57+
public static function getName(): string
58+
{
59+
return self::NAME;
60+
}
61+
62+
/** {@inheritDoc} */
63+
public function get(string $reference): string
64+
{
65+
try {
66+
return $this->connector->getObject($this->bucket, $this->pathForRef($reference), false);
67+
} catch (\RuntimeException $exception) {
68+
throw new StorageException(sprintf('Cannot get reference %s', $reference), $exception->getCode(), $exception);
69+
}
70+
}
71+
72+
/** {@inheritDoc} */
73+
public function put(string $data, string $reference = ""): string
74+
{
75+
if ($reference === '') {
76+
try {
77+
$reference = Strings::getRandomHex();
78+
} catch (\Exception $exception) {
79+
throw new StorageException('S3 storage failed to generate a random hex', $exception->getCode(), $exception);
80+
}
81+
}
82+
83+
try {
84+
$input = Input::createFromData($data);
85+
$this->connector->putObject($input, $this->bucket, $this->pathForRef($reference));
86+
return $reference;
87+
} catch (\Exception $exception) {
88+
throw new StorageException(sprintf('Cannot put data for reference %s', $reference), $exception->getCode(), $exception);
89+
}
90+
}
91+
92+
/** {@inheritDoc} */
93+
public function delete(string $reference)
94+
{
95+
try {
96+
$this->connector->deleteObject($this->bucket, $this->pathForRef($reference));
97+
} catch (CannotDeleteFile $exception) {
98+
throw new StorageException(sprintf('Cannot delete reference %s', $reference), $exception->getCode(), $exception);
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)