1
0
Fork 0
mirror of synced 2024-06-29 03:30:34 +12:00
appwrite/app/controllers/api/avatars.php

391 lines
15 KiB
PHP
Raw Normal View History

2019-05-09 18:54:39 +12:00
<?php
2020-03-29 01:42:16 +13:00
global $utopia, $request, $response;
2019-05-09 18:54:39 +12:00
use Utopia\Exception;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Range;
use Utopia\Validator\URL;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Filesystem;
use Appwrite\Resize\Resize;
2020-04-09 01:38:36 +12:00
use Appwrite\URL\URL as URLParse;
2019-05-09 18:54:39 +12:00
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
2020-03-29 01:42:16 +13:00
use Utopia\Config\Config;
2019-05-09 18:54:39 +12:00
2019-12-17 08:35:33 +13:00
include_once __DIR__ . '/../shared/api.php';
2019-11-30 07:26:06 +13:00
2019-05-09 18:54:39 +12:00
$types = [
2019-12-17 08:35:33 +13:00
'browsers' => include __DIR__.'/../../config/avatars/browsers.php',
'credit-cards' => include __DIR__.'/../../config/avatars/credit-cards.php',
'flags' => include __DIR__.'/../../config/avatars/flags.php',
2019-05-09 18:54:39 +12:00
];
$avatarCallback = function ($type, $code, $width, $height, $quality) use ($types, $response, $request) {
2019-05-09 18:54:39 +12:00
$code = strtolower($code);
$type = strtolower($type);
if (!array_key_exists($type, $types)) {
2019-05-09 18:54:39 +12:00
throw new Exception('Avatar set not found', 404);
}
if (!array_key_exists($code, $types[$type])) {
2019-05-09 18:54:39 +12:00
throw new Exception('Avatar not found', 404);
}
if (!extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$output = 'png';
$date = date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = md5('/v1/avatars/:type/:code-'.$code.$width.$height.$quality.$output);
$path = $types[$type][$code];
$type = 'png';
2019-05-09 18:54:39 +12:00
2019-11-13 11:39:07 +13:00
if (!is_readable($path)) {
2019-11-13 11:40:14 +13:00
throw new Exception('File not readable in '.$path, 500);
2019-05-09 18:54:39 +12:00
}
2020-02-20 01:41:23 +13:00
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */);
2019-05-09 18:54:39 +12:00
if ($data) {
2019-05-09 18:54:39 +12:00
//$output = (empty($output)) ? $type : $output;
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data, 0)
;
}
$resize = new Resize(file_get_contents($path));
$resize->crop((int) $width, (int) $height);
2019-05-09 18:54:39 +12:00
$output = (empty($output)) ? $type : $output;
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('', null)
;
$data = $resize->output($output, $quality);
2019-11-17 10:17:23 +13:00
$cache->save($key, $data);
2019-05-09 18:54:39 +12:00
echo $data;
unset($resize);
exit(0);
};
$utopia->get('/v1/avatars/credit-cards/:code')
->desc('Get Credit Card Icon')
->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['credit-cards'])); }, 'Credit Card Code. Possible values: '.implode(', ', array_keys($types['credit-cards'])).'.')
2020-02-10 18:54:26 +13:00
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2020-01-27 19:14:14 +13:00
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getCreditCard')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-09 21:31:51 +13:00
->label('sdk.description', '/docs/references/avatars/get-credit-card.md')
->action(function ($code, $width, $height, $quality) use ($avatarCallback) { return $avatarCallback('credit-cards', $code, $width, $height, $quality);
});
2019-05-09 18:54:39 +12:00
$utopia->get('/v1/avatars/browsers/:code')
->desc('Get Browser Icon')
->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['browsers'])); }, 'Browser Code.')
2020-02-10 18:54:26 +13:00
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2020-01-27 19:14:14 +13:00
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getBrowser')
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-browser.md')
->action(function ($code, $width, $height, $quality) use ($avatarCallback) { return $avatarCallback('browsers', $code, $width, $height, $quality);
});
2019-05-09 18:54:39 +12:00
$utopia->get('/v1/avatars/flags/:code')
->desc('Get Country Flag')
->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['flags'])); }, 'Country Code. ISO Alpha-2 country code format.')
2020-02-10 18:54:26 +13:00
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2020-01-27 19:14:14 +13:00
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getFlag')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-flag.md')
->action(function ($code, $width, $height, $quality) use ($avatarCallback) { return $avatarCallback('flags', $code, $width, $height, $quality);
});
2019-05-09 18:54:39 +12:00
$utopia->get('/v1/avatars/image')
2019-08-24 02:16:25 +12:00
->desc('Get Image from URL')
->param('url', '', function () { return new URL(); }, 'Image URL which you want to crop.')
2020-02-10 18:54:26 +13:00
->param('width', 400, function () { return new Range(0, 2000); }, 'Resize preview image width, Pass an integer between 0 to 2000.', true)
->param('height', 400, function () { return new Range(0, 2000); }, 'Resize preview image height, Pass an integer between 0 to 2000.', true)
->label('scope', 'avatars.read')
2020-01-27 19:14:14 +13:00
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getImage')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-image.md')
->action(
2019-10-09 16:53:37 +13:00
function ($url, $width, $height) use ($response) {
$quality = 80;
$output = 'png';
$date = date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = md5('/v2/avatars/images-'.$url.'-'.$width.'/'.$height.'/'.$quality);
$type = 'png';
2020-02-20 01:41:23 +13:00
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 7 /* 1 week */);
if ($data) {
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data, 0)
;
}
if (!extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$fetch = @file_get_contents($url, false);
if (!$fetch) {
throw new Exception('Image not found', 404);
}
try {
$resize = new Resize($fetch);
} catch (\Exception $exception) {
throw new Exception('Unable to parse image', 500);
}
$resize->crop((int) $width, (int) $height);
$output = (empty($output)) ? $type : $output;
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('', null)
;
$data = $resize->output($output, $quality);
$cache->save($key, $data);
echo $data;
unset($resize);
exit(0);
}
);
2019-05-09 18:54:39 +12:00
$utopia->get('/v1/avatars/favicon')
->desc('Get Favicon')
->param('url', '', function () { return new URL(); }, 'Website URL which you want to fetch the favicon from.')
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2020-01-27 19:14:14 +13:00
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getFavicon')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-favicon.md')
2019-05-09 18:54:39 +12:00
->action(
2020-03-29 01:42:16 +13:00
function ($url) use ($response, $request) {
$width = 56;
$height = 56;
$quality = 80;
$output = 'png';
$date = date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
$key = md5('/v2/avatars/favicon-'.$url);
$type = 'png';
2020-02-20 01:41:23 +13:00
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */);
if ($data) {
2019-05-09 18:54:39 +12:00
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data, 0)
;
}
if (!extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_URL => $url,
2020-03-02 06:05:51 +13:00
CURLOPT_USERAGENT => sprintf(APP_USERAGENT,
2020-03-29 01:42:16 +13:00
Config::getParam('version'),
2020-03-02 06:05:51 +13:00
$request->getServer('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
2019-05-09 18:54:39 +12:00
]);
$html = curl_exec($curl);
2019-05-09 18:54:39 +12:00
curl_close($curl);
if (!$html) {
2019-05-09 18:54:39 +12:00
throw new Exception('Failed to fetch remote URL', 404);
}
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
@$doc->loadHTML($html);
$links = $doc->getElementsByTagName('link');
$outputHref = '';
$outputExt = '';
$space = 0;
2019-05-09 18:54:39 +12:00
foreach ($links as $link) { /* @var $link DOMElement */
$href = $link->getAttribute('href');
$rel = $link->getAttribute('rel');
$sizes = $link->getAttribute('sizes');
2020-04-09 01:38:36 +12:00
$absolute = URLParse::unparse(array_merge(parse_url($url), parse_url($href)));
2019-05-09 18:54:39 +12:00
switch (strtolower($rel)) {
case 'icon':
case 'shortcut icon':
//case 'apple-touch-icon':
$ext = pathinfo(parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
2019-05-09 18:54:39 +12:00
switch ($ext) {
case 'ico':
case 'png':
case 'jpg':
case 'jpeg':
$size = explode('x', strtolower($sizes));
$sizeWidth = (isset($size[0])) ? (int) $size[0] : 0;
$sizeHeight = (isset($size[1])) ? (int) $size[1] : 0;
2019-05-09 18:54:39 +12:00
if (($sizeWidth * $sizeHeight) >= $space) {
$space = $sizeWidth * $sizeHeight;
$outputHref = $absolute;
$outputExt = $ext;
2019-05-09 18:54:39 +12:00
}
break;
2019-05-09 18:54:39 +12:00
}
break;
2019-05-09 18:54:39 +12:00
}
}
if (empty($outputHref) || empty($outputExt)) {
$default = parse_url($url);
$outputHref = $default['scheme'].'://'.$default['host'].'/favicon.ico';
$outputExt = 'ico';
2019-05-09 18:54:39 +12:00
}
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
$data = @file_get_contents($outputHref, false);
if (empty($data) || (mb_substr($data, 0, 5) === '<html') || mb_substr($data, 0, 5) === '<!doc') {
throw new Exception('Favicon not found', 404);
}
2019-05-09 18:54:39 +12:00
$cache->save($key, $data);
$response
->setContentType('image/x-icon')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send($data, 0)
;
}
$fetch = @file_get_contents($outputHref, false);
if (!$fetch) {
2019-05-09 18:54:39 +12:00
throw new Exception('Icon not found', 404);
}
$resize = new Resize($fetch);
$resize->crop((int) $width, (int) $height);
2019-05-09 18:54:39 +12:00
$output = (empty($output)) ? $type : $output;
$response
->setContentType('image/png')
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'miss')
->send('', null)
;
$data = $resize->output($output, $quality);
$cache->save($key, $data);
echo $data;
unset($resize);
exit(0);
}
);
$utopia->get('/v1/avatars/qr')
2019-11-28 06:24:36 +13:00
->desc('Get QR Code')
2020-02-10 18:54:26 +13:00
->param('text', '', function () { return new Text(512); }, 'Plain text to be converted to QR code image.')
->param('size', 400, function () { return new Range(0, 1000); }, 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true)
2020-04-14 17:44:15 +12:00
->param('margin', 1, function () { return new Range(0, 10); }, 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
->param('download', 0, function () { return new Range(0, 1); }, 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true)
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2020-01-27 19:14:14 +13:00
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getQR')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-qr.md')
2019-05-09 18:54:39 +12:00
->action(
function ($text, $size, $margin, $download) use ($response) {
2019-05-09 18:54:39 +12:00
$renderer = new ImageRenderer(
new RendererStyle($size, $margin),
new ImagickImageBackEnd('png', 100)
);
$writer = new Writer($renderer);
if ($download) {
2019-05-09 18:54:39 +12:00
$response->addHeader('Content-Disposition', 'attachment; filename="qr.png"');
}
$response
->addHeader('Expires', date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
2019-05-09 18:54:39 +12:00
->setContentType('image/png')
->send('', $writer->writeString($text))
;
}
2020-04-09 01:38:36 +12:00
);